TCP是一个面向连接的协议。无论哪一方向另一方发生数据之前,都必须先在双方直接建立一条连接。而建立连接的过程需要经历三次握手,而连接的断开则需要进行四次挥手,下面将来简要说明下连接建立和断开的过程。
TCP首部的数据格式如下图。不算上可选项的话通常是20字节,最大60字节。
端口号:每个tcp首部都包含源端口号和目的端口号,用于寻找发送和接收的应用进程。这两个端口号加上IP首部中的源IP和目的IP地址组成唯一的一个TCP连接。
序号:序号用来标识从TCP源端向目的地发送的数据字节流,它表示这个报文段中的第一个数据字节。如果将字节流当作两个应用程序中的单向流动,则TCP用序列号来对每个字节进行计数。其为32位无符号数,当达到2^32 - 1后又从0开始。
确认序号:对于TCP协议来说,每传输一个TCP报文段都需要接收方确认。不过这种效率较低,所以在TCP中使用累计确认的机制,即对于多个TCP报文段只用对最后一个报文段确认即可。
例如:发送方发生一个报文段,其序号为11232且报文段大小为204,则接收方应当回复确认序号为11436。
说明:只有A C K标志为 1时确认序号字段才有效。
4位首部长度:代表TCP报文段首部的长度,由于是4位所以最大为15。其基本单位为4字节,所以最大代表15 X 4 = 60字节。
保留:未使用。
6个标志位:
URG:紧急指针(u rgent pointer)有效
ACK:确认序号有效
PSH:接收方应该尽快将这个报文段交给应用层
RST:重建连接
SYN:同步序号用来发起一个连接
FIN : 发端完成发送任务
窗口大小:TCP的流量控制由连接的每一端通过声明的窗口大小来提供。窗口大小为字节数,起始于确认序号字段指明的值,这个值是接收端期望接收到的字节。
检验和:检验和覆盖了整个TCP报文段,包括首部和数据部分。这是一个强制字段,由发送端计算和存储,并且由接收端验证。
紧急指针:在URG置为1时才有效。
建立连接的过程如下:
第一次挥手:客户端向服务器发出释放连接请求的报文,其中FIN(终止位) = 1,seq(序列号)=M;在客户端发送完之后,客户端进入FIN-WAIT-1(终止等待1)状态。此时客户端还是可以进行收数据的
第二次挥手:服务器在收到客户端的连接释放请求后,随即向客户端发送确认报文。其中ACK=1,seq=v,ack(确认号) = M +1;在服务器发送完毕后,服务器端进入CLOSE_WAIT(关闭等待)状态。此时A收到这个确认后就进入FIN-WAIT-2(终止等待2)状态,等待服务器发出连接释放的请求。此时服务器还是可以发数据的。
(如果服务器直接跑路,则客户端永远处与这个状态。TCP 协议里面并没有对这个状态的处理,但 Linux 有,可以调整 tcp_fin_timeout 这个参数,设置一个超时时间。)
第三次挥手:当服务器已经没有要发送的数据时,就会给客户端发送一个释放连接报文,其中FIN=1,ACK=1,seq=N,ack=M+1,在服务器发送完之后,进入LAST-ACK(最后确认)状态。
第四次挥手:当客户端收到服务器的释放连接请求时,必须对此发出确认,其中ACK=1,seq=M+1,ack=N+1;客户端在发送完毕后,进入到TIME-WAIT (时间等待)状态。服务器在收到客户端的确认之后,进入到CLOSED(关闭)状态。在经过时间等待计时器设置的时间之后,客户端才会进入CLOSED状态。
前面说了,TCP是全双工的通信模式,在任意时刻数据流都可以相互流动。所以客户端和服务器的挥手对都对应着某一个方向上数据流向的终止。即两次挥手只是客户端或者服务器方向上连接的断开,需要四次挥手才是客户端和服务端分别释放连接的过程。
msl(Maximum Segment Lifetime):报文段最大生存时间。它是任何报文段被丢弃前在网络内存活的最大时间。RFC 793 [Postel 1981c] 指出MSL为2分钟。然而,实现中的常用值是30秒,1分钟,或2分钟。
等待2msl主要有两个原因,一个是为了让服务器能够按照正常步骤进入CLOSED状态,二是为了防止已经失效的请求连接报文出现在下次连接中。
1. 由于客户端最后一次的确认报文可能会丢失,这会导致服务器无法进入CLOSE状态。于是服务器会重传释放连接的请求报文,如果此时客户端没有等待直接关闭了,那么客户端无法接收到服务器重传的请求,这样就会导致服务器无法正常释放。如果客户端还在等待,就会收到服务器的重传,然后进行应答,这样B就可以进入CLOSED状态了。
2. 在这2MSL等待时间里面,本次连接的所有的报文都已经从网络中消失,从而不会出现在下次连接中。