图解 TCP协议核心| 你管这破玩意儿叫TCP?

你是一台电脑,你的名字叫 A

图解TCP协议 | 你管这破玩意儿叫TCP?

经过《如果让你来设计网络》这篇文章中的一番折腾,只要你知道另一位伙伴 B 的 IP 地址,且你们之间的网络是通的,无论多远,你都可以将一个数据包发送给你的伙伴 B

图解TCP协议 | 你管这破玩意儿叫TCP?

这就是物理层、数据链路层、网络层这三层所做的事情。

站在第四层的你,就可以不要脸地利用下三层所做的铺垫,随心所欲地发送数据,而不必担心找不到对方了。

图解TCP协议 | 你管这破玩意儿叫TCP?

虽然你此时还什么都没干,但你还是给自己这一层起了个响亮的名字,叫做传输层。

你本以为自己所在的第四层万事大吉,啥事没有,但很快问题就接踵而至。

问题来了

前三层协议只能把数据包从一个主机搬到另外一台主机,但是,到了目的地以后,数据包具体交给哪个程序(进程)呢?

图解TCP协议 | 你管这破玩意儿叫TCP?

所以,你需要把通信的进程区分开来,于是就给每个进程分配一个数字编号,你给它起了一个响亮的名字:端口号。

图解TCP协议 | 你管这破玩意儿叫TCP?

然后你在要发送的数据包上,增加了传输层的头部,源端口号与目标端口号。

图解TCP协议 | 你管这破玩意儿叫TCP?

OK,这样你将原本主机到主机的通信,升级为了进程和进程之间的通信。

你没有意识到,你不知不觉实现了 UDP 协议!

(当然 UDP 协议中不光有源端口和目标端口,还有数据包长度和校验值,我们暂且略过)

就这样,你用 UDP 协议无忧无虑地同 B 进行着通信,一直没发生什么问题。

图解TCP协议 | 你管这破玩意儿叫TCP?

但很快,你发现事情变得非常复杂…

丢包问题

由于网络的不可靠,数据包可能在半路丢失,而 A 和 B 却无法察觉。

图解TCP协议 | 你管这破玩意儿叫TCP?

对于丢包问题,只要解决两个事就好了。

第一个,A 怎么知道包丢了?

答案:让 B 告诉 A

第二个,丢了的包怎么办?

答案:重传

于是你设计了如下方案,A 每发一个包,都必须收到来自 B 的确认(ACK),再发下一个,否则在一定时间内没有收到确认,就重传这个包。

图解TCP协议 | 你管这破玩意儿叫TCP?

你管它叫停止等待协议。只要按照这个协议来,虽然 A 无法保证 B 一定能收到包,但 A 能够确认 B 是否收到了包,收不到就重试,尽最大努力让这个通信过程变得可靠,于是你们现在的通信过程又有了一个新的特征,可靠交付。

效率问题

停止等待虽然能解决问题,但是效率太低了,A 原本可以在发完第一个数据包之后立刻开始发第二个数据包,但由于停止等待协议,A 必须等数据包到达了 B ,且 B 的 ACK 包又回到了 A,才可以继续发第二个数据包,这效率慢得可不是一点两点。

于是你对这个过程进行了改进,采用流水线的方式,不再傻傻地等。

图解TCP协议 | 你管这破玩意儿叫TCP?

顺序问题

但是网路是复杂的、不可靠的。

有的时候 A 发出去的数据包,分别走了不同的路由到达 B,可能无法保证和发送数据包时一样的顺序。

图解TCP协议 | 你管这破玩意儿叫TCP?

在流水线中有多个数据包和ACK包在乱序流动,他们之间对应关系就乱掉了。

难道还回到停止等待协议?A 每收到一个包的确认(ACK)再发下一个包,那就根本不存在顺序问题。应该有更好的办法!

A 在发送的数据包中增加一个序号(seq),同时 B 要在 ACK 包上增加一个确认号(ack),这样不但解决了停止等待协议的效率问题,也通过这样标序号的方式解决了顺序问题。

图解TCP协议 | 你管这破玩意儿叫TCP?

而 B 这个确认号意味深长:比如 B 发了一个确认号为 ack = 3,它不仅仅表示 A 发送的序号为 2 的包收到了,还表示 2 之前的数据包都收到了。这种方式叫累计确认或累计应答。

