3. Server端的监听套接字与会话套接字的不同:

当server端的socket调用bind和listen之后,监听套接字的状态就会变为LISTEN状态,监听套接字的工作决定了它只是监听连接。

当server端调用accept,相当于从套接字的完成队列中取出一个client的连接,此时可以确定四元组,即:client的IP和port;server的IP和port,双方各有一个套接字进行交互,这里需要说一下server端的socket,我称server端accept后产生的套接字为会话套接字,这个套接字一出生的状态就是ESTABLISH。由server端得四元组我们可以看出,一个server从理论上可以接收的连接数量取决于文件描述符的个数。

4,状态为TIME_WAIT是不是所有执行主动关闭的socket都会进入TIME_WAIT状态呢?

有没有什么情况使主动关闭的socket直接进入CLOSED状态呢?

主动关闭的一方在发送最后一个 ack 后就会进入 TIME_WAIT 状态 停留2MSL(max segment lifetime)时间,这个是TCP/IP必不可少的,也就是“解决”不了的。也就是TCP/IP设计者本来是这么设计的。

主要有两个原因:

1。防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)

2。可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发 fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以 主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。

特别提示的是:为什么TIME_WAIT状态还需要等待2MSL才能回到CLOSED状态?或者为什么TCP要引入TIME_WAIT状态?

《TCP/IP详解》中如此解释:当TCP执行一个主动关闭,并发回最后一个ACK后,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL,这样可以让TCP再次发送最后的ACK以防止这个ACK丢失(另一端超时重发最后的FIN)。

附注:MSL(Maximum Segment Lifetime)即最大生存时间,RFC 793中指出MSL为2分钟,但是实现中的常用值为30秒、1分钟或者2分钟。

建立TCP连接(三次握手)

由于TCP协议提供可靠的连接服务,于是采用有保障的三次握手方式来创建一个TCP连接。三次握手的具体过程如下:

客户端发送一个带SYN标志的TCP报文(报文1)到服务器端,表示希望建立一个TCP连接。

服务器发送一个带ACK标志和SYN标志的TCP报文(报文2)给客户端,ACK用于对报文1的回应,SYN用于询问客户端是否准备好进行数据传输。

客户端发送一个带ACK标志的TCP报文(报文3),作为报文2的回应。

至此,一个TCP连接就建立起来了。

终止TCP连接(四次挥手)

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。原则是主动关闭的一方(如已传输完所有数据等原因)发送一个FIN报文来表示终止这个方向的连接,收到一个FIN意味着这个方向不再有数据流动,但另一个方向仍能继续发送数据,直到另一个方向也发送FIN报文。

四次挥手的具体过程如下:

客户端发送一个FIN报文(报文4)给服务器,表示我将关闭客户端到服务器端这个方向的连接。

服务器收到报文4后,发送一个ACK报文(报文5)给客户端,序号为报文4的序号加1。

服务器发送一个FIN报文(报文6)给客户端,表示自己也将关闭服务器端到客户端这个方向的连接。

客户端收到报文6后,发回一个ACK报文(报文7)给服务器,序号为报文6的序号加1。

至此,一个TCP连接就关闭了。(4次挥手不是关闭TCP连接的唯一办法,见下文Q3疑问)

TCP三次握手,四次挥手的时序图:

TCP三次握手,四次挥手的时序图:

TCP相关疑问

几个常见的TCP/IP相关的疑问:

Q1 为什么在TCP协议里,建立连接是三次握手,而关闭连接却是四次握手呢?

A1因为当处于LISTEN 状态的服务器端SOCKET当收到SYN报文(客户端希望新建一个TCP连接)后,它可以把ACK(应答作用)和SYN(同步作用)放在同一个报文里来发送给客户端。但在关闭TCP连接时,当收到对方的FIN报文时,对方仅仅表示对方没有数据发送给你了,但未必你的所有数据都已经全部发送给了对方,所以你大可不必马上关闭SOCKET(发送一个FIN报文),等你发送完剩余的数据给对方之后,再发送FIN报文给对方来表示你同意现在关闭连接了,所以通常情况下,这里的ACK报文和FIN报文都是分开发送的。

Q2为什么TIME_WAIT 状态还需要等2*MSL秒之后才能返回到CLOSED 状态呢?

A2因为虽然双方都同意关闭连接了,而且握手的4个报文也都发送完毕,按理可以直接回到CLOSED 状态(就好比从SYN_SENT 状态到ESTABLISH 状态那样),但是我们必须假想网络是不可靠的,你无法保证你(客户端)最后发送的ACK报文一定会被对方收到,就是说对方处于LAST_ACK 状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT 状态的作用就是用来重发可能丢失的ACK报文。

Q3关闭TCP连接一定需要4次挥手吗?

A3不一定,4次挥手关闭TCP连接是最安全的做法。但在有些时候,我们不喜欢TIME_WAIT 状态(如当MSL数值设置过大导致服务器端有太多TIME_WAIT状态的TCP连接,减少这些条目数可以更快地关闭连接,为新连接释放更多资源),这时我们可以通过设置SOCKET变量的SO_LINGER标志来避免SOCKET在close()之后进入TIME_WAIT状态,这时将通过发送RST强制终止TCP连接(取代正常的TCP四次握手的终止方式)。但这并不是一个很好的主意,TIME_WAIT 对于我们来说往往是有利的。


相关内容