(1)TCP状态转换图
其中图中分为三种状态:实线代表的主动发起连接,虚线代表的被动发起连接,细实线代表的可以双向发起连接的状态。
主动发起连接方状态变化:1)主动发起连接的一方发送SYN标志位,进入SYN_SENT状态,等待接收被发起连接方发送ACK应答和数据包序号,接收到ACK应答后,同时向被被发起连接方发送ACK应答,表示数据已经接收到,同一发起连接,此时进入ESTABLISHED状态,表示三次握手完成。2)主动发起连接的一方向另一方发送FIN标志位,请求关闭连接,立即进入FIN_WATI_1,等待被动发起连接方发送ACK应答信号,主动发起连接方接收到ACK应答信号后,进入FIN_WATI_2状态,代表主动发起连接端半关闭完成。此时,如果被动连接方发送FIN信号请求关闭连接,主动发起连接的一方会发送一个ACK应答信号同意关闭,但是此时不确定被动发起连接方是否收到信号(因为主动发起连接方已经关闭),所以要等待一个2MSL时间(确保最后发送的一个ACK应答信号被接收到),2MSL时间一到,被动连接方关闭,四次挥手完成。
被动发起连接方状态变化:1)被动发起连接方处于监听状态,等待连接,当被动发起连接方接收到主动方SYN状态请求连接时,被动发起连接方会发送一个ACK应答同时携带自己的数据报序号给主动方,进入SYN_RCVD状态,等待主动方发送ACK应答信号,当接收到主动方发起的ACK应答信号时,被动发起连接方进入ESTABLISHED状态,表示三次握手完成。2)当主动方发送FIN请求关闭时,被动连接方接收FIN并同时向主动方发送ACK应答,同意关闭,此时被动连接方进入CLOSE_WAIT状态。如果此时被动连接方发送FIN信号,则进入LAST_ACK状态,等待主动方的应答信号,当接收到主动方的应答信号,被动方关闭,四次挥手完成。
双向连接状态:1)当被动发起连接方进入SYN_RCVD状态,等待主动发起方发送ACK应答信号时,此时如果网络中断,则三次握手中断,重新进行三次握手,被接受方发送RST信号,重新连接。2)主动发起连接方接收到FIN信号进入FIN_WAIT_1状态,此时如果主动接收到ACK和FIN信号,同时给被动方发送ACK应答信号,则主动方进入TIME_WAIT状态,等待2MSL时间关闭文件。如果只收到ACK应答信号和FIN信号,则会进入CLOSING状态,当主动发送ACK应答信号时,主动方进入TIME_WAIT状态,等待2MSL时间关闭文件。
(2)C/S模型的TCP状态图
(3)半关闭
当TCP链接中A发送FIN请求关闭,B端回应ACK后(A端进入FIN_WAIT_2状态),B没有立即发送FIN给A,此时A处于半关闭状态,A可以接收B发送的数据,但是A不能向B发送数据了。
使用close关闭文件描述符只是中止一个连接,它减少的只是描述符的引用计数,并不直接关闭连接,当引用计数达到0时,才关闭连接。而使用shutdown不考虑描述符的引用计数,直接关闭描述符,也可以中止一个方向的连接,只中止读或者写。
#include<sys/socket.h>
int shutdown(int sockfd,int how)
参数:how SHUT_RD(0):关闭套接字读功能缓存区
SHUT_WR(1):关闭套接字写功能缓存区
SHUT_RDWR(2):关闭套接字读写功能缓存区
(4)端口复用
当服务器断开连接时,实际上此时服务器使用的端口处于TIME_WAIT状态,需要等待2MSL时间才能重新被利用。如果想要在服务器断开连接时端口可以被使用,则需要使用端口复用功能,具体方法是使用setsockopt()设置socket描述符选项的S0_REUSEADDR为1,表示允许创建端口号相同,但IP地址不同的多个socket描述符。
在server代码中的socket()和bind()之间插入代码:
int opt=1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));