`
wen866595
  • 浏览: 263915 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

TCP连接关闭

 
阅读更多

关闭机制的工作流程是:应用程序通过调用套接字的close方法或shutdownOutput方法表明数据已经发送完毕。此刻,底层的TCP实现首先将留存在SendQ队列中的数据传输出去(还要依赖于另一端RecvQ队列的剩余空间),然后向另一端发送一个关闭TCP连接的握手信息。该关闭握手消息可以看作是流终止标志:它告诉接收到TCP不会再有新的数据传入RecvQ队列了。(注意,关闭握手消息本身并没有传递给接收到应用程序,而是通过read方法返回-1来指示其在字节流中的位置。)正在关闭的TCP将等待其关闭握手信息的确认信息,该确认信息表明在连接上传输的所有数据已经完全地传输到了RecvQ中。只要收到了确认消息,该连接就变成“半关闭(Half closed)”状态。直到连接的另一个方向中收到了对称的握手信息后,连接才完全关闭--也就是说,连接的两端都表明它们在没有数据要发送了。

 

TCP连接的关闭事件序列可能以两种方式发生:一种方式是先由一个应用程序调用close()方法(或shutdownOutput()方法),并在另一端调用close()方法之前完成其关闭握手消息;另一种方式是两端同时调用close()方法,它们的关闭握手消息在网络上交叉传输。

 

下图展示了以第一种方式关闭连接时,底层实现中的事件序列:



 关闭握手消息已经发送,套接字数据结构的状态也已经设置为“Closing”(FIN_WAIT_1),然后,close()调用返回。完成这些工作后,将禁止在该Socket上的任何读写操作(会抛出异常)。当收到关闭握手确认消息后,套接字数据结构的状态则改变为“半关闭”(FIN_WAIT_2),这种状态将一直持续,直到接收到另一端的关闭握手消息。

注意:如果连接处于半关闭状态时,远程终端已经离开,那么本地底层数据结构则将无限期地保持在该状态。当另一端的关闭握手消息到达后,则发回一条确认消息并将状态改变成“Time-Wait”。虽然应用程序中相应的Socket实例可能早已消失,与之关联的底层数据结构还将在底层实现中继续存留几分钟。

 

 

 

下图展示了没有首先发起关闭的终端上的事件序列:


 

关闭消息到达后,它立即发回一个确认消息,并将连接状态改变为“Close-Wait”。
此时,只需要等待应用程序调用Socket的close()方法。调用该方法后,将发起最终的关闭握手消息,并释放底层套接字数据结构,虽然对原始Socket实力的引用仍然留存在Java 程序中。

注意:close()方法和shutdownOutput()方法都没有等待关闭握手的完成,而是调用后立即返回。

Socket.setSoLinger()用于控制close()方法在返回前是否等待关闭握手的完成。setSoLinger()方法没有为当前实现的引用程序提供任何额外担保。

由于消息在网络中传输时看你延迟,TCP规范要求在终止连接时,两端的关闭握手都完成后,至少要有一个套接字在Time-Wait状态保持一段时间。如果在连接两端都完成了关闭握手后,它们都移除了其底层数据结构,而此时在同样一对套接字地址之间又立即建立了新的连接,那么前一个连接在网络上传输时延迟的消息就可能在新连接建立后到达。由于其包含了相同的源地址和目的地址,旧消息就会被错误地认为是属于新连接的,其包含的数据就可能被错误地分配到应用程序中。

Time-Wait状态用于保证每个TCP连接都在一段平静时间内结束,这期间不会有数据发送。平静时间的长度应该等于分组报文在网络上留存的最长时间的两倍。

Time-Wait状态最重要的作用是,只要底层套接字数据结构还存在,就不允许在相同的本地端口上关联其他套接字。尤其是试图使用该端口创建新的Socket实例时,将抛出IOException异常。

 

 

第一种情况的时序图:

第二种情况的时序图:

  • 大小: 16.2 KB
  • 大小: 21.4 KB
  • 大小: 27.6 KB
  • 大小: 16 KB
2
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics