TCP协议通信原理
概述
TCP协议是计算机网络中非常重要的一个协议,其工作在OSI模型的传输层。由于网络层的IP协议并不提供差错控制等服务,只保证尽力交付,所以需要使用更高一层的TCP协议来提供差错控制、流量控制、拥塞控制等服务,保证传输无误。
TCP协议有以下特点:
-
面向字节流。TCP协议的数据单位是字节。
-
面向连接,提供全双工通信。在通信前需要建立连接,且双方都可以进行数据传输和接收。
-
提供可靠交付的服务。TCP保证数据准确无误地、按顺序交付给上一层。
一、数据报格式
1 |
|
TCP协议使用一个四元组来区分一个连接,除了IP层使用的地址以外,还需要使用目的端口和起始端口来区分两台主机上的两个进程。
- Sequence Number用于当数据报到达目的地时按顺序组装TCP数据报。
- Acknowledgment Number用于告知对方自己已经收到的数据报。
- Data Offset其实就是头部长度,单位是32bit。
- 中间的URG、ACK等6bit是用于建立和释放连接的。
- window用于告知对方窗口大小,用于拥塞控制和流量控制。
- Checksum是校验和,校验TCP头部以及整个数据段的数据是否出现错误。
二、建立连接
当客户端想要与服务端进行数据通信时,需要经过“三次握手”,
- 会首先向服务器发送一个SYN字段被设置为1的数据报,并将这个数据报的Seq字段用一个随机数x填充,表示自己希望建立连接。
- 服务端收到时便会回复一个SYN字段和ACK字段都被设置为1的数据报,同时也将这个数据报的Seq用一个随机数y填充,Ack用x+1填充,表示自己希望收到的下一个数据报Seq=x+1。
- 客户端收到数据报后,回复一个ACK,Ack=y+1,Seq=x+1。
至此,连接建立完毕,两边可以开始数据传输。
服务端回复后会进入半连接状态,等待客户端回复。但若此时客户端迟迟不回复,就会引起服务器长时间等待,引发资源浪费。可以利用此原理产生大量IP地址对服务端进行SYN泛洪攻击。
三、数据传输
服务器与客户端经过三次握手后就可以进行数据传输。最简单的数据传输方式叫做停止等待协议,即发送一个数据报后,需要等待对方确认接收,才能继续发送下一个数据报,但是这种方式的信道利用率太低了。所以产生了另一种ARQ协议,即一次性发送多个数据报,对方可以一次性确认多个数据报,发送方根据确认接收的情况来选择重发哪些数据报。
ARQ协议
ARQ协议需要维护一个窗口,我们可以把需要发送的数据抽象成一个序列,窗口包含了连续的一段序列,窗口前的已经发送并被确认的数据报,窗口后是暂时不能被发送的数据报,而窗口中间是可以被发送的数据报。窗口又被分隔成两个部分,后一部分是可以被发送但未发送的内容,前一部分是已经被发送但未被确认的内容。
TCP协议的接受方和发送方就是用这种滑动窗口的方式来保证数据的每一个数据报都能够准确且按顺序的到达对方。值得一提的是,TCP协议允许接收方一次性确认多个数据报,当接收方发送ack=x+1的数据报时,表示其已经确认接受了seq<=x的所有数据报,这种方法保证了ack报文丢失时可以被其他ack报文所捎带确认。
重传条件
发送方遇到下面两种情况时会重传数据报:
- 超时:发送每一个数据报后,会启用一个定时器,定时器到期时若还未收到接收方的ack,则会自动重传
- 3-ack:当接收方未收到数据报时,会向发送方发送ack报文来“索取”该数据报,如果接收方累积收到3个ack数据报时,就会重传该数据报。
为什么要区分这两种情况?TCP的拥塞控制机制会根据这两种情况调整窗口大小。
四、释放连接
数据传输完毕后,客户端与服务端进行“四次挥手”就可以释放连接。其实说是“四次挥手”,不如说是“两个两次挥手”,因为前两次是确认客户端没有数据要发送,后两次是确认服务端没有数据要发送。
需要注意的是,FIN=1的两个报文并不需要占用seq序列号,也就是说,如果最后一个数据传输的报文seq=u,那么FIN=1这个报文的seq也为u。
为什么最后需要等待2MSL
MSL是报文的最大生成时间。客户端最后发送的ACK报文有可能因为各种原因没有被服务端接受,所以需要等待两个MSL,如果在这个过程中没有再次接受服务端发送的FIN=1,ACK=1数据报,则说明服务器正确地接受了我们数据报;如果服务端没有正确接受数据报,则会重发FIN-=1,ACK=1数据报,客户端需要重新处理。
五、流量控制和拥塞控制
流量控制
流量控制是为了防止接收方来不及接收数据从而出现丢包的现象,接收方会在数据报的头部window字段中写入接收方最大的滑动窗口大小,称为rwnd,从而方式发送方一次性发送过多的数据报。
拥塞控制
如果一连接入网络就以最大速度发送数据报,很容易引起网络拥堵,造成数据报大量丢失。所以TCP协议用了一系列方式来根据网络状况限制滑动窗口大小,这个最大值称为cwnd。关于cwnd的策略如下:
- 慢启动。最开始会将cwnd设置为1,并以指数形式增长,每次增大为原来的两倍。
- 加法增大。拥塞控制机制记录一个阈值ssthresh,当cwnd超过这个阈值时,就会由指数增大变为加法增大,每次增大1,从而避免拥塞。
- 发生丢包:发送丢包时,ssthresh需要减小为原来的一半,cwnd则根据重传两种情况进行调整:
- 超时:cwnd直接降为1(慢启动)
- 3-ack:cwnd也降为原来的一半(快重传)
最终发送方的窗口大小就是对rwnd和cwnd取最小值获得。