TCP是面连接的可靠传输,具有流量控制,拥塞控制的保证数据包按序到达的传输控制协议。
TCP是面向连接的,因此TCP数据传输的过程是:建立连接 、数据传输、断开连接。
1.建立连接:三次握手:
client ----syn-----> server : syn_received
client <---ack\syn-- server: syn_sent
client ----ack------> server : established.
2. 数据传输: 拥塞控制,流量控制、(停等协议, 慢启动、快恢复)等等,
3. 连接的断开:四次握手
状态迁移
1). SO_LINGER/ SO_REUSEADDR
TCP正常的关闭过程如下(四次握手过程):
(FIN_WAIT_1) A ---FIN---> B(CLOSE_WAIT)
(FIN_WAIT_2) A <--ACK-- B(CLOSE_WAIT)
(TIME_WAIT) A <--FIN---- B(LAST_ACK)
(TIME_WAIT) A ---ACK-> B(CLOSED)
Ø A端首先发送一个FIN请求给B端,请求断开连接,
发送FIN请求后, A端的TCP状态变为FIN_WAIT_1,
接收到FIN请求后, B端的TCP状态变更为CLOSE_WAIT。
Ø B端接收到FIN请求后,B端回一个ACK给A端,确认接收到的FIN请求,
A接收到ACK确认后,A端的TCP状态变更为为FIN_WAIT_2。
Ø B端接着再发送一个FIN请求给A端,
与连接过程的3次握手过程不一样,这个FIN请求之所以并不是与上一个确认一起发送,
之所以如此处理,是因为TCP是双通道的,允许在发送ACK请求后,并不马上发FIN请求,
即只关闭A到B端的数据流,仍然允许B端到A端的数据流。
这个FIN请求发送之后,B端的TCP状态变更为LAST_ACK,
A端的状态变更为TIME_WAIT。
Ø A端接收到B端的FIN请求后,再回B端一个ACK信息,对上一个FIN请求进行确认,到此时B端状态变更为CLOSED,Socket可以关闭。
除了如上正常的关闭(优雅关闭)之外,TCP还提供了另外一种非优雅的关闭方式RST(Reset)
(CLOSED) A ---RST--> B (CLOSED)
Ø A端发送RST状态之后,TCP进入CLOSED状态,B端接收到RST后,也即可进入CLOSED状态。
正常(优雅)关闭存在的问题 :
在第一种关闭方式上(优雅关闭),非常遗憾,A端在最后发送一个ACK请求后,并不能马上将该Socket回收,
因为A并不能确定B一定能够接收到这个ACK确认,因此A端必须对这个Socket维持TIME_WAIT状态2MSL.
(MSL=Max Segment Lifetime,取决于操作系统和TCP实现,该值为30秒、60秒或2分钟)。
如果A端是客户端,这并不会成为问题;但如果A端是服务端,那就很危险了,如果连接的Socket非常多,而又维持如此多的TIME_WAIT状态的话,那么有可能会将Socket耗尽(报Too Many Open File)。
服务端为了解决这个问题,可选择的方式有三种:
Ø 保证由客户端主动发起关闭(即B端为服务器)
Ø 关闭的时候使用RST的方式(非优雅关闭)
Ø 对处于TIME_WAIT状态的TCP允许重用
一般我们当然最好是选择第一种方式,
实在没有办法的时候,我们可以使用SO_LINGER选择第二种方式,使用SO_REUSEADDR选择第三种方式