读《《图解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_ERROR

2.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. 读<走出软件作坊>有感

    1.成功的人都是在不可能完成任务的情况下完成的,成功的人也从来不会抱怨客观条件多么糟糕. 2.公司给你的资源,永远小于你做事需要的资源,这就是现实,就这么多人,就这样的素质,必须在现状中想出做事的办法 ...

  2. 为什么DIY报价----走出软件作坊:三五个人十来条枪 如何成为开发正规军(十二)[转]

    前段时间,写了一个开发.实施.服务费用计算三部曲. 水清则无鱼--走出软件作坊:三五个人十来条枪 如何成为开发正规军(八) 实施费用也能DIY--走出软件作坊:三五个人十来条枪 如何成为开发正规军(九 ...

  3. 将服务费用DIY到底----走出软件作坊:三五个人十来条枪 如何成为开发正规军(十)[转]

    前一段时间,讲了一系列开发经理.实施经理.服务经理的工具箱:开发经理的工具箱---走出软件作坊:三五个人十来条枪 如何成为开发正规军(三) ,实施经理的工具箱--走出软件作坊:三五个人十来条枪 如何成 ...

  4. 《走出软件作坊》//TODO

    目录 简介 结束语 简介 作者吕建伟(@阿朱),研发管理专家,原京东技术学院院长,中国互联网技术联盟发起人,历任首席架构师.技术总监以及CTO等职位.目前已接受用友集团董事长王文京邀请,加入用友组建研 ...

  5. 实施费用也能DIY--走出软件作坊:三五个人十来条枪 如何成为开发正规军(九)[转]

    上次咱们讲完了开发费用的计算,很多人在后面跟帖在那里算费用. 有人说:你把程序员都不当人,94天,一天都不休息啊. 我想答曰:94天,是工作时间.不算双休日在里面.也就是说,实际的开发周期长度是94+ ...

  6. Sql Server查询性能优化之走出索引的误区

    据了解绝大多数开发人员对于索引的理解都是一知半解,局限于大多数日常工作没有机会.也什么没有必要去关心.了解索引,实在哪天某个查询太慢了找到查询条件建个索引就ok,哪天又有个查询慢了,再建立个索引就是, ...

  7. 走出MFC子类化的迷宫

    走出MFC子类化的迷宫 KEY WORDS:子类化 SUBCLASSWINDOW  MFC消息机制 许多Windows程序员都是跳过SDK直接进行RAD开发工具[或VC,我想VC应不属于RAD]的学习 ...

  8. ofo走出校园观察:市场定位导致产品错位?

    Ofo和摩拜单车虽然同样都是做单车共享,但实际上两者在最初的市场定位是有明显的差异的,因此提供的产品方案也存在巨大的差异. 市场定位不同,导致产品方案的巨大差异 摩拜单车一开始就定位于开放市场,充分的 ...

  9. 对于家政020 APP平台如何走出资本寒冬?

    成都亿合科技小编了解到,随着O2O烧钱大战过去,网络上流传的一份O2O项目死亡名单上显示,近年来,汽车.社区.旅游.教育等16个领域的多个O2O项目关门大吉,仅外卖餐饮O2O项目倒闭的就有十几个.只有 ...

  10. 走出测试,走向CEO

    飞测说:大家好,我们又见面了,我是黑夜小怪.不巧,今晚加班回来路上,湿身了,淋了个落汤鸡,不过明天也许可以看海了,也就呵呵了,原本想回来后聊些技术的,现在突然想先聊聊我的一些想法,仅供交流. 走出测试 ...

随机推荐

  1. 1084. Broken Keyboard (20)【字符串操作】——PAT (Advanced Level) Practise

    题目信息 1084. Broken Keyboard (20) 时间限制200 ms 内存限制65536 kB 代码长度限制16000 B On a broken keyboard, some of ...

  2. ES6里关于字符串的拓展

    一.子串识别 自从 JS 引入了 indexOf() 方法,开发者们就使用它来识别字符串是否存在于其它字符串中.ES6 包含了以下三个方法来满足这类需求: 1.includes():该方法在给定文本存 ...

  3. EL表达式中的empty运算符

  4. redhat mount iso as one yum repository

    prepare redhat DVD iso rhel-server-6.4-x86_64-dvd.iso mount cd / mkdir /mnt/rhel mount -o loop rhel- ...

  5. Java基础学习过程

    转载:http://blog.csdn.net/scythe666/article/details/51699954JVM 1. 内存模型( 内存分为几部分? 堆溢出.栈溢出原因及实例?线上如何排查? ...

  6. 【转载】Loadrunner实现Android / IOS 手机APP压力测试

    随着手机APP用户量的增大,大的手机APP一般都需要进行压力测试,这几天用了loadrunner 12进行了手机APP的压力测试,整理了下,大家可以参考参考怎样给Andorid / IOS手机APP进 ...

  7. S3:代理模式 Proxy

    为其他对象提供一种代理以控制对这个对象的访问. 使用场合: 1.远程代理:为一个对象在不同的地址空间提供局部代表,隐藏对象存在于不同地址空间的事实.2.虚拟代理:根据需要创建开销很大的对象,通过它来存 ...

  8. python例子三

    例一:匹配长度为1-15的域名 #-*-encoding:utf--*- import re regex=re.compile('^www[.][a-z]{1,15}[.](com|org)') m1 ...

  9. c语言字符串赋值

    char *p="asdf";能运行 定义一个字符指针,并用它指向常量字符串"asdf"的首地址 char *p;p="asdf"; 能运行 ...

  10. 【BZOJ4008】【HNOI2015】亚瑟王 概率DP

    链接: #include <stdio.h> int main() { puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢"); puts("网 ...