一、TCP状态转换
CLOSED:表示
初始
状态。
LISTEN:表示
服务器端
的某个SOCKET处于监听
状态,可连接
。
SYN_SENT:表示
客户端已发送SYN报文
。这个状态与SYN_RCVD
遥相呼应,当客户端SOCKET
执行CONNECT
连接时,它首先发送SYN报文
,随即进入到了SYN_SENT
状态,并等待服务端的发送三次握手中的第2个报文。
SYN_RCVD: 表示接收到
SYN报文
,在正常情况下,这个状态是服务器端的SOCKET
在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂。此种状态时,当收到客户端的ACK报文后
,会进入到ESTABLISHED
状态。
ESTABLISHED:表示
连接已建立
。
FIN_WAIT_1: 表示
等待对方的FIN报文
。
区别是:
- FIN_WAIT_1状态是当socket在
ESTABLISHED
状态时,想主动关闭连接,向对方发送FIN报文
,此时该socket进入到FIN_WAIT_1状态。- FIN_WAIT_2状态是
当对方回应ACK
后,该socket进入到FIN_WAIT_2状态,正常情况下,对方应马上回应ACK报文,所以FIN_WAIT_1状态一般较难见到,而FIN_WAIT_2状态可用netstat看到。
FIN_WAIT_2:表示
等待对方的FIN报文
。主动关闭链接的一方,发出FIN收到ACK以后进入该状态。称之为半连接或半关闭状态。该状态下的socket只能接收数据,不能发。
TIME_WAIT: 表示
收到了对方的FIN报文
,并发送出了ACK报文
,等2MSL
后即可回到CLOSED可用
状态。如果FIN_WAIT_1状态下,收到对方同时带FIN标志
和ACK标志
的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
CLOSING: 这种状态较特殊,属于一种较
罕见
的状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报文
,再收到对方的FIN报文
。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文
。
- 什么情况下会出现此种情况呢?
如果双方几乎在同时close一个SOCKET
的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
CLOSE_WAIT: 表示在
等待关闭
。当对方关闭一个SOCKET后发送FIN报文给自己,系统会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT
状态。接下来呢,查看是否还有数据发送给对方,如果没有可以close这个SOCKET,发送FIN报文给对方,即关闭连接。所以在CLOSE_WAIT状态下,需要关闭连接。
LAST_ACK: 该状态是
被动关闭一方
在发送FIN报文后,最后等待对方的ACK报文
。当收到ACK报文后,即可以进入到CLOSED可用状态。
1、半关闭
当TCP链接中A
发送FIN请求关闭
,B端回应ACK后(A端进入FIN_WAIT_2状态),B没有立即发送FIN给A时
,A方处在半链接
状态,此时A可以接收B发送的数据,但是A已不能再向B发送数据
。
close与shutdown的区别:
- 使用
close
中止一个连接,但它只是减少描述符的引用计数
,并不直接关闭连接,只有当描述符的引用计数为0时才关闭连接
。shutdown
不考虑描述符的引用计数,直接关闭描述符
。也可选择中止一个方向的连接,只中止读
或只中止写
。
注意:
- 如果有
多个进程共享一个套接字
,close每被调用一次,计数减1,直到计数为0时,也就是所用进程都调用了close,套接字将被释放。- 在多进程中如果一个进程调用了
shutdown(sfd, SHUT_RDWR)
后,其它的进程将无法进行通信
。但,如果一个进程close(sfd)将不会影响到其它进程。
2、2MSL
为什么存在2MSL TIME_WAIT?
让
4次握手关闭
流程更可靠
;
- 4次握手的最后一个ACK是由
主动关闭方发送
出去的,若这个ACK丢失
,被动关闭方会再次发送
一个FIN过来- 若
主动关闭
方能够保持一个2MSL的TIME_WAIT
状态,则有更大的机会让丢失的ACK被再次发送出去。防止
lost duplicate
对后续新建正常链接的传输造成破坏
;
- lost uplicate在实际的网络中非常常见,常由于
路由器产生故障
,路径无法收敛
导致一个packet在路由器A,B,C之间做类似死循环
的跳转。IP头部有个TTL
,限制了一个包在网络中的最大跳数
,因此这个包有两种命运,要么最后TTL变为0,在网络中消失;要么TTL在变为0之前路由器路径收敛,它凭借剩余的TTL跳数终于到达目的地。但非常可惜的是TCP通过超时重传机制
在早些时候发送了一个跟它一模一样的包,并先于它达到了目的地
,因此它的命运也就注定被TCP协议栈抛弃
;
TCP协议栈
TCP是
流式
的,所有包到达的顺序不一致
的,依靠序列号
由TCP协议栈做顺序的拼接;假设一个incarnation connection
这时收到的seq=1000, 来了一个lost duplicate
为seq=1000,len=1000, 则TCP认为这个lost duplicate
合法,并存放入了receive buffer,导致传输出现错误。通过一个2MSL TIME_WAIT
状态,确保所有的lost duplicate
都会消失掉,避免对新连接造成错误。