TCP中的数据是怎么传输的?

1,075 阅读8分钟

交互式数据是怎么传输的?

交互式数据指泛指每次传递的字节很少,比如Telnet,Rlogin

以Rlogin为例,它每次传到服务器的是一个字节的按键,并且要求服务器回显客户端输入的字符。理论上完整的交互包括4个报文段:

  1. 客户端发送交互的数据
  2. 服务端对交互的数据进行ack
  3. 服务端对数据进行会显
  4. 客户端对数据回显进行确认

输入字符 date\n 产生的数据流如下:


可以发现真实的数据流存在如下特点:

  • 数据是一个一个字节的发送的
对应客户端发送的序号为 1、4、7、10、13,可以看到字节长度都是1,其中13的回显确认字节长度为2是因为换行符包括两个字符
  • 服务端存在数据捎带ACK现象。即数据的发送和ACK混合在了一起
以序号为2的数据流为例,服务端发送了数据,并进行了ack操作,也就是合并了数据回显和客户端数据发送的ack,数据交互理论上的4次在实际中只有3次报文交互
  • 客户端发送ACK的实际时间差基本上是200ms的整数倍。这是因为TCP内部使用了200ms的定时器
ack发送时间序号对应3、6、9、12等,值分别是 139.9ms、539.9ms、940.1ms、1339.9ms,其时间间隔为 400ms,400.2ms,399.8ms
侧面反应TCP发送数据最大的等待时延也就是200ms
  • 服务端不存在经受时延的ACK。经受时延的ack是指如果TCP等待数据发送的200ms之内还没有收到数据,就单独发送一个ack,不带数据。
数据到达和回显的时间间隔为序号1-2,4-5,7-8等,值为16.5ms 16.3ms 16.5ms,也就是在200ms定时器溢出之前,都有数据到达

单个字节发送的缺点是什么?

每次只发送一个字节的数据,那么在网络中很有可能充斥这许多41字节长的分组(20的IP包首部,20的TCP包首部,1字节的数据),过多的这种小分组则会增加拥塞的可能性

Nagle算法是如何解决单字节发送缺点的?

  1. TCP连接上最多只有一个未被确认的未完成的小分组
  2. 未完成确认的小分组确认之前,不能发送其它的小分组
  3. 在确认到达之前收集少量的分组,在确认到达之后以一个分组的方式发送出去

关闭Nagle算法的场景有哪些?

如果应用场景使得用户能够感觉到明显的延迟,那么就可以选择关闭Nagle选项。

通常情况使用Nagle算法是在较慢的广域网中,以便能够减少小报文的数目

成块的数据是如何传输的?

成块的数据比如电子邮件

tcp通过滑动窗口来控制成块数据的流量,使得发送方在不需要每发送一个分组就等待确认,从而加快了数据的传输

什么是滑动窗口?


上图是滑动窗口的一个快照,以时间为横轴,其中1-11表示发送方发送的字节的标号。

窗口是指数据处理方【发送方和接收方】维护的一个序列,在TCP协议中可以看做是报文片段序列,所谓滑动窗口则是描述随着时间的推移,原始的报文已经被处理,可从窗口中移除,并开始去处理新加入窗口的报文序列。

滑动窗口本身可以看做是一个协议,适合于数据传输过程中要求有严格顺序处理的场景
上图中,滑动窗口将时间轴上的数据分成了4个部分:
A:标识所在表示当前快照产生时,1-3个字节已经被接收方所处理,并且发送方确认了ack; 
B:标识所在表示快照下的滑动窗口内发送方已经发送了3个字节,但是接收方还没确认;
C:标识标识接收方当前的可用窗口大小,也是发送方能够发送的数据量;
D:标识标识当前快照下,接收方无法再接收数据了,空间不足,发送方无法发送;

窗口是如何运动的?

  • 当窗口左边沿向右边沿靠近时,称作窗口合拢。这种现象发生在数据被发送和确认时;
  • 当窗口右边沿向右移动时,称作窗口张开。这种现象发生在另一端接收进程读取已经确认的数据并释放TCP的接收缓存时;
  • 当右边沿向左移动时,称作窗口收缩。不建议使用这种方式,但是TCP需要实现这种场景
  • 窗口的左边沿一般不可能向左移动,如果接收到左边沿向左移动的ACK,则会被认为是重复的ACK,并被丢弃
左边沿到达右边沿称为零窗口,此时发送方不能发送任何数据

如何理解TCP报文中的win?

报文中的win用来描述窗口的大小。接收方窗口的大小可以通过接收方来实现控制,默认情况下4.3BSD中窗口大小为4096个字节,如果窗口中有还没来得及被应用程序读取的数据,那么返回报文中的win就会相应减小,当窗口中数据被处理之后,可能会出现携带ack但不确认任何数据,而win值变大的包,这种情况是用来增加窗口的有边沿,也称作窗口更新。

如果发送方和接收方之间存在多个路由器和较慢的链路时,TCP协议发送方是如何处理的?

TCP实现了一个慢启动算法,它为发送方提供一个拥塞窗口,开始时只会发送1个报文段,然后等待ACK


图中显示的是离散的时间单元,时间点1、2、3表示报文段从左向右移动一个时间单元,时间4接收方读取报文段并产生一个确认,时间点5、6、7表示ACK传输给发送方,整个过程经历了一个8个时间单元的RTT(Round-Trip Time)

收到ACK后,进而发送两个报文段


时间点12和时间点13产生的两个ack之间的间隔和报文段之间的间隔一样,被称为TCP的自计时(self-clocking)行为。实际运用过程中,返回路径上的排队会改变ACK的到达率

两个ACK的到达使得拥塞窗口从2个报文段增加为4个


4个ack到达增加为8个


在时间点31之后的时间里,发送方和接收方的管道都被填满,此时无论拥塞窗口和通告窗口是多少,它都不能再容纳更多的数据,此时放回路径上总是能保持相同数量的ACK,也就是连接的理想稳定状态

拥塞窗口是发送方使用的流量控制,通告窗口是接收方使用的流量控制;发送方的发送上限为拥塞窗口和通告窗口的最小值。

TCP报文中的PUSH标识是干什么用的?

客户端用来通知TCP在向服务器发送一个报文时,不要因等待额外数据而使已提交数据在缓存中滞留。服务器收到带PUSH标识的TCP报文时,则立即将这些数据递交给服务器进程。

TCP的紧急方式有什么用?

它使得一端可以告知另一端有些具有某种方式的“紧急数据”已经放置在普通数据流中,接收方收到通知后可以做相应处理。

以Telnet和Rlogin为例。当服务器进入了紧急方式,此时服务器是无法发送任何数据的,但服务器TCP会立即发送紧急指针和URG标志,当客户端TCP收到这个通知时,便会通知客户端进程,于是客户端可以从服务器读取其输入、打开窗口使数据流动

如何设置TCP的紧急方式?

设置TCP首部的URG标志为1,并且一个16bit的紧急指针被置为一个正的偏移量,次偏移量与TCP首部中的序号字段相加,便得到紧急数据的最后一个字节的序号。

只要接收方当前读取位置到紧急指针之间有数据存在,就认为应用程序处于“紧急方式”
如果接收方在处理第一个紧急方式之前,发送方多次进入紧急方式,接收方收到的旧紧急指针将会被新值覆盖

附录

把书读薄(TCP/IP详解 卷一 第十九章 第二十章)