一 TCP连接的建立过程:

java socket tcp client 长连接_数据结构与算法

 

1 在java中,何时完成三次握手:

        当服务端的serversocket构造方法返回,创建好底层数据结构FD,启动对指定端口的监听时.是在客户端socket的构造函数中与服务端的socket进行连接;也可以先调用无参的构造方法得到空socket对象,然后再调用connect方法进行连接,当构造方法或connect()方法返回时,客户端与服务端的底层TCP的三次握手已经完成.

 

2 关于serversocket的accept()方法:

(1)serversocket底层数据结构中有两个队列:syn队列和accept队列,当,这两个队列的大小与backlog的值(见第四点总结)有关;

(2)当服务端接收到客户端的连接请求后,会在serversocket底层数据结构的syn队列中创建一个未完全连接的套接字,其状态为SYN-RCVD,然后向客户端回一个握手确认消息

(3)在接收到客户端发来的3次握手的第3条消息之前,服务器TCP并不会认为握手消息已经完成.第3条握手消息到来后,syn队列对应的套接字数据结构的状态设置为ESTABLISHED,并会被移动到accept队列,该队列的套接字代表了能够通过accept()方法进行接收的已成功建立的连接.(如果第3条握手消息接收失败,或者超时,那么会将syn队列对应的套接字数据结构删除)

(4)但在客户端,只要接收到了开放握手的第2条消息,就可以立即发送数据,这可能比服务器调用accept()方法为其获取一个Socket实例要早很长时间,但这并不影响数据的传输,因为套接字低层的接收缓冲区已经创建,调用accept()方法后,是等待accept队列中有新的连接,然后java层面会创建一个已建立的连接对应的Socket对象返回,并把新的连接的数据结构从accept队列中删除.

(5)对于应用服务器来说,如果ACCEPT队列中有已经建立好的TCP连接,却没有及时的把它取出来(在java中是调用accept()方法),这样,一旦导致两个队列满了后,就会使客户端不能再建立新连接,所以,如TOMCAT等服务器会使用独立的线程,只做accept获取连接这一件事,以防止不能及时的去accept获取连接。

(6)在企业级的服务器进程中,若某一线程既用accept获取新连接,又继续在这个连接上读、写字符流,那么,这个连接对应的套接字通常要设为非阻塞。这样调用accept时不会长期占用所属线程的CPU时间片,使得线程能够及时的做其他工作

 

 

二 握手时的MSS告知

 

1 关于IP数据报的分片对性能的影响

        由于每一种链路层协议都规定了帧的数据部分的长度上限,即最大传输单元MTU,所以当IP数据报长度超过MTU时,必须对数据报进行分片传输.

        IP层同时希望这个分片对于传输层来说是透明的,接收方的IP层会根据收到的多个IP包头部,将发送方IP层分片出的IP包重组为一个消息。但这种IP层的分片效率是很差的,因为必须所有分片都到达才能重组成一个包,其中任何一个分片丢失了,都必须重发所有分片。

 

2 使用MSS有效避免IP数据报的分片

        为了避免IP层的分片,TCP协议定义了一个新的概念:最大报文段长度MSS。它定义了一个TCP连接上,一个主机期望对端主机发送单个报文的最大长度。TCP3次握手建立连接时,连接双方都要互相告知自己期望接收到的MSS大小。

        用以太网的MTU减去IP和TCP头部的长度,就是MSS的长度,三次握手中,SYN包都会携带期望的MSS值.当应用层调用TCP层提供的发送方法时,内核的TCP模块会按照对方告知的MSS来分片,把消息流分为多个网络分组,再调用IP层的方法发送数据。

        在建立握手时告知对方期望接收的MSS值并不一定靠得住。因为这个值是预估的,TCP连接上的两台主机若处于不同的网络中,那么,连接上可能有许多中间网络,这些网络分别具有不同的数据链路层,这样,TCP连接上有许多个MTU。特别是,若中间途径的MTU小于两台主机所在的网络MTU时,选定的MSS仍然太大了,会导致中间路由器出现IP层的分片。

        怎样避免中间网络可能出现的分片呢?

        通过IP头部的DF标志位,这个标志位是告诉IP报文所途经的所有IP层代码:不要对这个报文分片。如果一个IP报文太大必须要分片,则直接返回一个ICMP错误,说明必须要分片了,且返回待分片路由器网络接受的MTU值。这样,连接上的发送方主机就可以重新确定MSS。

        MSS的修正是协议内部实现的功能,应用层不用做额外的处理.

 

 三  地址重用

     在使用TCP连接方式的时候,当一个绑定到指定端口的描述符主动断开时,在这个端口上的连接将进入TIME_WAIT状态,并且会等待2*MSL时间才释放。如果在这个时间内,需要再次绑定相同的指定的端口,就需要设置描述符的SO_REUSEADDR属性为true,否则绑定会失败。  

 

四 关于syn队列和accept队列,backlog,

1.syn队列是一个未完成连接的队列,此队列维护着那些已收到了客户端SYN分节信息,等待完成三路握手的连接,socket的状态是SYN_RCVD

2.accept队列是一个已完成的连接的队列,此队列包含了那些已经完成三路握手的连接,socket的状态是ESTABLISHED

3.backlog参数历史上被定义为上面两个队列的大小之和

4.Berkely实现中的backlog值为上面两队列之和再乘以1.5

5.当客户端的第一个SYN到达的时候,TCP会在未完成队列中增加一个新的记录然后回复给客户端三路握手中的第二个分节(服务端的SYN和针对客户端的ACK),这条记录会在未完成队列中一直存在,直到三路握手中的最后一个分节到达,或者直到超时(Berkeley时间将这个超时定义为75秒)

6.如果当客户端SYN到达的时候队列已满,TCP将会忽略后续到达的SYN,但是不会给客户端发送RST信息,因为此时允许客户端重传SYN分节,如果返回错误信息,那么客户端将无法分清到底是服务端对应端口上没有相应应用程序还是服务端对应端口上队列已满这两种情况