2011-10-20 78 views
2

我目前正在调试两个通过TCP连接交换数据的Java应用程序。发送紧急数据后TCP连接重置

TCP客户端之一通过调用Socket#sendUrgentData(int)定期将紧急数据发送给另一个TCP服务器。本月18日试图发送紧急数据,TCP客户机抛出以下异常

java.io.IOException:BrokenPipe 
    at java.net.PlainSocketImpl.socketSendUrgentData(Native Method) 
    at java.net.PlainSocketImpl.sendUrgentData(PlainSocketImpl.java:541) 
    at java.net.Socket.sendUrgentData(Socket.java:927) 

的TCP服务器抛出该异常

java.net.SocketException: Software caused connection abort: recv failed 
    at java.net.SocketInputStream.socketRead0(Native Method) 
    at java.net.SocketInputStream.read(Unknown Source) 
    at java.net.SocketInputStream.read(Unknown Source) 

我相信例外是通过尝试写入引起/读到一个封闭的连接/插座。我不明白为什么连接或套接字在调用sendUrgentData()17次后关闭。我可以重复它,它总是在17次后发生。

如果我在Windows上运行客户端和服务器,则会出现问题。如果我在Solaris上运行客户端和服务器,则不会发生此问题。如果我在Solaris上运行客户端并在Windows上运行服务器,则会出现问题。如果我在Windows上运行客户机并在Solaris上运行服务器,则不会发生此问题。这让我觉得它可能与Windows有关?

使用Wireshark的我看到

--> = from TCP client to TCP server 
<-- = from TCP server to TCP client 

--> [PSH, ACK, URG] (Seq=1, Ack=1) 
<-- [ACK] (Seq=1, Ack=2) 
--> [PSH, ACK, URG] (Seq=2, Ack=1) 
<-- [ACK] (Seq=1, Ack=3) 
... 
--> [PSH, ACK, URG] (Seq=17, Ack=1) 
<-- [RST, ACK] (Seq=1, Ack=18) 

我写了一些简单的测试类,这表明这一问题的连接以下网络通信。

TCPServer.java IP_ADDRESS端口

public class TCPServer 
{ 
    public static void main(String[] args) throws Exception 
    { 
     ServerSocket socket = new ServerSocket(); 
     socket.bind(new InetSocketAddress(args[0], Integer.parseInt(args[1]))); 
     System.out.println("BOUND/" + socket); 
     Socket connection = socket.accept(); 
     System.out.println("CONNECTED/" + connection); 
     int b; 
     while ((b = connection.getInputStream().read()) != -1) { 
      System.out.println("READ byte: " + b); 
     } 
     System.out.println("CLOSING .."); 
     connection.close(); 
     socket.close(); 
} 
} 

TCPClient.java IP_ADDRESS端口Interval_Between_Urgent_Data

public class TCPClient 
{ 
    public static void main(String[] args) throws Exception 
    { 
     final Socket socket = new Socket(); 
     socket.connect(new InetSocketAddress(InetAddress.getByName(args[0]), Integer.parseInt(args[1]))); 
     System.out.println("CONNECTED/"+socket); 
     Timer urgentDataTimer = new Timer(true); 
     urgentDataTimer.scheduleAtFixedRate(new TimerTask() 
     {  
      int n = 0; 
      public void run() { 
       try { 
        System.out.println("SENDING URGENT DATA ("+(++n)+") .."); 
        socket.sendUrgentData(1); 
        System.out.println("SENT URGENT DATA"); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }, 1000, Integer.parseInt(args[2])); 

     int b; 
     while ((b = socket.getInputStream().read()) != 1) { 
      System.out.println("READ byte: " + b); 
     } 
     System.out.println("CLOSING .."); 
     urgentDataTimer.cancel(); 
     socket.close(); 
    } 
} 

有人能解释这里发生了什么?

谢谢。

回答

0

紧急数据由Java在线接收,这会导致数据流出现乱序。接收者可能不了解无序数据并关闭连接。然后你一直写信给它,并且可能会导致'通过对等方重置连接'。道德是你基本上不能使用Java中的紧急TCP数据,除非接收器是非常仔细的编写的。

1

我假设你实际上正确地接收到失败的应用程序中的紧急数据,并且数据如你所期望的那样?

这有很多原因导致失败,特别是如果你在跨平台的情况下尝试它:在TCP中有两个冲突的数据工作原理的描述,RFC 793其中详细说明TCP表示紧急指针指示紧跟在紧急数据后面的字节,但是RFC 1122纠正了这一点,并指出紧急指针指示紧急数据的最终字节。如果一个对等方使用RFC 793定义,而另一个使用RFC 1122定义,则会导致互操作性问题。

因此,首先确认您的应用程序实际上是获取正确的紧急数据字节。是的,我说的字节,有更多的兼容性复杂性,因为Windows只支持单个字节的带外数据,而RFC 1122指定TCP必须支持任意长度的紧急数据字节序列。Windows也没有指定如何或者是否会缓冲后续的带外数据,所以如果读取一个紧急数据字节的速度很慢,另一个紧急数据字节到达,那么其中一个字节可能会丢失;尽管我们的测试显示Windows确实缓冲了紧急数据。这一切都使得使用带有TCP的Windows上的紧急数据在带有某些不可靠的带外信令上的使用成为可能。

然后,如果碰巧使用重叠I/O,还有其他所有问题。

我在一个小更深入覆盖这一点,尽管是从一个C++的角度来看,在这里:http://www.serverframework.com/asynchronousevents/2011/10/out-of-band-data-and-overlapped-io.html

+0

这是在Java中所有内联。 – EJP

+0

感谢您的答案。由于[setOOBInline()](http://download.oracle.com/javase/6/docs/api/java/net/Socket.html#setOOBInline%28boolean%29))没有收到紧急数据设置(false)。紧急数据仅发送以防止在一段时间不活动后断开连接。设置[setKeepAlive](http://download.oracle.com/javase/6/docs/api/java/net/Socket.html#setKeepAlive%28boolean%29))将是一个更好的解决方案。 如果我设置了setOOBInline()(true),则不会发生断开连接并收到正确的字节。 – Stirls

+0

为什么你使用紧急数据呢?只需发送应用程序级别的ping消息。紧急数据只有在您有大量排队等待处理(或发送)的数据时才有用,并且您希望立即通知另一端,而不是在处理完所有数据之后通知另一端。为了保持活力,情况并非如此;根据定义,您没有任何待处理的数据,并且可以正常发送应用程序级别的消息。由于RFC不匹配和平台问题,恕我直言,给出了紧急数据的问题,我很好地解决了这个问题。 –