tcp 连接与断开
一 tcp关键结构信息
- 源端口号和目的端口:各占2字节,与ip首部ip地址组成一个唯一的tcp连接。
- 序号和确认序号:4字节,无符号,是本报文段的第一个字节的序号,例如,一报文段的序号为300,而且数据共100字节,则下一个报文段的序号就是400;序号是32bit的无符号数,序号到达2^32-1后从0开始。确认序号是期望收到对方下次发送数据的第一个字节的序号,确认序号应该是上次已成功收到数据字节序号+1。只有ACK标志为1时,确认序号才有效。
标志位:6个,各占1位。
- SYN:请求建立连接;
- ACK:当ACK=1,确认序号有效;
- FIN:发送端发送完成,请求断开连接;
- URG:注解此报文应尽快传送,而不要按本来的列队次序来传送。与“紧急指针”字段共同应用,紧急指针指出在本报文段中的紧急数据的最后一个字节的序号,使接管方可以知道紧急数据共有多长;
- PSH:接收方应该尽快将本报文段立即传送给其应用层。
- RST:重置连接。
- 窗口:16位,65535,tcp通过滑动窗口进行流量控制,设想发送方发送速度很快,而接收方接受速度很慢,为了保证数据不丢,而进行流量控制。所谓滑动窗口,可理解为接收方所需要缓冲区的大小。
二 tcp建立连接
- 发起方发送请求建立连接报文,syn=1,初始化序号x,进入syn_send状态,TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
- 服务器收到报文后,如果同意连接,则发确认报文,ACK=1,SYN=1,确认序号x+1,同时初始化自己的序号y,此时服务器进入syn_rcvd状态,这个报文也不能携带数据,但是同样要消耗一个序号。
- 发起方收到服务器的确认报文后还要再发确认报文,ACK=1,确认序号y+1,此时tcp连接建立,客户端进入established状态,TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。当服务器收到后也进入es状态,双方可以通信。
三 三次握手问题补充
为什么两次不行?
首先信道传输不可靠,为了保证可靠传输,3次是理论上最小值。
在《计算机网络》一书中其中有提到,三次握手的目的是“为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误”。
这种情况是:发起方A发送的请求报文没有丢失,而是因为某些未知的原因在某个网络节点上发生滞留,导致在连接释放以后的某个时间才到服务器B,本来这是一个早已失效的报文段,但是B收到此失效的报文之后,会误认为是A再次发出的一个新的连接请求,于是B端就向A又发出确认报文,表示同意建立连接。如果不采用“三次握手”,那么只要B端发出确认报文就会认为新的连接已经建立了,但是A端并没有发出建立连接的请求,因此不会去向B端发送数据,B端没有收到数据就会一直等待,这样B端就会白白浪费掉很多资源。如果采用“三次握手”的话就不会出现这种情况,B端收到一个过时失效的报文段之后,向A端发出确认,此时A并没有要求建立连接,所以就不会向B端发送确认,这个时候B端也能够知道连接没有建立。
四 四次挥手
- 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
- 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号seq=v,此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
- 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
- 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
- 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
四 四次挥手问题补充
为什么是四次
为了确保数据能够完成传输。
关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
time_wait 为什么是2msl?
首先说下什么是MSL:
MSL是Maximum Segment Lifetime英文的缩写,中文可以译为“报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
为了保证A发送的最后一个ACK报文能够到达B。这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认。B会超时重传这个FIN+ACK报文段,而A就能在2MSL时间内收到这个重传的FIN+ACK报文段,重置时间等待计时器(2MSL)。如果A在TIME-WAIT状态不等待一段时间,而是在发送完ACK报文段后就立即释放连接,就无法收到B重传的FIN+ACK报文段,因而也不会再发送一次确认报文段。这样,B就无法按照正常的步骤进入CLOSED状态。说白了,2msl就是确保B能收到A的ACK报文。
11种状态
通用状态:
CLOSE:初始状态,表示tcp连接未建立
ESTABLISH:表示已建立连接
连接:
LISTEN:表示服务器某个socket处于监听状态,可以接受客户端的连接
SYN_SEND:表示客户端已发送syn报文。
SYN_RECV:表示服务器收到了客户端的syn报文,正常情况下,服务器收到ack报文后直接进入es状态,所以这个状态很少见。
断开:
FIN_WAIT_1:客户端发送完fin报文后进入的状态,收到ack后变成2
CLOSE_WAIT:表示等待关闭连接,处理未完成的事情。
FIN_WAIT_2:客户端收到ACK后进入FIN_WAIT_2状态,
LAST_ACK:当被动关闭的一方发送完FIN报文后,等待对方的ACK报文时,处于LAST_ACK状态,收到ACK报文后进入close状态
TIME_WAIT:客户端收到对方的FIN报文并发出ACK报文后,进入TIME_WAIT状态,时间为2*MSL,确保服务器收到ACK报文。
CLOSING(特殊情况): 正常情况下断开时在收到FIN报文后,在FIN_WAIT_1状态等待对方的ACK报文,但是如果收到的不是ACK报文,而是FIN报文的情况下,会进入CLOSING状态;什么情况下会发生呢?那就是双方同时关闭,同时发FIN报文,在这种情况下两方都会收到FIN报文,收到FIN后进入CLOSING状态,发送ACK报文,然后进入TIME_WAIT状态,接下来就正常断开了。