3.5 面向连接的运输: TCP
3.5.1 TCP连接
- TCP是因特网运输层的面向连接的可靠的运输协议。
- TCP连接提供全双工服务(full-duplex service).
- TCP连接是点对点的连接.
1).最大报文段长度(Maximum Segment Size,MSS):该术语很容易被混淆,它其实指的是报文段里应用数据的最大长度,而不是包括TCP首部的TCP报文段的最大长度。
2).最大传输单元(Maximun Transmission Unit,MTU):指的是最大链路层帧长度,即应用数据+TCP首部+IP首部.
3.5.2 TCP报文段结构
1.序号和确认号。
在TCP协议中,序号是对应用层所要发送的数据的字节流的编号,而不是对报文段的编号。举例来说,进程A想要想进程B发送50000字节的数据,而其MSS为1000字节。那么,在TCP协议中,第一个报文段的序号是0,第二个报文段的序号是1000,第三个报文段的序号是2000......以此类推。序号表示的是:当前的报文段中应用层数据的首个字节在整个字节流中的位置。
TCP连接是全双工的,当主机A向主机B发送数据的同时,也有可能受到来自主机B的数据。举例来说,A收到了B发送的0~535的报文,同时它打算发送一个报文给B,那它的发送的这条报文的确认号将为536,表示它希望下一个接收到的报文的第一个序号是536 (当然,这条报文的初始序号是主机A自己随机选择的,比如0)
2.Telnet : 序号与确认号的一个学习案例。
假设客户端A的起始序号是42,服务器B的起始序号是79。在客户端中中输入一个字符‘c’,那么从A发出的报文应该是一个序号=42,确认号=79,data='c' 的TCP报文(前面说过,确认号指的是期待收到的下一个字节序号。在双方还没开始互发数据之前,客户端A等待的是79,而服务端B等待的是42)
3.5.3 往返时间的估计与超时
1.估计往返时间
报文段的“样本RTT”(SampleRTT)就是指某报文段被发出到该报文段的确认被收到的时间间隔。TCP维护一个SampleRTT的均值EstimatedRTT,每次收到一个新的SampleRTT,就对这个EstimatedRTT进行更新,“RTT偏差”(DevRTT)用于估算SampleRTT偏离EstimateRTT的程度
2.设置和管理重传超时间隔
3.5.4 可靠数据传输
由于TCP是在IP不可靠的、尽力而为的服务上建立的一种可靠数据传输协议,即确保接收端从其接受缓存中读取到的数据是无损坏、无间隔、非冗余和按序的。但TCP的报文是被IP数据报携带着在网络中传输的,有可能会出现比特错误、丢包。失序到达等问题,TCP如何保证数据的可靠性呢?
1. 在TCP的接收端,维护一个nextrcvseqnum变量,保存接收端下一个要接收的最小有序的报文段的序号。
①当接受到的报文的是正确的并且序号为nextrcvseqnum时,更新接收端的nextrcvseqnum,使nextrcvseqnum = nextrcvseqnum+sizeof(接收到的数据),回送确认号为nextrcvseqnum的ACK
②当接收到的报文是正确的,当序号>nextrcvseqnum时,先将这个报文缓存起来,然后继续回复确认号为nextrcvseqnum的ACK告诉发送方自己现在最想要的是序号为nextrcvseqnum的报文。
③当接收到的报文是正确的,但序号<nextrcvseqnum时,接收端丢弃这个报文,并回复确认号为nextrcvseqnum的ACK
④当接受到的报文是错误的,则不管序号是什么,接收端丢弃这个报文,并回复确认号为nextrcvseqnum的ACK
2. 在TCP的发送端,需要为最早发送、未被确认的报文段维护一个baseSend变量来保存其序号和一个定时器来记录其等待时间是否超时。
①当发送端接收到一个确认序号num>baseSend的ACK报文,那么,由于TCP采用的是累积确认机制,所以发送方知道序号<=num的报文都已经正确到达接收端了。所以发送端更新最早发送、未被确认的序号baseSend=num。如果当前还有未被确认的报文段,TCP还要重新启动定时器。
②当发送端接收到一个确认序号=baseSend的ACK报文(当确认序号=baseSend时,说明接收方是为baseSend的上一个报文回送的ACK)时,它的定时器会继续运行,并统计接收到的冗余ACK的数量。当接收到对相同数据的3个冗余ACK时,发送方不等定时器的超时时间,直接快速重传序号为baseSend的ACK报文(可以减少等待超时的时间,提升性能),并重新启动定时器。
③当发送端接收到一个序号<baseSend的ACK报文,直接忽视(这个报文可能在网络中延迟了,比较晚到达,而比它晚的累计确认报文已经到了,所以它没有意义了)
④当发送端的定时器过期时,还没有收到序号>baseSend的ACK,则发送端重传序号为baseSend的报文,并将定时器的值增加一倍。
TCP的可靠数据传输协议是在GBN的基础上,进行了改造。首先,GBN的接收端不会存储任何失序报文,所以GBN遇到超时事件会直接重传所有未被确认的报文,而TCP中只需重传最小未被确认的报文即可;其次,在TCP中加入了冗余ACK机制,可以实现快速重传,不必每次都等待漫长的超时事件的发生。二者大大提高了传输协议的整体性能。
3.5.5 流量控制
TCP为它的应用程序提供了流量控制服务(flow-control service)以消除发送方使接收方缓存溢出的可能性(TCP的发送端也有可能因为IP网络的拥塞而被遏制,这种形式的控制成为拥塞控制(congestion control))
3.5.6 TCP 连接管理
1. TCP连接的建立:客户端发起
TCP连接的建立也成为三次握手协议,通过以下三个步骤:
(1)首先,客户端想服务端发送一个特殊的“SYN报文”,这个报文不包含应用数据,但其首部的一个标志位SYN被置为1。另外,客户端会随机选择一个初始序号client_isn,并将此编号放入报文的序号字段中;
(2)一旦服务器接收到SYN报文,服务器为TCP连接分配TCP缓存和变量,并向客户TCP发送允许连接的SYNACK报文。在SYNACK报文中,SYN比特被置为1,首部的确认字段被置为client_isn+1,最后,服务器选择自己的随机起始序号server_isn,放入序号段中。
(3)在收到SYNACK后,客户端也要为自己的TCP连接分配TCP缓存和变量,并且向服务器发送一个报文,这个报文是对服务器的允许连接的一个确认。这个报文段的确认字段为server_isn+1;而由于此时连接已经建立,所以SYN比特被置为0。并且,在这个报文段里,可以携带应用层数据。
2. TCP连接的拆除,四次挥手
参与TCP连接的两个进程中的任意一个都能终止该连接。假设客户端打算终止连接:
(1)客户TCP向服务器进程发送一个特殊的报文,这个报文首部的FIN标志位被置为1;
(2)当服务器接收到这个报文时,他发送一个回送ACK报文
(3)服务器发送它的终止报文,其FIN标志位被置为1
(4)最后,该客户对服务器的终止报文进行确认。此时,这两台主机上用于该连接的所有资源都被释放了。
3.6 拥塞控制原理
3.6.1 拥塞原因与代价
- 网络传输中某些数据的丢失是因为网络的拥塞而导致的路由器的缓冲区的溢出而造成的。
- 当分组到达的速率接近链路容量的时候,会产生很大的队列等待延迟。
- 发送方为了对路由器缓冲区溢出所产生的分组丢失做出补偿,就必须执行数据的重发操作。
- 当发生超长时间延迟时,发送方所进行的重传操作可能会导致路由器浪费其带宽去传送一些不必要的分组副本。
- 当一个分组在传送路径中丢失,那么前面所有发送过该分组的、一直到丢失该分组的路由器所做的传输工作就都浪费了。
3.6.2 拥塞控制方法
- 端到端的拥塞控制
- 网络层发生作用的拥塞控制
3.7 TCP 拥塞控制
TCP 的拥塞控制主要来避免两种现象,包丢失和超时重传。一旦出现了这些现象就说明,发送速度太快了,要慢一点。TCP的拥塞控制主要原理依赖于一个拥塞窗口(cwnd)来控制
1.慢启动
最初的TCP在连接建立成功后会向网络中发送大量的数据包,这样很容易导致网络中路由器缓存空间耗尽,从而发生拥塞。因此新建立的连接不能够一开始就大量发送数据包,而只能根据网络情况逐步增加每次发送的数据量,以避免上述现象的发生。具体来说,当新建连接时,cwnd初始化为1个最大报文段(MSS)大小,发送端开始按照拥塞窗口大小发送数据,每当有一个报文段被确认,cwnd就增加1个MSS大小。这样cwnd的值就随着网络往返时间(Round Trip Time,RTT)呈指数级增长,事实上,慢启动的速度一点也不慢,只是它的起点比较低一点而已。
一条 TCP 连接开始,cwnd 设置为一个报文段,一次只能发送一个;当收到这一个确认的时候,cwnd 加一,于是一次能够发送两个;当这两个的确认到来的时候,每个确认 cwnd 加一,两个确认 cwnd 加二,于是一次能够发送四个;当这四个的确认到来的时候,每个确认 cwnd 加一,四个确认 cwnd 加四,于是一次能够发送八个。可以看出这是指数性的增长。
2.拥塞避免
从慢启动可以看到,cwnd可以很快的增长上来,从而最大程度利用网络带宽资源,但是cwnd不能一直这样无限增长下去,一定需要某个限制。TCP使用了一个叫慢启动门限(ssthresh)的变量,当cwnd超过该值后,慢启动过程结束,进入拥塞避免阶段。对于大多数TCP实现来说,ssthresh的值是65536(同样以字节计算)。拥塞避免的主要思想是加法增大,也就是cwnd的值不再指数级往上升,开始加法增加。此时当窗口中所有的报文段都被确认时,cwnd的大小加1,cwnd的值就随着RTT开始线性增加,这样就可以避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。
涨到什么时候是个头呢?有一个值 ssthresh 为 65535 个字节,当超过这个值的时候,就要小心一点了,不能倒这么快了,可能快满了,再慢下来。 每收到一个确认后,cwnd 增加 1/cwnd,我们接着上面的过程来,一次发送八个,当八个确认到来的时候,每个确认增加 1/8,八个确认一共 cwnd 增加 1,于是一次能够发送九个,变成了线性增长。
3.快速重传与快速恢复
拥塞的一种表现形式是丢包,需要超时重传,这个时候,将 sshresh 设为 cwnd/2,将 cwnd 设为 1,重新开始慢启动。这真是一旦超时重传,马上回到解放前。但是这种方式太激进了,将一个高速的传输速度一下子停了下来,会造成网络卡顿。快速重传算法:当接收端发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速的重传,不必等待超时再重传。TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,cwnd 减半为 cwnd/2,然后 sshthresh = cwnd,当三个包返回的时候,cwnd = sshthresh + 3,也就是没有一夜回到解放前,而是还在比较高的值,呈线性增长。
参考
https://blog.csdn.net/qq_36464448/article/details/80409476
https://blog.csdn.net/peijian1998/article/details/25684937
https://blog.csdn.net/itmacar/article/details/12278769