《Java TCP/IP Socket 编程 》读书笔记之十一:深入剖析socket——TCP套接字的生命周期
转载请注明出处:http://blog.csdn.net/ns_code/article/details/16113083
建立TCP连接
客户端连接的建立
Socket构造函数的调用与客户端连接建立时所关联的协议事件之间的关系下图所示:
当客户端以服务器端的互联网地址W.X.Y.Z和端口号Q作为参数,调用Socket的构造函数时,底层实现将创建一个套接字实例,该实例的初始状态是关闭的。TCP开放握手也称为3次握手,这通常包括3条消息:一条从客户端到服务端的连接请求,一条从服务端到客户端的确认消息,以及另一条从客户端到服务端的确认消息。对客户端而言,一旦它收到了服务端发来的确认消息,就立即认为连接已经建立。通常这个过程发生的很快,但连接请求消息或服务端的回复消息都有可能在传输过程中丢失,因此TCP协议实现将以递增的时间间隔重复发送几次握手消息。如果TCP客户端在一段时间后还没有收到服务端的回复消息,则发生超时并放弃连接。如果服务端并没有接收连接,则服务端的TCP将发送一条拒绝消息而不是确认消息。
服务端连接的建立
当客户端的事件序列则有所不同。服务端首先创建一个ServerSocket实例,并将其与已知端口相关联(在此为Q),套接字实现为新的ServerSocket实例创建一个底层数据结构,并就Q赋给本地端口,并将特定的通配符(*)赋给本地IP地址(服务器可能有多个IP地址,不过通常不会指定该参数),如下图所示:
现在服务端可以调用ServerSocket的accept()方法,来将阻塞等待客户端连接请求的到来。当客户端的连接请求到来时,将为连接创建一个新的套接字数据结构。该套接字的地址根据到来的分组报文设置:分组报文的目标互联网地址和端口号成为该套接字的本地互联网地址和端口号;而分组报文的源地址和端口号则成为改套接字的远程互联网地址和端口号。注意,新套接字的本地端口号总是与ServerSocket的端口号一致。除了要创建一个新的底层套接字数据结构外,服务端的TCP实现还要向客户端发送一个TCP握手确认消息。如下图所示:
但是,对于服务端来说,在接收到客户端发来的第3条消息之前,服务端TCP并不会认为握手消息已经完成。一旦收到客户端发来的第3条消息,则表示连接已建立,此时一个新的数据结构将从服务端所关联的列表中移除,并为创建一个Socket实例,作为accept()方法的返回值。如下图所示:
这里有非常重要的一点需要注意,在ServerSocket关联的列表中的每个数据结构,都代表了一个与另一端的客户端已经完成建立的TCP连接。实际上,客户只要收到了开放握手的第2条消息,就可以立即发送数据——这可能比服务端调用accept()方法为其获取一个Socket实例要早很长时间。
关闭TCP连接
TCP协议有一个优雅的关闭机制,以保证应用程序在关闭时不必担心正在传输的数据会丢失,这个机制还可以设计为允许两个方向的数据传输相互独立地终止。关闭机制的工作流程是:应用程序通过调用连接套接字的close()方法或shutdownOutput()方法表明数据已经发送完毕。底层TCP实现首先将留在SendQ队列中的数据传输出去(这还要依赖于另一端的RecvQ队列的剩余空间),然后向另一端发送一个关闭TCP连接的握手消息。该关闭握手消息可以看做流结束的标志:它告诉接收端TCP不会再有新的数据传入RecvQ队列了。注意:关闭握手消息本身并没有传递给接收端应用程序,而是通过read()方法返回-1来指示其在字节流中的位置。而正在关闭的TCP将等待其关闭握手消息的确认消息,该确认消息表明在连接上传输的所有数据已经安全地传输到了RecvQ中。只要收到了确认消息,该连接变成了“半关闭”状态。直到连接的另一个方向上收到了对称的握手消息后,连接才完全关闭——也就是说,连接的两端都表明它们没有数据发送了。
TCP连接的关闭事件序列可能以两种方式发生:一种方式是先由一个应用程序调用close()方法或shutdownOutput方法,并在另一端调用close()方法之前完成其关闭握手消息;另一种方式是两端同时调用close()方法,他们的关闭握手消息在网络上交叉传输。下图展示了以第一种方式关闭连接时,发起关闭的一端底层实现中的事件序列:
注意,如果连接处于半关闭状态时,远程终端已经离开,那么本地底层数据结构则无限期地保持在该状态。当另一端的关闭握手消息到达后,则发回一条确认消息并将状态改为“Time—Wait”。虽然应用程序中相应的Socket实例可能早已消失,与之关联的底层数据结构还将在底层实现中继续存留几分钟。
对于没有首先发起关闭的一端,关闭握手消息达到后,它立即发回一个确认消息,并将连接状态改为“Close—Wait”。此时,只需要等待应用程序调用Socket的close()方法。调用该方法后,将发起最终的关闭消息 ,并释放底层套接字数据结构。 下图展示了没有首先发起关闭的一端底层实现中的事件序列:
注意这样一个事实:close()方法和shutdownOutput()方法都没有等待关闭握手的完成,而是调用后立即返回,这样,当应用程序调用close()方法或shutdownOutput()方法并成功关闭连接时,有可能还有数据留在SendQ队列中。如果连接的任何一端在数据传输到RecvQ队列之前崩溃,数据将丢失,而发送端应用程序却不会知道。
最好的解决方案是设计一种应用程序协议,以使首先调用close()方法的一方在接收到了应用程序的数据已接收保证后,才真正执行关闭操作。例如,在http://blog.csdn.net/ns_code/article/details/14642873这篇博客的分析示例中,客户端程序确认其接收到的字节数与其发送的字节数相等后,它就能够知道此时在连接的两个方向上都没有数据在传输,因此可以安全地关闭连接。
关闭TCP连接的最后微妙之处在于对Time—Wait状态的需要。TCP规范要求在终止连接时,两端的关闭握手都完成后,至少要有一个套接字在Time—Wait状态保持一段时间。这个要求的提出是由于消息在网络中传输时可能延迟。如果在连接两端都完成了关闭握手后,它们都移除了其底层数据结构,而此时在同样一对套接字地址之间又建立了新的连接,那么前一个连接在网络上传输时延迟的消息就可能在新建立的连接后到达。由于包含了相同的源地址和目的地址,旧消息就会被错误地认为是属于新连接的,其包含的数据就可能被错误地分配到应用程序中。虽然这种情况很少发生,TCP还是使用了包括Time—Write状态在内的多种机制对其进行防范。
Time—Wait状态最重要的作用是:只要底层套接字数据结构还存在,就不允许在相同的本地端口上关联其他套接字,尤其试图使用该端口创建新的Socket实例时,将抛出IOException异常。
《Java TCP/IP Socket 编程 》读书笔记之十一:深入剖析socket——TCP套接字的生命周期的更多相关文章
- TCP/IP网络编程 读书笔记1
本篇主干内容是TCP/IP网络编程1-9章学习笔记 1. linux文件描述符 描述符从3开始以由小到大的顺序编号,0,1,2,分配给标准I/O用作标准输入.标准输出和标准错误. 2. 协议族与套接字 ...
- 【Java TCP/IP Socket】深入剖析socket——TCP套接字的生命周期
建立TCP连接 新的Socket实例创建后,就立即能用于发送和接收数据.也就是说,当Socket实例返回时,它已经连接到了一个远程终端,并通过协议的底层实现完成了TCP消息或握手信息的交换. ...
- 《TCP/IP图解》读书笔记
看这本书的目的: 了解计算机之间是怎么通信的 熟悉TCP/IP协议 后面就这两个目的进行展开,要达到这两个目的,读这本书,学到了哪些知识. 一.计算机之间是怎么通信的 先来了解下面几个概念,中继器,二 ...
- TCP/IP知识总结(TCP/IP协议族读书笔记四)
参考:http://blog.chinaunix.net/uid-26275986-id-4109679.html 继续!TCP的流量控制和拥塞控制. TCP相对UDP可靠的地方在于它的拥塞控制.流量 ...
- TCP/IP知识总结(TCP/IP协议族读书笔记三)
接下来,总结传输层的两大协议UDP和TCP. 一.UDP(用户数据报协议) 讲UDP之间,先了解两个概念:有连接和无连接. 有连接:通信之前,通信双方必须建立一条通道: 无连接:不需要建立通道,发送方 ...
- TCP/IP知识总结(TCP/IP协议族读书笔记二)
接下来,总结一下网络层的协议,IP,ARP,RARP,ICMP,IGMP.当我们在网络传输的过程中,把分组交付到主机或路由器需要两级地址:物理地址和逻辑地址.而且我们需要能够把物理地址映射成为相应的逻 ...
- TCP/IP知识总结(TCP/IP协议族读书笔记一)
一.简述TCP/IP协议 Transmission Control Protocol/Internet Protocol的简写,即传输控制协议/互联网互联协议,又名网络通信协议.是Internet最基 ...
- TCP/IP详解 读书笔记:TCP:传输控制协议
TCP的服务 TCP为应用层提供一种面向连接的.可靠的字节流服务. 一个TCP连接中,仅有两方进行彼此通信,所以广播和多播不能用于TCP. TCP通过以下方式提供可靠性: 应用数据被切割为TCP认为最 ...
- TCP/IP详解 读书笔记(一):概述
分层 网络协议通常分不同层次进行开发,每一层负责不同的职责,一个协议簇指的是一组不同层次上的多个协议的组合. TCP/IP通常被认为是一个四层协议系统: 链路层:主要是处理与电缆或其他传输媒介的物理接 ...
随机推荐
- setFocus一定要写在setLayout设置的后面,否则不起作用——使用setFocusPolicy为控件设置不同的焦点策略:Tab焦点,Click焦点,Wheel焦点和没有焦点
QLineEdit* pEditor = new QLineEdit(m_strText); pEditor->resize(.......); pEditor->move(. ...
- 源码安装rsyslog
<pre name="code" class="html">下载下列软件 json-c-0.12-20140410.tar.gz---------- ...
- Genymotion配置及使用教程(最新最完整版附各部分下载地址)
Genymotion配置及使用教程(最新最完整版附各部分下载地址) FROM:http://blog.csdn.net/beiminglei/article/details/13776013 早都听说 ...
- 解决android加载图片时内存溢出问题
尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过jav ...
- javascript中对变量类型的推断
本文正式地址:http://www.xiabingbao.com/javascript/2015/07/04/javascript-type 在JavaScript中,有5种基本数据类型和1种复杂数据 ...
- html网页编码问题
之前碰到过一些html编码乱码问题,都理解的模模糊糊,问了别人解释的也是模模糊糊.近期要做前端这个问题研究了下仅仅须要两句话就能非常清楚的解释了(之前问的那些人是不是自己都没理解非常郁闷.) < ...
- Hadoop-04-HBase全然分布式环境搭建
Hbase分布式具体安装步骤 Hadoop全然分布式环境已经搭建完毕(參见01_Hadoop全然分布式环境搭建). 注意:Hbase和Hadoop的版本号必须相应! 不然会出现各种问题! 这里选用的是 ...
- linux中时间函数
linux下常用时间类型有四种: time_t . struct tm. struct timeval . struct timespec 1.time_t 时间函数 time_t ...
- jQuery validate api(转)
官网地址:http://bassistance.de/jquery-plugins/jquery-plugin-validation jQuery plugin: Validation 使用说明 转载 ...
- 用jersey + spring 实现rest服务及单元测试
jersey提供了强大的rest功能,可以通过简洁的标注和编码实现业务的需求,架构会透明的把你的pojo对象转化为客户端可以接受的json/xml文件模式,当然也可以用它做一些基于ajax的表单提交和 ...