图解TCP协议 | 你管这破玩意儿叫TCP?

注意,实际上 ack 的号是收到的最后一个数据包的序号 seq + 1,也就是告诉对方下一个应该发的序号是多少。但图中为了便于理解,ack 就表示收到的那个序号,不必纠结。

流量问题

有的时候,A 发送数据包的速度太快,而 B 的接收能力不够,但 B 却没有告知 A 这个情况。

图解TCP协议 | 你管这破玩意儿叫TCP?

怎么解决呢?

很简单,B 告诉 A 自己的接收能力,A 根据 B 的接收能力,相应控制自己的发送速率,就好了。

B 怎么告诉 A 呢?B 跟 A 说"我很强"这三个字么?那肯定不行,得有一个严谨的规范。

于是 B 决定,每次发送数据包给 A 时,顺带传过来一个值,叫窗口大小(win),这个值就表示 B 的接收能力。同理,每次 A 给 B 发包时也带上自己的窗口大小,表示 A 的接收能力。

图解TCP协议 | 你管这破玩意儿叫TCP?

B 告诉了 A 自己的窗口大小值,A 怎么利用它去做 A 这边发包的流量控制呢?

很简单,假如 B 给 A 传过来的窗口大小 win = 5,那 A 根据这个值,把自己要发送的数据分成这么几类。

图解TCP协议 | 你管这破玩意儿叫TCP?

图片过于清晰,就不再文字解释了。

当 A 不断发送数据包时,已发送的最后一个序号就往右移动,直到碰到了窗口的上边界,此时 A 就无法继续发包,达到了流量控制。

图解TCP协议 | 你管这破玩意儿叫TCP?

但是当 A 不断发包的同时,A 也会收到来自 B 的确认为,此时整个窗口会往右移动,因此上边界也往右移动,A 就能发更多的数据包了。

图解TCP协议 | 你管这破玩意儿叫TCP?

以上都是在窗口大小不变的情况下,而 B 在发给 A 的 ACK 包中,每一个都可以重新设置一个新的窗口大小,如果 A 收到了一个新的窗口大小值,A 会随之调整。

如果 A 收到了比原窗口值更大的窗口大小,比如 win = 6,则 A 会直接将窗口上边界向右移动 1 个单位。

图解TCP协议 | 你管这破玩意儿叫TCP?

如果 A 收到了比原窗口值小的窗口大小,比如 win = 4,则 A 暂时不会改变窗口大小,更不会将窗口上边界向左移动,而是等着 ACK 的到来,不断将左边界向右移动,直到窗口大小值收缩到新大小为止。

图解TCP协议 | 你管这破玩意儿叫TCP?

OK,终于将流量控制问题解决得差不多了,你看着上面一个个小动图,给这个窗口起了一个更生动的名字,滑动窗口。

拥塞问题

但有的时候,不是 B 的接受能力不够,而是网络不太好,造成了网络拥塞。


image

拥塞控制与流量控制有些像,但流量控制是受 B 的接收能力影响,而拥塞控制是受网络环境的影响。

拥塞控制的解决办法依然是通过设置一定的窗口大小,只不过,流量控制的窗口大小是 B 直接告诉 A 的,而拥塞控制的窗口大小按理说就应该是网络环境主动告诉 A。

但网络环境怎么可能主动告诉 A 呢?只能 A 单方面通过试探,不断感知网络环境的好坏,进而确定自己的拥塞窗口的大小。

image

拥塞窗口大小的计算有很多复杂的算法,就不在本文中展开了,假如拥塞窗口的大小为 cwnd,上一部分流量控制的滑动窗口的大小为 rwnd,那么窗口的右边界受这两个值共同的影响,需要取它俩的最小值。

窗口大小 = min(cwnd, rwnd)

含义很容易理解,当 B 的接受能力比较差时,即使网络非常通畅,A 也需要根据 B 的接收能力限制自己的发送窗口。当网络环境比较差时,即使 B 有很强的接收能力,A 也要根据网络的拥塞情况来限制自己的发送窗口。正所谓受其短板的影响嘛~

连接问题

有的时候,B 主机的相应进程还没有准备好或是挂掉了,A 就开始发送数据包,导致了浪费。

