Reputation: 97
I am currently debugging two Java applications that exchange data via a TCP connection.
One of the applications, the TCP client, periodically sends urgent data to the other, the TCP server, by calling Socket#sendUrgentData(int). On the 18th attempt to send the urgent data, the TCP client throws the following exception
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)
The TCP server throws this exception
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)
I believe the exceptions are caused by an attempt to write/read to a closed connection/socket. What I don't understand is why the connection or socket becomes closed after calling sendUrgentData() 17 times. I am able to repeat it and it occurs always after 17 times.
If I run the client and server on Windows, the issue occurs. If I run the client and server on Solaris the issue does not occur. If I run the client on Solaris and the server on Windows the issue occurs. If I run the client on Windows and the server on Solaris the issue does not occur. This makes me think it may be Windows related?
Using Wireshark I see the following traffic on the connection
--> = 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)
I wrote some simple test classes which show the issue.
TCPServer.java IP_Address Port
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 Port 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();
}
}
Could someone explain what is happening here?
Thanks.
Upvotes: 2
Views: 4550
Reputation: 21616
I assume that you are actually correctly receiving the urgent data in the application that's failing and that the data is as you expect it to be?
There are many reasons for this to fail, especially if you're attempting it in a cross platform situation: In TCP there are two conflicting descriptions of how urgent data works, RFC 793 which details TCP says that the Urgent Pointer indicates the byte that follows the urgent data but RFC 1122 corrects this and states that the Urgent Pointer indicates the final byte of urgent data. This leads to interoperability issues if one peer uses the RFC 793 definition and the other uses the RFC 1122 definition.
So, first confirm that your application is actually getting the correct byte of urgent data. Yes, I said byte, there's more compatibility complexity in that Windows only supports a single byte of out of band data whereas RFC 1122 specifies that TCP MUST support sequences of urgent data bytes of any length. Windows also doesn't specify how or if it will buffer subsequent out of band data, so if you are slow in reading a byte of urgent data and another byte of urgent data arrives then one of the bytes may be lost; though our tests have shown that Windows does buffer urgent data. This all makes the use of out of band signalling using urgent data somewhat unreliable on Windows with TCP.
And then there are all the other issues that come about if you happen to be using overlapped I/O.
I've covered this in a little more depth, albeit from a C++ perspective, here: http://www.serverframework.com/asynchronousevents/2011/10/out-of-band-data-and-overlapped-io.html
Upvotes: 1
Reputation: 310884
Urgent data is received in-line by Java, which would put the data stream out of order. Probably the receiver didn't understand the out-of-order data and closed the connection. Then you kept writing to it, and that can cause 'connection reset by peer'. Moral is that you basically can't use urgent TCP data in Java unless the receiver is very carefully written.
Upvotes: 0