JAVA Socket超时浅析(转)

  套接字或插座(socket)是一种软件形式的抽象,用于表达两台机器间一个连接的“终端”。针对一个特定的连接,每台机器上都有一个“套接字”,可以想象它们之间有一条虚拟的“线缆”。JAVA有两个基于数据流的套接字类:ServerSocket,服务器用它“侦听”进入的连接;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+ACK,最后再发送确认报文ACK。TCP连接的关闭通过4次挥手来完成,主动关闭TCP连接的一方发送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 timeout)可以设置超时时间,如果到了超时时间仍没有数据,read会抛出一个SocketTimeoutException,程序需要捕获这个异常,但是当前的socket连接仍然是有效的。

  如果对方进程崩溃、对方机器突然重启、网络断开,本端的read会一直阻塞下去,这时设置超时时间是非常重要的,否则调用read的线程会一直挂起。

  TCP模块把接收到的数据放入RecvQ中,直到应用层调用输入流的read方法来读取。如果RecvQ队列被填满了,这时TCP会根据滑动窗口机制通知对方不要继续发送数据,本端停止接收从对端发送来的数据,直到接收者应用程序调用输入流的read方法后腾出了空间。

4 socket写超时

  socket的写超时是基于TCP的超时重传。超时重传是TCP保证数据可靠性传输的一个重要机制,其原理是在发送一个数据报文后就开启一个计时器,在一定时间内如果没有得到发送报文的确认ACK,那么就重新发送报文。如果重新发送多次之后,仍没有确认报文,就发送一个复位报文RST,然后关闭TCP连接。首次数据报文发送与复位报文传输之间的时间差大约为9分钟,也就是说如果9分钟内没有得到确认报文,就关闭连接。但是这个值是根据不同的TCP协议栈实现而不同。

  如果发送端调用write持续地写出数据,直到SendQ队列被填满。如果在SendQ队列已满时调用write方法,则write将被阻塞,直到SendQ有新的空闲空间为止,也就是说直到一些字节传输到了接收者套接字的RecvQ中。如果此时RecvQ队列也已经被填满,所有操作都将停止,直到接收端调用read方法将一些字节传输到应用程序。

  当Socket的write发送数据时,如果网线断开、对端进程崩溃或者对端机器重启动,TCP模块会重传数据,最后超时而关闭连接。下次如再调用write会导致一个异常而退出。

  Socket写超时是基于TCP协议栈的超时重传机制,一般不需要设置write的超时时间,也没有提供这种方法。

5 双重嵌套异常捕获

   如果ServerSocket、Socket构造失败,只需要仅仅捕获这个构造失败异常而不需要调用套接字的close方法来释放资源(必须保证构造失败后不会留下任何需要清除的资源),因为这时套接字内部资源没有被成功分配。如果构造成功,必须进入一个try finally语句块里调用close释放套接字。请参照下面例子程序。

 

 

[java:nogutter] view plaincopy

 

 

  1. import java.net.*;  
  2. import java.io.*;  
  3. public class SocketClientTest  
  4. {  
  5.   public static final int PORT = 8088;  
  6.   public static void main( String[] args ) throws Exception  
  7.   {  
  8.     InetAddress addr = InetAddress.getByName( "127.0.0.1" );  
  9.     Socket socket = new Socket();  
  10.     try  
  11.     {  
  12.       socket.connect( new InetSocketAddress( addr, PORT ), 30000 );  
  13.       socket.setSendBufferSize(100);  
  14.         
  15.       BufferedWriter out = new BufferedWriter( new OutputStreamWriter( socket.getOutputStream() ) );  
  16.       int i = 0;  
  17.         
  18.       while( true )  
  19.       {  
  20.         System.out.println( "client sent --- hello *** " + i++ );  
  21.         out.write( "client sent --- hello *** " + i );  
  22.         out.flush();  
  23.           
  24.         Thread.sleep( 1000 );  
  25.       }  
  26.     }  
  27.     finally  
  28.     {  
  29.       socket.close();  
  30.     }  
  31.   }  
  32. }  

 

 

 

