2024年6月7日发(作者:)

JAVA Socket超时浅析 转

套接字或插座(socket)是一种软件形式的抽象,用于表达两台机器间一

个连接的"终端"。针对一个特定的连接,每台机器上都有一个"套接字",可以

想象它们之间有一条虚拟的"线缆"。JAVA有两个基于数据流的套接字类:Serv

erSocket,服务器用它"侦听"进入的连接;Socket,客户端用它初始一次连接

。侦听套接字只能接收新的连接请求,不能接收实际的数据包。

套接字是基于TCP/IP实现的,它是用来提供一个访问TCP的服务接口,或者说

套接字socket是TCP的应用编程接口API,通过它应用层就可以访问TCP提供的

服务。

在JAVA中,我们用ServerSocket、Socket类创建一个套接字连接,从套接字得

到的结果是一个InputStream以及OutputStream对象,以便将连接作为一个IO

流对象对待。通过IO流可以从流中读取数据或者写数据到流中,读写IO流会有

异常IOException产生。

套接字底层是基于TCP的,所以socket的超时和TCP超时是相同的。下面先讨论

套接字读写缓冲区,接着讨论连接建立超时、读写超时以及JAVA套接字编程的

嵌套异常捕获和一个超时例子程序的抓包示例。

1 socket读写缓冲区

一旦创建了一个套接字实例,操作系统就会为其分配缓冲区以存放接收和要发

送的数据。

JAVA可以设置读写缓冲区的大小-setReceiveBufferSize(int

size),setSendBufferSize(int size)。

向输出流写数据并不意味着数据实际上已经被发送,它们只是被复制到了发送

缓冲区队列SendQ,就是在Socket的OutputStream上调用flush()方法,也不能

保证数据能够立即发送到网络。真正的数据发送是由操作系统的TCP协议栈模

块从缓冲区中取数据发送到网络来完成的。

当有数据从网络来到时,TCP协议栈模块接收数据并放入接收缓冲区队列RecvQ

,输入流InputStream通过read方法从RecvQ中取出数据。

2 socket连接建立超时

socket连接建立是基于TCP的连接建立过程。TCP的连接需要通过3次握手报文

来完成,开始建立TCP连接时需要发送同步SYN报文,然后等待确认报文SYN+AC

K,最后再发送确认报文ACK。TCP连接的关闭通过4次挥手来完成,主动关闭TC

P连接的一方发送FIN报文,等待对方的确认报文;被动关闭的一方也发送FIN

报文,然等待确认报文。

正在等待TCP连接请求的一端有一个固定长度的连接队列,该队列中的连接已

经被TCP接受(即三次握手已经完成),但还没有被应用层所接受。TCP接受一个

连接是将其放入这个连接队列,而应用层接受连接是将其从该队列中移出。应

用层可以通过设置backlog变量来指明该连接队列的最大长度,即已被TCP接受

而等待应用层接受的最大连接数。

当一个连接请求SYN到达时,TCP确定是否接受这个连接。如果队列中还有空间

,TCP模块将对SYN进行确认并完成连接的建立。但应用层只有在三次握手中的

第三个报文收到后才会知道这个新连接。如果队列没有空间,TCP将不理会收

到的SYN。

如果应用层不能及时接受已被TCP接受的连接,这些连接可能占满整个连接队

列,新的连接请求可能不被响应而会超时。如果一个连接请求SYN发送后,一

段时间后没有收到确认SYN+ACK,TCP会重传这个连接请求SYN两次,每次重传

的时间间隔加倍,在规定的时间内仍没有收到SYN+ACK,TCP将放弃这个连接请

求,连接建立就超时了。

JAVA

Socket连接建立超时和TCP是相同的,如果TCP建立连接时三次握手超时,那么

导致Socket连接建立也就超时了。可以设置Socket连接建立的超时时间-

connect(SocketAddress endpoint,int timeout)

如果在timeout内,连接没有建立成功,在TimeoutException异常被抛出。如

果timeout的值小于三次握手的时间,那么Socket连接永远也不会建立。

不同的应用层有不同的连接建立过程,Socket的连接建立和TCP一样-

仅仅需要三次握手就完成连接,但有些应用程序需要交互很多信息后才能成功

建立连接,比如Telnet协议,在TCP三次握手完成后,需要进行选项协商之后

,Telnet连接才建立完成。

3 socket读超时

如果输入缓冲队列RecvQ中没有数据,read操作会一直阻塞而挂起线程,直到

有新的数据到来或者有异常产生。调用setSoTimeout(int