上一篇文章已经介绍了TCP和UDP报文的首部,这一篇详细解析TCP中确保可靠传输的方法。
为了保证可靠传输,TCP比UDP多了很多控制协议和算法。
在TCP中当发送端的数据达到接受主机时,接受主机端都会返回一个消息,告诉对方我已经收到了。这个消息叫确认应答。发送确认应答时,TCP首部中的ACK标志位设1。
TCP中的确认应答通过序列号和确认应答号来实现。如下图所示,主机A发送第一个包时序列号为1,数据长度为1000,那么主机B收到包后发送一个数据包给主机A,在该包中将TCP首部中的32位确认号设为1001.相当于告诉对方1001之前的我都收到了,下次从1001开始发。
经受时延的确认应答:为了降低确认应答包的数量,TCP提出了经受时延的确认应答。接受端在收到数据后并不立即发送一个应答数据包,而是等待一段时间,如果有新的数据被接受就更新应答号,如果有其他数据要发送就坐上该数据包的顺风车。在系统的内核中维持了一个定时器,一般是200ms如果定时器溢出,即使没有其他数据到达,也发送该应答数据包。
Nagle算法: TCP是基于流的传输协议,在Rlogin和Telnet传输中会出现只有一个字节数据的TCP数据包。而一个TCP数据包的首部加上IP首部就有40个字节,很显然发这样的数据包划不来。为了减少这样的数据包,有人提出了Nagle算法。
Nagle算法简单讲就是,等待服务器应答包到达后,再发送下一个数据包。数据在发送端被缓存,如果缓存到达指定大小就将其发送,或者上一个数据的应答包到达,将缓存区一次性全部发送。
Nagle算法是从发送端角度考虑减少了数据包的个数,时延应答从接收端角度考虑减少了数据包的个数。
建立连接
发送数据
断开连接
其中:CLOSING状态是同时打开才发生的
为什么是3次
可能你会认为第3次好像是多余的。是因为信道是不可靠的,可能存在延时或者丢包,而三次是满足可靠传输的最小次数。
举例说明:如果只有两次,假设主机A发送的第一个请求包延时,主机A在等待一段时间后重新发送一个请求包,完成数据连接并断开。但是这个时候上次的发的请求包才到达主机B,这时主机B认为是又一次连接,因此发送一个请求包给A,但是A并没有发送新的请求因此会丢失该数据包。最后,B就一直等待A发送数据,浪费了资源。
除此之外,我个人认为3次握手更加安全,加大了攻击的难度。如果只有两次,一个发送一个应答,那么攻击着可以采用IP欺骗,发动SYN洪水攻击,并且服务端还都是ESTABLISHED状态。如何防御?难度更大了。对于三次握手的可以限制半连接的数量来达到一个防御的作用。
为什么是4次
TCP通信是一种全双工的通信,可以进行半关闭(与半打开区别:半打开是连接后的客户端和服务端有一端异常关闭了),所谓半关闭是指可以只关闭从A到B的方向,而B到A的方向还可以继续传输。因此,在客户端和服务器端分别进行关闭。
窗口是TCP中为了解决应答机制等待时间过长而引入的方法,如果没有窗口,则TCP每发送一次数据就必须等待应答,收到应答后继续发送,如果没有收到则等待一段时间后重发,如果很长时间都无法收到应答则判断为网络断开。而使用窗口后,窗口的大小指无需等待应答可以连续发送多个数据包。
下图中窗口的大小为4000,一次性发送了4个数据包,每个数据包大小为1000。
TCP窗口在每个传输方向都有两个窗口,发送端窗口和接受端窗口,又因为TCP是全双工通信,因此有四个窗口。
发送端窗口分为已经发送但是未接到回应的部分和可以被发送的部分
接收端端口大小 = 被分配缓存区 - 已接受确认等待被进程消耗的区域,发送端窗口的大小有ACK应答包中的窗口大小确认。
发送端窗口,根据应答包的确认号确定窗口的位置,根据应答包中窗口的大小确定窗口的大小,窗口是从左向右逐渐滑动。
窗口合拢:由左端边缘向右靠近,称为窗口合拢,在接受到数据后发生。
窗口张开:右右端向右移动,称为窗口打开,在处理完数据后发生。
如果接收端发送的应答包中窗口大小为0,则客户端会等待一段时间后发送探测包,重新确认窗口的大小。接受端如果处理完了数据也会重新发送应答包,通知发送端。反正死锁的发生。
引入窗口后,TCP的应答包如果部分丢失,无需重传,由后面的应答包保证。TCP为了提高效率,采用延时再确认应答,和选择性确认应答,即收到数据包后不立即发送应答包,而是等待收到下一个或多个包后发一个应答。
TCP是可靠的传输协议,意味着必须按序,无差错的传送数据和目的端。通过校验和,确认应答,重传来保证。校验和应答已经介绍过,这里主要讲解重传机制。重传分为两种:超时重传和快速重传。
超时重传(RTO)
当一个包被发送后,就开启一个定时器,如果定时时间到了,还未收到能确认该发送包的应答包,就重传一份数据。注意收到的应答包可能是该包也可能是后面包的,但是只要能确认该包被收到就行。另外如果,是因为网络延时造成重传,则接受端收到重复数据包后丢弃该包。
快速重传
当如果发送端收到一个包的三次应答包后,立即重传,比超时重传更高效。
流量是根据发送方和接受方的缓冲区大小来确定的,保证数据处理不出现拥堵,具体实现方法是窗口滑动。而拥塞机制是考虑到网络中的拥堵,比如路由器要处理的数据过多,导致缓冲区溢出而丢包。而拥塞处理就是来解决这种情况,避免网络出现过载的现象。
判定拥塞出现的条件:网络中出现分组丢失(发生超时或收到重复确认)
拥塞避免算法中用到了慢启动,快速重传,快速恢复。
拥塞避免算法需要维持两个变量:拥塞窗口和慢启动阀值。
慢启动算法(工作过程如下图所示):设置初始拥塞窗口大小为1,以后每收到一个应答拥塞窗口大小就加1(图中指定一个窗口大小是1000个字节),窗口大小呈指数级增长。客户端可发送数据的取拥塞窗口和应答包窗口两者中较小的那个。
拥塞避免算法:拥塞避免算法与慢启动的区别在于,收到一个应答后,拥塞窗口大小cwnd只增加1/cwnd。窗口大小整体呈现线性增长。
拥塞控制算法先采用慢启动算法,到达慢启动阀值后采用拥塞避免算法。
下图为拥塞控制过程中,拥塞窗口的大小变化图。有了拥塞窗口后,实际窗口的大小为拥塞窗口和ACK包中传递的窗口大小两个中的最小值。
TCP不对ACK应答报文进行确认,如果接受端缓冲被占满,发送一个窗口为0的应答,过了一段时间数据处理完毕,重新发送一个应答,告诉发送端窗口大小。不幸的是,如果这个包丢了,就会进入死锁状态——发送端等待更新窗口的应答包,接收端等待接收数据。
为了避免死锁了发生,TCP使用了一个坚持定时器来周期性地向接收方查询,以便发现窗口是否已经增大。这一过程也被称为窗口探查。
[1]http://www.jianshu.com/p/d9edbba4035b
[2]图解TCP/IP第五版
[3]计算机网络教程:自顶向下方法(第五版)
[4]TCP/IP详解卷一