[java:nogutter] view plaincopy

 

 

  1. import java.io.*;  
  2. import java.net.ServerSocket;  
  3. import java.net.Socket;  
  4. public class SocketServerTest  
  5. {  
  6.   public static final int PORT = 8088;  
  7.   public static final int BACKLOG = 2;  
  8.   public static void main( String[] args ) throws IOException  
  9.   {  
  10.     ServerSocket server = new ServerSocket( PORT, BACKLOG );  
  11.     System.out.println("started: " + server);  
  12.     try  
  13.     {  
  14.       Socket socket = server.accept();  
  15.       try  
  16.       {  
  17.         BufferedReader in = new BufferedReader( new InputStreamReader( socket.getInputStream() ) );  
  18.         String info = null;  
  19.           
  20.         while( ( info = in.readLine() ) != null )  
  21.         {  
  22.           System.out.println( info );  
  23.         }  
  24.       }  
  25.       finally  
  26.       {  
  27.         socket.close();  
  28.       }  
  29.     }  
  30.     finally  
  31.     {  
  32.       server.close();  
  33.     }  
  34.   }  
  35. }  

 

 

  执行上面的程序,在程序运行一会儿之后,断开client和server之间的网络连接,在机器上输出如下:

 

Server上的输出:

 

Echoing:client sent -----hello0

 

Echoing:client sent -----hello1

Echoing:client sent -----hello2

Echoing:client sent -----hello3

Echoing:client sent -----hello4

Echoing:client sent -----hello5

Echoing:client sent -----hello6

 

---->> 断开了网络连接之后没有数据输出

 

Client上的输出:

socket default timeout = 0

socket = Socket[addr=/10.15.9.99,port=8088,localport=4691]

begin to read

client sent --- hello *** 0

client sent --- hello *** 1

client sent --- hello *** 2

client sent --- hello *** 3

client sent --- hello *** 4

client sent --- hello *** 5

client sent --- hello *** 6

client sent --- hello *** 7

client sent --- hello *** 8  

client sent --- hello *** 9

client sent --- hello *** 10

 

 ---->> 断开网络连接后客户端进程挂起

 

java.net.SocketException : Connection reset by peer: socket write error

    at java.net.SocketOutputStream.socketWrite0( Native Method )

    at java.net.SocketOutputStream.socketWrite( SocketOutputStream.java:92 )

    at java.net.SocketOutputStream.write( SocketOutputStream.java:136 )

    at sun.nio.cs.StreamEncoder.writeBytes( StreamEncoder.java:202 )

    at sun.nio.cs.StreamEncoder.implFlushBuffer( StreamEncoder.java:272 )

    at sun.nio.cs.StreamEncoder.implFlush( StreamEncoder.java:276 )

    at sun.nio.cs.StreamEncoder.flush( StreamEncoder.java:122 )

    at java.io.OutputStreamWriter.flush( OutputStreamWriter.java:212 )

    at java.io.BufferedWriter.flush( BufferedWriter.java:236 )

    at com.xtera.view.SocketClientTest.main( SocketClientTest.java:99 )

 

  当hello6被发送到server端后,网络连接被断开,这时server端不能接收任何数据而挂起。client端仍然继续发送数据,实际上hello7、hello8、hello9、hello10都被复制到SendQ队列中,write方法立即返回。当client的SendQ队列被填满之后,write方法就被阻塞。TCP模块在发送报文hello7之后,没有收到确认而超时重传,再重传几次之后关闭了TCP连接,同时导致被阻塞的write方法异常返回。

  通过抓包工具,我们可以看到超时重传的报文。

 

 

 

 

 

 

 

 

http://blog.csdn.net/zdwzzu2006/article/details/7748006

时间: 2024-11-21 03:40:03

JAVA Socket超时浅析(转)的相关文章

Java Socket 网络编程常见异常

原文:http://blog.csdn.net/xyz_lmn/article/details/7901474 1.java.net.SocketTimeoutException.这个异常比较常见,socket超时.一般有2个地方会抛出这个,一个是connect的时候,这个超时参数由connect(SocketAddress endpoint,int timeout)中的后者来决定,还有就是setSoTimeout(int timeout),这个是设定读取的超时时间.它们设置成0均表示无限大.

java socket重要参数讲解

