读《《图解TCP-IP》》有感
读《《图解TCP/IP》》有感
TCP/IP
近期几天读完《《图解TCP/IP》》,收获蛮多,记得上学时读stevens的《《TCP/IP具体解释》》时那是一个囫囵吞枣,没认真看也看不下去。等有时间再拜读下《《TCP/IP具体解释》》吧,预计能有不少共鸣。
如今认为。要想比較透彻理解TCP/IP,还得须要有server编程经验,学校应该同一时候开设《socket编程》》相关课程,最好同一个老师教,能够串讲,不然光理论不实战就是花架子,并且也不easy真正理解TCP/IP协议。
TCP/UDP一览
仅仅有较为透彻的理解了TCP/UDP才知道为啥TCP叫传输控制协议。UDP叫用户数据包协议。TCP在背后帮你做了非常多事,连接管理,保存时序,超时重发,拥塞控制。流控。通过mss自己主动帮你做分片,以降低传输途中路由器分片消耗(IPV6路由器是不会分片的),这叫传输控制协议,突出“控制”二字。而UDP什么都不帮你做,不保证时序。不做超时重传,是须要上层协议来保证,也就是用户来写规则,这也是第一个U(user)的含义。同一时候UDP没有mss,所以一次性发送数据最好不要太多。以免分片导致性能损耗。
那UDP为什么叫用户数据包协议呢。说到数据包就得说说TCP和UDP的面向连接和无连接了。
我们知道TCP是面向连接的,而UDP是无连接的。这就直接导致了TCP会通过三次握手连接server,假设serverport不正确TCP是知道,但UDP不一样。压根不知道server段的情况。即使server不在线,client照样发送数据。
In [22]: import socket
In [23]: s=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
In [24]: s.sendto("hello world",("127.0.0.1",9999))
Out[24]: 11
没有服务端。我们照样发送数据,sendto()照样返回。假设你之前不知道。这会让你大吃一惊,这也正说明了UDP不做数据包确认。
TCP传输的是流式数据,也就是说中间是没有分段的,这也就导致了我们常有的粘包问题,所以我们须要封包和解包,常见的都是字节长度+数据的方式,如websocket。也就是说我们接受一次,可能是对方多次发送后的数据集合,也可能是发送一次的部分数据,这都有可能,不能假设是哪种情况。这不经让我想起曾经上学时写的简单socketclient,server程序。都是一发一收。我们没封包解包不也正常吗?这就要说说Linux recv系统调用了。
int recv( SOCKET s, char FAR *buf, int len, int flags);
recv运行流程(来自网上)
1.recv等待s发送缓冲区数据发送完成,假设中途出现网络错误,recv函数返回SOCKET_ERROR2.recv检查s的接受缓冲区。假设接受缓冲区没有数据或者
正在接受数据。那么recv一直等待,直到把数据接受完成。当接受完成后,recv会把s接受缓冲区的数据copy到buf中(缓冲区数据可能大于buf长度。所以须要多次调用recv。recv仅仅是copy数据,真正接受数据是底层协议来完成的)
那这就好理解了,因为我们client一般是先send再recv,再发送完成之后recv会等待,并且我们数据量比較小,每次间隔比較大。所以有数据了,recv就返回了。当然这也是存在隐患的,发送数据大。一次recv可能接受不了。那就须要多次recv。这时就须要封包了,不然你怎么知道什么时候该返回。
就拿Http来说,服务端通过Content-Length和chunked编码来封包,让浏览器解析数据。
当然http1.0事实上Content-Length是非必须的,因为每次发送完数据后。服务端都会close,clientrecv会返回0,这时候也就知道数据结束了,也就是close作为了EOF标志。一帮下载都是通过这样的方式,所以也不会有粘包。
而UDP是不会出现粘包的,接收肯定能完整接收到发送的数据,世界瞬间清净了,这也是UDP**数据包**的概念,一个一个的数据包,接收的都是完整的数据包。
校验和
TCP/IP中校验和校验算法都几乎相同,也就是所谓的
反码求和
步骤例如以下
1.将校验和置为0
2.将数据转换为16字节整形,不足补零,求和(採用32位加法,将高16位和低16位相加,再将可能产生的进位与低16位相加)
3.和求反
最后发送者将校验和替换为求反的结果,将数据发给接受者,接受者忽略第一步採用相同算法。假设最后结果不为0,将丢弃该数据包。
手痒了一下。于是用golang模拟了下
//校验和算法
//对每16位(2字节)进行二进制反码求和,反码求和的意思是先对每16位求和。再将得到的和转为反码
func CheckSum(data []byte) uint16 {
//padding 16
all := len(data) / 2
if all*2 < len(data) {
all++
data = append(data, 0)
}
u16 := (*[0xffff]uint16)(unsafe.Pointer(&data[0]))[:all]
var sum uint32
for _, u := range u16 {
sum += uint32(u)
}
sum = (sum >> 16) + (sum & 0xffff) //把高位的进位,加到低八位
sum += sum >> 16 //前一步可能有进位
return (uint16)(^sum)
}
func main() {
check := make([]byte, 2)
data := []byte("tcp/ip checksum test1")
//首位作为校验和字段
all := []byte(string(check) + string(data))
sum := CheckSum(all)
fmt.Println(all, sum)
//校验和写入首位
copy(all, (*[2]byte)(unsafe.Pointer(&sum))[:])
fmt.Print(all, CheckSum(all))
}
结果:
[0 0 116 99 112 47 105 112 32 99 104 101 99 107 115 117 109 32 116 101 115 116 49] 22732
[204 88 116 99 112 47 105 112 32 99 104 101 99 107 115 117 109 32 116 101 115 116 49] 0
我们发现最后的确是0。说明我们算法没问题,至于为什么应该是0,小伙伴们想想就知道了。
TCP序列号与滑动窗体
以下说说TCP的seq,ack,在两方通信时seq開始是随机生成的,
假设A開始seq100,ack 1,发送数据size 100
通信 | seq | ack | size |
---|---|---|---|
A->B | 100 | 1 | 10 |
B->A | 200 | 111 | 0 |
A->B | 111 | 201 | 1000 |
B->A | 201 | 1112 | 0 |
1.A->B 初始随机seq 100,ack 1,发送数据10字节
2.B->A 初始睡觉seq200,告诉A下一个发送111。前面的数据都收到了,因为B仅仅是回应包 size 0
3.A->B A收到B的回应。看到ack为111。知道前面的10个自己都已经成功发送了。所以seq为111,发送1000个字节,并回应B下一个開始发送201
4.B->A ack为1000+111+1。告诉A下次从1112開始发送
仅仅要对方回应了ack。那么就代表ack之前的数据都已经成功接收了。这是对方就能够把自己缓冲区的相应数据给清空了,不然须要保存以待重传。因为每次都单项传输。吞吐量有点低,所以TCP引用了滑动窗体。也就是在没有明确收到ack之前能够连续发送w个包。w就是滑动窗体的大小。因为引用了滑动窗体。有些ack丢失也没有关系。仅仅要收到了兴许的ack确认就代表之前的数据都已经成功接收了。
CSMA/CD
通信专业的对CSMA/CD肯定都不陌生。我发现老师特别喜欢说这玩意,但我曾经始终不明确这玩意有啥用?事实上CSMA/CD是用于共享网络的,也就是通过hub或同轴电缆等连接的总线型或星型拓扑结构。因为这东西我们基本都没见过(过时了),因为须要冲突检查所以是半双工通信。当交换机出来时,CSMA/CD就被淘汰了。通过交换机的port转发实现了全双工通信。不知道是当初老师的问题还是我太弱,如今才明确应用场景。
如今的无线也是半双工的。是CSMA/CD的改良版,叫CSMA/CA。
读《《图解TCP-IP》》有感的更多相关文章
- 读<走出软件作坊>有感
1.成功的人都是在不可能完成任务的情况下完成的,成功的人也从来不会抱怨客观条件多么糟糕. 2.公司给你的资源,永远小于你做事需要的资源,这就是现实,就这么多人,就这样的素质,必须在现状中想出做事的办法 ...
- 为什么DIY报价----走出软件作坊:三五个人十来条枪 如何成为开发正规军(十二)[转]
前段时间,写了一个开发.实施.服务费用计算三部曲. 水清则无鱼--走出软件作坊:三五个人十来条枪 如何成为开发正规军(八) 实施费用也能DIY--走出软件作坊:三五个人十来条枪 如何成为开发正规军(九 ...
- 将服务费用DIY到底----走出软件作坊:三五个人十来条枪 如何成为开发正规军(十)[转]
前一段时间,讲了一系列开发经理.实施经理.服务经理的工具箱:开发经理的工具箱---走出软件作坊:三五个人十来条枪 如何成为开发正规军(三) ,实施经理的工具箱--走出软件作坊:三五个人十来条枪 如何成 ...
- 《走出软件作坊》//TODO
目录 简介 结束语 简介 作者吕建伟(@阿朱),研发管理专家,原京东技术学院院长,中国互联网技术联盟发起人,历任首席架构师.技术总监以及CTO等职位.目前已接受用友集团董事长王文京邀请,加入用友组建研 ...
- 实施费用也能DIY--走出软件作坊:三五个人十来条枪 如何成为开发正规军(九)[转]
上次咱们讲完了开发费用的计算,很多人在后面跟帖在那里算费用. 有人说:你把程序员都不当人,94天,一天都不休息啊. 我想答曰:94天,是工作时间.不算双休日在里面.也就是说,实际的开发周期长度是94+ ...
- Sql Server查询性能优化之走出索引的误区
据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是, ...
- 走出MFC子类化的迷宫
走出MFC子类化的迷宫 KEY WORDS:子类化 SUBCLASSWINDOW MFC消息机制 许多Windows程序员都是跳过SDK直接进行RAD开发工具[或VC,我想VC应不属于RAD]的学习 ...
- ofo走出校园观察:市场定位导致产品错位?
Ofo和摩拜单车虽然同样都是做单车共享,但实际上两者在最初的市场定位是有明显的差异的,因此提供的产品方案也存在巨大的差异. 市场定位不同,导致产品方案的巨大差异 摩拜单车一开始就定位于开放市场,充分的 ...
- 对于家政020 APP平台如何走出资本寒冬?
成都亿合科技小编了解到,随着O2O烧钱大战过去,网络上流传的一份O2O项目死亡名单上显示,近年来,汽车.社区.旅游.教育等16个领域的多个O2O项目关门大吉,仅外卖餐饮O2O项目倒闭的就有十几个.只有 ...
- 走出测试,走向CEO
飞测说:大家好,我们又见面了,我是黑夜小怪.不巧,今晚加班回来路上,湿身了,淋了个落汤鸡,不过明天也许可以看海了,也就呵呵了,原本想回来后聊些技术的,现在突然想先聊聊我的一些想法,仅供交流. 走出测试 ...
随机推荐
- JavaScript入门:006—JS函数的定义
JS函数的声明. 声明函数的格式例如以下: function 函数名(參数列表){ //函数语句: return 返回值; } 来看详细的函数声明.1.普通函数 <script type=&qu ...
- spring-hadoop-samples
官方的spring-hadoop-samples的demo 写的还是挺好的,值得学习. 官网地址: http://projects.spring.io/spring-hadoop/#quick-sta ...
- 【JUnit】Junit命令行执行、参数化执行、Main方法执行
参考资料: main方法执行:http://stackoverflow.com/questions/2543912/how-do-i-run-junit-tests-from-inside-my-ja ...
- azkaban2.5 具体配置以及使用方式
azkaban支持shell.java.mapreduce以及hive的工作流调度,在对这些不同类型任务调度之前须要配置所相应的插件:azkaban总体分为两部分azkaban executor se ...
- 点击Div,显示其innerHTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 我是怎样理解web页面的
事实上web页面包括三部分东东 1.页面展示的元素(HTML) 2.页面元素展示的样式(CSS) 3.控制页面元素的交互(JavaScript) 不管页面多么复杂,从这三方面去看,都会得到清晰的认识的 ...
- 【Lucene】Apache Lucene全文检索引擎架构之入门实战1
Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供.Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻.在Java开发环境里Lucene是一个成熟的 ...
- JS input 银行卡号格式转换
replace(/\D/g,'').replace(/....(?!$)/g,'$& ')
- source sh运行脚本的差别
主要有两种方式运行shell脚本 1)source test.bsh 2)sh test.bsh 1)souce运行脚本文件会在父程序中运行.各项动作都会在原本的bash内生效.运行过程不另开进程.脚 ...
- HTML5 2D平台游戏开发#5攻击
目前为止,角色除了基本的移动外还什么都不能做,于是我打算先实现角色的攻击动画.角色的普通攻击一共可以分为三个阶段: 一段斩 二段斩 三段斩 移动攻击 跳跃攻击 触发方式为角色站立时按下J(攻击)键,角 ...