本文首发于个人博客
基本特点
面向连接的可靠字节流服务。
建立连接——三次握手
TCP建立连接时,需要经历下图所示的三次握手过程。
每一次握手都可以反映出不同的信息。
第一次握手
SYN标志提示TCP连接的服务端检查序列编号(seq),该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。客户端通过SYN=1来告诉服务端它想要建立连接。SYN=1的报文段不携带数据,但是也要消耗一个序号。
seq是基于时钟生成一个序号,每4微秒加1,到2^32-1时又从0开始。tcp提供全双工服务,客户端和服务端都有各自的序号。编号是为了解决网络包乱序的问题。
第一次握手成功后,服务器可以知道客户端的发送能力是正常的,服务器自己的接收能力是正常的。
第二次握手
服务端用SYN=1和ACK=1来表示这条数据报是针对之前的连接请求的应答。
ack=m+1表示服务端已经收到了到m为止的报文,期待的下一个报文是m+1。
第二次握手成功后,客户端知道了服务端的接收和发送能力正常,客户端自己的接收和发送能力是正常的。
第三次握手
客户端发送确认包(ACK=1)进行应答,ack=n+1表示n为止的序号都收到了,下一次希望收到序号是n+1的报文。
seq=m+1表示当前序号时m+1,需要注意的是,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。所以下一个报文的序号还是m+1。
服务端检查ack的值以及ACK=1,接收成功,第三次握手完成。
这时,因为收到了正确的回传,说明第二次握手的信息客户端正确接收了,所以服务端知道了客户端的接收能力是正常的,同时也说明了服务器自己的发送能力是正常的。
至此,客户端和服务端都确定了自己和对方的发送、接收都是正常了,tcp连接建立完成。
问题:建立连接为什么一定要3次握手?两次可以吗
不可以。
1、根据之前的分析已经知道只有3次握手之后,客户端和服务端才都确定了自己和对方的发送、接收都是正常的。
2、假如2次挥手就可以建立连接。客户端发出一次连接请求,但是由于网络原因,这个请求迟迟没到。客户端超时重传一个连接请求,假设这个请求被服务端接收到,服务端进入连接状态,并回复给客户端一个确认报文。客户端接收到之后也进入连接状态。双方开始通信,结束后正常断开连接。这时,之前客户端发出的那个由于网络原因没有到达的请求到达了服务端,服务器回复确认报文,并进入连接状态等待数据传输。但是因为客户端现在已经时关闭状态,无法接收这个确认报文,导致服务器长时间等待,造成资源浪费。
3、TCP保证可靠传输需要依赖报文中的序号值。这个序号并不是一定从0开始的,因此客户端和服务器都需要知道对方的初始序号才能确保数据的准确性。而2次握手的话,只有客户端的序号能被确认。
断开连接——四次挥手
TCP 是全双工 的, 每个方向必须单独地进行关闭。断开连接时需要进行4次握手。
第一次挥手
FIN=1表示要关闭连接,seq=x是当前报文序号。这次挥手表示客户端已经没有数据要发送了,想要断开连接。客户机进入终止等待1。
第二次挥手
服务端收到第一次挥手的报文后,通知上层应用程序对方已经请求断开连接,并且回复给客户端一个ACK确认报文。服务器进入关闭等待状态,此时,服务器还可以继续发送那些尚未发送完成的数据,客户端也可以接收。客户端收到确认报文后进入终止等待2。
第三次挥手
服务器发送完所有的数据后,发送一个FIN=1的报文表示自己可以断开连接了。因为第二次挥手后,服务器有可能又发送了一些数据,所以此时的seq是一个新的序列号z。服务器进入最终确认阶段。
第四次挥手
客户端收到服务器的FIN报文后,恢复一个ACK确认报文,服务器接到这个确认报文后直接关闭连接。而客户端在发出FIN报文后先是进入时间等待状态,经过**2MSL **(两倍的报文段最大存活时间,常用值有30秒、1分钟和2分钟) 的时间后,客户端也进入关闭状态。
至此,tcp连接关闭。
问题:为什么建立连接时3次握手就可以,而关闭时需要4次?
建立连接时,服务端收到第一次握手SYN请求后可以直接回复一个SYN+ACK报文,一方面确认了自己收到了客户端的连接请求,另一方面也表明自己也同意连接。
然而,关闭连接时,服务端收到客户端的FIN报文时,可能还有数据没有发送完,服务端不能立刻停止连接,它只能先发送一个确认报文给客户端。等到服务端的数据都发完了,才能向客户端发送FIN报文。所以需要4次握手。
问题:为什么要有2MSL的时间等待?
因为网络可能不稳定,导致第4次握手的ACK报文迟迟没有被服务器收到,服务器会一直超时重传FIN报文,如何客户端已经关闭,就无法处理了。所以客户端在送出第4次挥手的报文后,会等待一段时间,这段时间内没收到FIN报文,说明服务器已经收到了确认报文,客户端就可以关闭了。至于为什么时间设置成2MSL,可以这样理解:
1、如果客户机发送的第3次挥手报文不能顺利到达服务器,那么服务器那边等待的时间不可能比MSL更长,所以在一个MSL的时间内,服务器一定会重传一个FIN报文。
2、这个FIN报文最多存活MSL的时间,也就是说客户端一定会在一个MSL的时间内收到这个报文。
上面两个步骤加在一起,客户端最多等2MSL的时间,就可以确定能否关闭连接了。