前言
TCP是面向连接的协议,两个端口希望通过TCP通信时,需要在两个端口间先建立传输链路,这被称为三次握手过程,结束通信时,需要断开端口间的传输链路,这被称为四次挥手过程,本文我们来详细了解这两个过程。
三次握手
三次握手的过程可以结合下图来了解:
在第一次握手之前,client和server都处于close状态,client发起对server的连接时,将发送第一次握手请求。
第一次握手请求的内容包括syn标志位的设置和seq序列号的发送,我们在TCP(一)中介绍过,syn标志位的设置意味这这个包是用于序列号的包,因此前两次握手都需要带上这个标志位。此外,client还需要与server同步序列号,cllient将随机生成一个序列号,以这个序列号为初始值同步server序列号,使用随机生成的主要原因有:
- 如果不使用随机序列号,使用自增序列号从0开始同步,那么假设一个TCP连接遇到了网络拥堵,数据包过了很长时间才传输到对端,而此时对端已经断开了之前的TCP连接,建立了新的TCP连接,此时之前拥堵的包到达,正好与TCP此时希望接收的包序列号一致(因为假设是序列号自增的),那么TCP连接将误认为这个数据包来自新的连接。
- 如果每次都从某个固定值开始同步,那网络中的黑客很容易就能猜测到当前传输包的序列号,从而截取数据包。
当server接收到第一次握手请求,将从close状态转变为syn_recv状态,此时将把接收到的请求放入半连接队列中,发送相应的确认号和server的序列号给对端。
client接收到第二次握手请求,将进入established状态,此时客户端认为连接已建立,发送最后的确认号和序列号给server端。
server端接收到第三次握手请求,也进入established状态,此时请求从半连接队列被放入全连接对联。
在三次握手的过程中,也可能发生丢包的情况,此时将借助重传机制完成三次握手。
为什么是三次?
三次握手其实分别有自己的作用:
- 第一次握手,server确定client能发送请求,
- 第二次握手,client确定server能接收和发送请求,
- 第三次握手,server确定client能接收请求。
可以看到,缺少了任何一次,都无法保证建立的传输链路是全双工的,因此需要三次握手。
TFO
三次握手能为TCP建立一个可靠的传输路径,但在使用短连接的高并发的服务器中,每次用户向服务器发起请求,就需要经历一次三次握手的过程,而三次握手过程仅仅是建立传输路径,并不能进行数据传输,这造成了非常多的浪费,据谷歌统计,谷歌服务器约35%的数据包是用于三次握手的过程,为了解决这个问题,谷歌提出了TFO(tcp fast open)。
TFO的工作过程如下:
- client向server发送syn数据包,包尾还需要加上4bytes的FOC数据,
- server接收到FOC请求后生成cookie,发送syn、ack和cookie数据包给client,
- client缓存cookie,
- 第二次client需要建立连接时,在第一个包中发送syn和cookie,
- server验证cookie是否正确,
- 发送syn和ack时,就带上需要发送的数据。
TFO的过程中,client只有第一次连接server时需要传统的三次握手过程,后续只要将cookie带上,三次握手时就可以和数据一起传输,下图表示了TFO的过程:
四次挥手
四次挥手的过程如下:
- client向server发出fin包,client进入FIN_WAIT1状态,
- server接收到client发来的fin包,发送确认包,并进入CLOSE_WAIT状态,
- server继续发送之前未发完的数据包,
- server将所有数据包发送完毕之后,发送fin包给client,进入LAST_ACK状态,
- client接收到server发来的fin包,发送确认包给server,进入TIME_WAIT状态,等待2MSL时间后,进入close状态,
- server接收到client发来的确认包,进入close状态。
这个过程可以用下图来表示(server到达close状态还需要发送一个ack给client,这里忘记画了):
2MSL
图中的client在发送完最后一个包后,还等待了2MSL时间,这主要是为了防止client发送的最后一个包丢失。
MSL(Max Segment Life)指的是报文在网络中的最大生存时间,超过MSL时间没有被接收,TCP就认为该报文在网络中丢失,现在假如client发送的最后一个包丢失了,server最长需要等待MSL时间,如果MSL时间没有收到这个包,server将重传server发送的上一个包,而这个包最大传输时间也为MSL。也就是说,当client发出最后一个包后,如果2MSL时间内没有收到server的重传,说明server已经接收了最后一个包正常关闭,因此client需要等待2MSL时间。
2MSL的危害和解决方法
在短连接的服务器中,server发送完client请求的数据后就将主动关闭连接,此时server将需要等待2MSL时间,当服务器需要处理大量请求,那么大量端口将用来等来2MSL时间,用户将无法连接到服务器。
为了解决2MSL的问题,可以使用下面的策略:
- 设置socket选项为SO_REUSEADDR,这个选项允许端口在TIME_WAIT状态被复用,
- 调整tcp内核参数,比如增加可用端口上限,减少MSL时间,设置内核参数tw_tcp_reuse和tw_tcp_recycle等。
为什么是四次,不是三次或五次?
- 四次挥手能完成断开连接,五次显然是没有必要的,
- 如果server在close_wait状态不发送数据包,减少一次握手,那么client接收到的数据将不完整。
总结
本文介绍了经典的三次握手和四次挥手的过程,tcp的原理通过这两篇文章基本了解了,下篇文章将结合tcp通信实例来理解tcp,通过tcpdump抓包来观察网络中实际的通信过程。