Java Socket的api可能很多人会用,但是Java Socket的参数可能很多人都不知道用来干嘛的,甚至都不知道有这些参数. backlog 用于ServerSocket,配置ServerSocket的最大客户端等待队列.等待队列的意思,先看下面代码 public class Main { public static void main(String[] args) throws Exception { int port = 8999; int backlog = 2; ServerSo

java socket 的参数选项解读(转)

  java socket中有很多参数可以选择,这篇博客的目的是沉淀出这些参数的语义和用法,供自己以后查阅. 1.java socket参数选项总览       在JDK1.6中有如下参数选项: 1 public final static int TCP_NODELAY = 0x0001; 2 3 public final static int SO_BINDADDR = 0x000F; 4 5 public final static int SO_REUSEADDR = 0x04; 6 7 p

Java socket编程入门

编程 第一章:学习提示 我适合学习这份教程吗 Socket提供了两台计算机之间进行通讯的机制,在James Gosling注意Java语言之前就已经很闻名了.Java语言使你更简单有效的使用Socket开发而无需了解操作系统的底层机制.大部分讨论Java编码的书籍都没有涉及这一主题,或者是只是"点到为止",留给读者大量的想象空间.这份教程就是让你了解在你的Java代码中如何更有效的使用Socket所需的知识.包含以下内容: •什么是Socket •它位于你的应用程序的哪些地方 •能正常

Java socket编程入门[1]

编程 第一章:学习提示 我适合学习这份教程吗 Socket提供了两台计算机之间进行通讯的机制,在James Gosling注意Java语言之前就已经很闻名了.Java语言使你更简单有效的使用Socket开发而无需了解操作系统的底层机制.大部分讨论Java编码的书籍都没有涉及这一主题,或者是只是"点到为止",留给读者大量的想象空间.这份教程就是让你了解在你的Java代码中如何更有效的使用Socket所需的知识.包含以下内容:•什么是Socket•它位于你的应用程序的哪些地方•能正常运行的

用Java Socket开发支持上千个并发的小型服务器(上)

Java Socket 套接字(socket)为两台计算机之间的通信提供了一种机制,在JamesGosling注意到Java语言之前,套接字就早已赫赫有名.该语言只是让您不必了解底层操作系统的细节就能有效地使用套接字. 1客户机/服务器模型 在饭店里,菜单上各种具有异国情调的食品映入你的眼帘,于是你要了一份pizza.几分钟后,你用力咀嚼浇着融化的乳酪和其他你喜欢的配料的热pizza.你不知道,也不想知道:侍者从那里弄来了pizza,在制作过程中加进了什么,以及配料是如何获得的. 上例中包含的实

java socket多线程的时间问题

问题描述 java socket多线程的时间问题 各位大神你们好, 我正在用java写一个接收服务器数据并进行统计的程序, 我用的是多线程socket. 该程序要求能够每秒钟接收几千条数据, 数据中包含value和timestamp以及其他字段. 每个数据通过单独的连接发送, 接收之后统计每秒的钟数据的最大值与平均值(指的是数据中所含有的"value"字段的值), 并将其存储到本地. 但是使用多线程时我有个疑问, 就是怎么判断是否到了1秒. 如果使用本地时钟计算肯定会与服务器有出入,

Java Socket聊天室编程(一)之利用socket实现聊天之消息推送_java

相关阅读:Java Socket聊天室编程(二)之利用socket实现单聊聊天室 网上已经有很多利用socket实现聊天的例子了,但是我看过很多,多多少有一些问题存在. 这里我将实现一个比较完整的聊天例子,并解释其中的逻辑. 由于socket这一块比较大,所以我将分出几篇来写一个比较完整的socket例子. 这里我们先来实现一个最简单的,服务器与客户端通讯,实现消息推送的功能. 目的:服务器与客户端建立连接,客户端可以向服务器发送消息,服务器可以向客户端推送消息. 1,使用java建立socke

Java socket字节流传输示例解析_java

本文为大家分享了Java socket字节流传输示例,供大家参考,具体内容如下 服务端server端:  package com.yuan.socket; import java.io.*; import java.net.ServerSocket; import java.net.Socket; /** * Created by YUAN on 2016-09-17. */ public class TalkServer4Byte { private ServerSocket server;