图解TCP协议 | 你管这破玩意儿叫TCP?

这个问题在于,A 在跟 B 通信之前,没有事先确认 B 是否已经准备好,就开始发了一连串的信息。就好比你和另一个人打电话,你还没有"喂"一下确认对方有没有在听,你就巴拉巴拉说了一堆。

这个问题该怎么解决呢?

地球人都知道,三次握手嘛!

A:我准备好了(SYN)

B:我知道了(ACK),我也准备好了(SYN)

A:我知道了(ACK)

图解TCP协议 | 你管这破玩意儿叫TCP?

A 与 B 各自在内存中维护着自己的状态变量,三次握手之后,双方的状态都变成了连接已建立(ESTABLISHED)。

虽然就只是发了三次数据包,并且在各自的内存中维护了状态变量,但这么说总觉得太 low,你看这个过程相当于双方建立连接的过程,于是你灵机一动,就叫它面向连接吧。

注意:这个连接是虚拟的,是由 A 和 B 这两个终端共同维护的,在网络中的设备根本就不知道连接这回事儿!

但凡事有始就有终,有了建立连接的过程,就要考虑释放连接的过程,又是地球人都知道,四次挥手嘛!

A:再见,我要关闭了(FIN)

B:我知道了(ACK)

给 B 一段时间把自己的事情处理完…

B:再见,我要关闭了(FIN)

A:我知道了(ACK)

图解TCP协议 | 你管这破玩意儿叫TCP?

总结

以上讲述的,就是 TCP 协议的核心思想,上面过程中需要传输的信息,就体现在 TCP 协议的头部,这里放上最常见的 TCP 协议头解读的图。

图解TCP协议 | 你管这破玩意儿叫TCP?

不知道你现在再看下面这句话,是否能理解:

TCP 是面向连接的、可靠的、基于字节流的传输层通信协议

面向连接、可靠,这两个词通过上面的讲述很容易理解,那什么叫做基于字节流呢?

很简单,TCP 在建立连接时,需要告诉对方 MSS(最大报文段大小)。

也就是说,如果要发送的数据很大,在 TCP 层是需要按照 MSS 来切割成一个个的 TCP 报文段 的。

切割的时候我才不管你原来的数据表示什么意思,需要在哪里断句啥的,我就把它当成一串毫无意义的字节,在我想要切割的地方咔嚓就来一刀,标上序号,只要接收方再根据这个序号拼成最终想要的完整数据就行了。

在我 TCP 传输这里,我就把它当做一个个的字节,也就是基于字节流的含义了。

图解TCP协议 | 你管这破玩意儿叫TCP?

最后留给大家一个作业,模拟 A 与 B 建立一个 TCP 连接。

第一题:A 给 B 发送 “aaa” ,然后 B 给 A 回复一个简单的字符串 “success”,并将此过程抓包。

第二题:A 给 B 发送 “aaaaaa … a” 超过最大报文段大小,然后 B 给 A 回复一个简单的字符串 “success”,并将此过程抓包。

下面是我抓的包(第二题)

三次握手阶段

A -> B [SYN] Seq=0 Win=64240 Len=0

MSS=1460 WS=256

B - >A [SYN, ACK] Seq=0 Ack=1 Win=29200 Len=0

MSS=1424 WS=512

A -> B [ACK] Seq=1 Ack=1 Win=132352 Len=0

数据发送阶段

A -> B [ACK] Seq=1 Ack=1 Win=132352 Len=1424

A -> B [ACK] Seq=1425 Ack=1 Win=132352 Len=1424

A -> B [PSH, ACK] Seq=2849 Ack=1 Win=132352 Len=1247

B -> A [ACK] Seq=1 Ack=1425 Win=32256 Len=0

B -> A [ACK] Seq=1 Ack=2849 Win=35328 Len=0

B -> A [ACK] Seq=1 Ack=4096 Win=37888 Len=0

B -> A [PSH, ACK] Seq=1 Ack=4096 Win=37888 Len=7

四次挥手阶段

B -> A [FIN, ACK] Seq=8 Ack=4096 Win=37888 Len=0

A -> B [ACK] Seq=4096 Ack=9 Win=132352 Len=0

A -> B [FIN, ACK] Seq=4096 Ack=9 Win=132352 Len=0

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容