深入浅出图解【计算机网络】 之 【TCP可靠传输的实现: 三次握手+滑动窗口】

【前言】这个系列主要会介绍一些计算机网络体系中“看上去稍有些复杂”但“一旦理解了又会很容易”的内容,我会尝试通过示意图/动图的方式对概念进行尽量直观的诠释,如果能够对大家学习计算机网络有所启发的话就最好了。

TCP(Transmission Control Protocol)是整个TCP/IP协议族中为数不多的能扛起“可靠传输”大旗的“扛把子”了(上有应用层协议嗷嗷待哺,下有IP协议寄予厚望(注:IP协议是不提供可靠传输服务的))能够肩负如此重任说明它的协议组成一定不简单,不过,作为一个“协议”,它一定也不会很难理解的。

这部分的内容我打算分为两个部分讲解,本篇文章主要会讲解TCP连接的建立和释放(三次握手+四次握手),以及发送端的滑动窗口一般原理;至于同样很重要的“拥塞控制”等内容,我会放到下一篇文章中。

说起TCP,我们应该了解它最突出的特点是“基于连接的传输”和“以字节为单位(面向字节流)”,这两点特点与同是运输层的UDP协议形成了鲜明对比。(UDP不建立连接,以报文为单位,两者的区别有机会会仔细说说)而实现这两点也是TCP能够提供可靠传输的关键。

预备知识

TCP报文结构

TCP_packet

下面简单解释一下报文中一些重要的部分:

  • 源端口和目的端口:各占两个字节(16位),端口(Port)是运输层与应用层的服务接口,以实现复用和分用。
  • 序号(sequence number):占4个字节,指本报文段所发送的数据的第一个字节的序号。
  • 确认号(acknowledgement number):同样占4个字节,是期望收到对方下一个报文段的第一个字节的序号。(即收到对方的报文段最后一个字节的序号加一)
  • (部分)标志位(Flags)
    • 确认(ACK):ACK=1时确认号字段(acknowledgement number)才有效;
    • 同步(SYN):SYN=1表示这是一个“连接请求(SYN)”或“连接接受(SYN-ACK)”报文;
    • 终止(FIN):用于释放连接,FIN=1表明报文段发送完毕,要求释放连接。
  • 窗口:占2字节,是用来让对方设置发送窗口的依据。

面向流的TCP协议

再了解了TCP报文结构之后,我们需要知道,这里的“报文”与我们常见的“报文”有些不同。

TCP 不关心应用进程一次把多长的报文发送到 TCP 缓存。这和UDP的“给多少装多少”正相反。
TCP 对连续的字节流进行分段,形成 TCP 报文段。
面向字节流

那么分段的依据是什么呢?
TCP在建立连接的基础上,发送方可以根据对方给出的窗口值(接下来要提到的:使用滑动窗口实现流量控制)和当前网络拥塞(下一篇文章将要提到的:拥塞控制机制)的程度来决定一个报文段应包含多少个字节(UDP 发送的报文长度是应用进程给出的)。

TCP连接的建立(“三次握手”)与释放(“四次握手”)

TCP 建立连接的过程叫做“握手”(handshake),可以说是一个颇经典的不那么贴切但为大家所普遍接受的拟人化称呼。

在连接的建立阶段,需要在客户和服务器之间交换三个 TCP 报文段。称之为三报文握手(there-way handshake)
3_way_handshake

三报文握手的过程就如上动图所示,是不是其实很简单。
就像是两个隔着很远的人在测试一个电话线路:
“能听见我说话吗?”
“听见了!能听见我说话吗?”
“听见了!”

与建立连接思路相似的,还有“四报文握手”(four-way handshake)的连接释放
4_way_handshake

与建立连接不同的是,如果某一方想要释放连接,另一方“不一定”就想跟着释放连接,因为它可能还有东西没传完呢。
就像是俩人打电话唠了很久,其中有个人想要睡了:
“喂,我困了,咱明天再聊吧”
“哦哦,那我最后说几句”
… …
“好,我说完了,晚安咯”
“晚安”

使用滑动窗口实现可靠传输+流量控制

滑动窗口,实际上就是发送方能连续发出的数据(字节流)的阈值。

现在假设,A收到了B发来的确认号为32(表明B期望收到的下一个报文段的开头序号为32),窗口为20字节的确认报文段,那么A便可以根据这两个数据构建出如下发送窗口:
发送窗口1

A发送11个字节的数据后,发送窗口位置不变,可用窗口变小:
发送窗口2

此时,B收到了前三个字节,向A发送确认号为35的确认报文字段(同时接收窗口向前移动三个字节);
A的发送窗口在收到B的确认报文后也向前移动三个字节。
发送窗口3

这里,就需要引入“停止等待”和“累计确认”的概念了:
停止等待”,顾名思义,就是每发送完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组。
而在TCP传输过程中,为了提高效率、节约资源,采用了“累计确认”的方式:即不必对收到的分组逐个发送确认,而是对按序到达的最后一个分组发送确认,表示到这个分组为止的所有分组都已正确收到了

接着,A继续向B发送报文,但是B一直没有发回确认报文。
A在发送至可用窗口为零时便停止发送。
发送窗口4

【后记】
运输层真的是承上启下的扛把子,这其中细节还有很多,今天挑了这两个是因为对于这部分的内容,“直观上的理解”比“文本上的记忆”更为重要。
TCP的拥塞避免也是个很重要的内容,会出现在下一篇文章中。
最后,你们的反馈就是我最大的动力啦(逃