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.
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
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.
Related
UDP in Java thinks that UDP has "connections". This surprised me, coming from a C background where I had always used UDP as a fire-and-forget type of protocol.
When testing UDP in Java, I noticed that if the remote UDP port is not listening, I get an error in Java before I attempt to send anything.
What does Java do (without me asking it to) in order to be able to tell whether a remote UDP port is listening?
(The code below is run in the receiving thread for the socket. Sending is done in a different thread.)
try {
socket = new DatagramSocket(udpPort);
socket.connect(udpAddr, udpPort);
} catch (SocketException e) {
Log.d(TAG, "disconnected", e);
}
...
while (true) {
// TODO: don't create a new datagram for each iteration
DatagramPacket packet = new DatagramPacket(new byte[BUF_SIZE], BUF_SIZE);
try {
socket.receive(packet); // line 106
} catch (IOException e) {
Log.d(TAG, "couldn't recv", e);
}
...
produces the error below, if the remote socket is not listening.
java.net.PortUnreachableException:
at libcore.io.IoBridge.maybeThrowAfterRecvfrom(IoBridge.java:556)
at libcore.io.IoBridge.recvfrom(IoBridge.java:516)
at java.net.PlainDatagramSocketImpl.doRecv(PlainDatagramSocketImpl.java:161)
at java.net.PlainDatagramSocketImpl.receive(PlainDatagramSocketImpl.java:169)
at java.net.DatagramSocket.receive(DatagramSocket.java:253)
at com.example.mypkg.MyClass.run(MyClass.java:106)
at java.lang.Thread.run(Thread.java:856)
Caused by: libcore.io.ErrnoException: recvfrom failed: ECONNREFUSED (Connection refused)
at libcore.io.Posix.recvfromBytes(Native Method)
at libcore.io.Posix.recvfrom(Posix.java:131)
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164)
...
First of all, it is clear that this is not implemented using real Java. The "libcore.io" packages are not part of the Java SE libraries. These are Android stacktraces. (This doesn't change anything ... but it could.)
OK, so lets start with the exception. The javadoc for java.net.PortUnreachableException says:
"Signals that an ICMP Port Unreachable message has been received on a connected datagram."
And for DatagramSocket.connect(...):
"If the remote destination to which the socket is connected does not exist, or is otherwise unreachable, and if an ICMP destination unreachable packet has been received for that address, then a subsequent call to send or receive may throw a PortUnreachableException. Note, there is no guarantee that the exception will be thrown."
So here's what I think has happened. Prior to creating the incoming socket, something on the client system has sent a UDP packet to the server on that port, and the server has responded with an ICMP Port Unreachable. Then your socket is created, and connected, and you call receive. This does a recvfrom syscall, and network stack responds with an ECONREFUSED error code ... which Java turns into a PortUnreachableException,
So does this mean that UDP is connection oriented?
Not really, IMO. It is simply reporting the that it received an ICMP message in response to something that happened earlier.
What about the connect methods, and the "connected socket" / "connected datagram" phraseology?
IMO, this is just some clumsy wording. The "connection" is really just referring to the fact that the datagram socket has been bound to a specific remote address and port ... so that you can send and receive datagrams without specifying the IP and port1.
These "connections" are pretty tenuous and certainly don't amount to making UDP "connection oriented".
What does Java do (without me asking it to) in order to be able to tell whether a remote UDP port is listening?
It is not doing anything. Java is simply reporting information from a previous ICMP message.
1 - Actually, there is a bit more to it than that. For example, binding tells the client-side OS to buffer UDP packets from that host / port an route UDP packets (and ICMP notifications) to the application. It also tells it not to respond with an ICMP Port Unreachable.
UDP in Java thinks that UDP has "connections".
No it doesn't, but UDP (regardless of Java) does have connected sockets. Not the same thing.
This surprised me, coming from a C background where I had always used UDP as a fire-and-forget type of protocol.
You can connect() a UDP socket in C too. Look it up. What you describe has nothing to do with Java specifically.
When testing UDP in Java, I noticed that if the remote UDP port is not listening, I get an error in Java before I attempt to send anything.
That's because you connected the socket. One of the side-effects of that is that incoming ICMP messages can be routed back to the sending socket in the form of errors.
What does Java do (without me asking it to) in order to be able to tell whether a remote UDP port is listening?
It calls the BSD Sockets connect() method.
The UDP server needs to listen on a local port.
Here's a code stub for a server.
int portNumber = 59123;
DatagramSocket server = new DatagramSocket(portNumber);
// read incoming packets
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
while(true)
{
server.receive(packet);
byte[] data = packet.getData();
String text = new String(data, 0, packet.getLength());
echo(packet.getAddress().getHostAddress() + ":" + packet.getPort() + " received: '" + text + "'");
}
I have tcp communication via socket code like :
public void openConnection() throws Exception
{
socket = new Socket();
InetAddress iNet = InetAddress.getByName("server");
InetSocketAddress sock = new InetSocketAddress(iNet, Integer.parseInt(port));
socket.connect(sock, 0);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
}
and send method as :
synchronized void send(String message)
{
try
{
out.println(message);
}
catch (Exception e)
{
throw new RuntimeException(this.getClass() + ": Error Sending Message: "
+ message, e);
}
}
This writes message on socket and communicated through tcp. (non-blocking call)
My doubt is, how can we determine if this packet was successfully sent or if dropped, what was the reason through java code?
TCP acknowledgement indicates that the data is pushed to the other end of the TCP/IP stack & it necessarily doesn't mean that the receiver application has processed the data. In windows/linux a successful send completion indicates the buffer is copied to the kernel mode socket buffer.
You can try setting the socket buffer to zero which makes the TCP/IP stack to complete the send call only after receiving acknowledgement for the buffer. This happens at least in windows & this behavior can't be assumed in java.
TCP provides reliable communication with the endpoint. If there is no error then the message was received. The wikipedia page on TCP states that:
TCP provides reliable, ordered and error-checked delivery of a stream
of octets between programs running on computers connected to a local
area network, intranet or the public Internet.
https://en.wikipedia.org/wiki/Transmission_Control_Protocol
If the communication fails then you can inspect the specific exception that has been thrown. You should review the API documentation to determine the specific exceptions that are thrown and the reason for them. To help with this it is useful to be more specific with your exception handling (handle specific types of exception separately rather than just catching Exception).
I established a TCP Connection with Java. The Server sends an "ALIVE"-Message to the Client every second to detect a broken connection.
but when I plug of the LAN-cable the IOException is thrown exactly after 23seconds, and not immediately after the send attempt.
My code to send looks like that
// out is an OutputStream
try {
out.write(String.format("%s\r\n", encryptedCommand).getBytes(Charset.forName("UTF-8")));
} catch (IOException e) {
fireConnectionClosed();
}
I'm pretty sure that this was working already, but now it doesn't any more
When a TCP packet fails to get through, the TCP stack resends the packet a few times. This allows the connection to stay up even if an occasional packet is lost. That prevents the problem of connections dropping whenever there's the least bit of congestion on the network. You can test this by unplugging the network for 5 or 10 seconds, then plugging it back in - the connection should be fine.
However, this feature also means that the TCP connection won't be recognized as having been lost until after a few retries.
I have encountered a problem of socket communication on linux system, the communication process is like below: client send a message to ask the server to do a compute task, and wait for the result message from server after the task completes.
But the client would hangs up to wait for the result message if the task costs a long time such as about 40 minutes even though from the server side, the result message has been written to the socket to respond to the client, but it could normally receive the result message if the task costs little time, such as one minute. Additionally, this problem only happens on customer environment, the communication process behaves normally in our testing environment.
I have suspected the cause to this problem is the default timeout value of socket is different between customer environment and testing environment, but the follow values are identical on these two environment, and both Client and server.
getSoTimeout:0
getReceiveBufferSize:43690
getSendBufferSize:8192
getSoLinger:-1
getTrafficClass:0
getKeepAlive:false
getTcpNoDelay:false
the codes on CLient are like:
Message msg = null;
ObjectInputStream in = client.getClient().getInputStream();
//if no message readObject() will hang here
while ( true ) {
try {
Object recObject = in.readObject();
System.out.println("Client received msg.");
msg = (Message)recObject;
return msg;
}catch (Exception e) {
e.printStackTrace();
return null;
}
}
the codes on server are like,
ObjectOutputStream socketOutStream = getSocketOutputStream();
try {
MessageJobComplete msgJobComplete = new MessageJobComplete(reportFile, outputFile );
socketOutStream.writeObject(msgJobComplete);
}catch(Exception e) {
e.printStackTrace();
}
in order to solve this problem, i have added the flush and reset method, but the problem still exists:
ObjectOutputStream socketOutStream = getSocketOutputStream();
try {
MessageJobComplete msgJobComplete = new MessageJobComplete(reportFile, outputFile );
socketOutStream.flush();
logger.debug("AbstractJob#reply to the socket");
socketOutStream.writeObject(msgJobComplete);
socketOutStream.reset();
socketOutStream.flush();
logger.debug("AbstractJob#after Flush Reply");
}catch(Exception e) {
e.printStackTrace();
logger.error("Exception when sending MessageJobComplete."+e.getMessage());
}
so do anyone knows what the next steps i should do to solve this problem.
I guess the cause is the environment setting, but I do not know what the environment factors would affect the socket communication?
And the socket using the Tcp/Ip protocal to communicate, the problem is related with the long time task, so what values about tcp would affect the timeout of socket communication?
After my analysis about the logs, i found after the message are written to the socket, there were no exceptions are thrown/caught. But always after 15 minutes, there are exceptions in the objectInputStream.readObject() codes snippet of Server Side which is used to accept the request from client. However, socket.getSoTimeout value is 0, so it is very strange that the a Timed out Exception was thrown.
{2012-01-09 17:44:13,908} ERROR java.net.SocketException: Connection timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:146)
at sun.security.ssl.InputRecord.readFully(InputRecord.java:312)
at sun.security.ssl.InputRecord.read(InputRecord.java:350)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:809)
at sun.security.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:766)
at sun.security.ssl.AppInputStream.read(AppInputStream.java:94)
at sun.security.ssl.AppInputStream.read(AppInputStream.java:69)
at java.io.ObjectInputStream$PeekInputStream.peek(ObjectInputStream.java:2265)
at java.io.ObjectInputStream$BlockDataInputStream.peek(ObjectInputStream.java:2558)
at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2568)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1314)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:368)
so why the Connection Timed out exceptions are thrown?
This problem is solved. using the tcpdump to capture the messages flows. I have found that while in the application level, ObjectOutputStream.writeObject() method was invoked, in the tcp level, many times [TCP ReTransmission] were found.
So, I concluded that the connection is possibly be dead, although using the netstat -an command the tcp connection state still was ESTABLISHED.
So I wrote a testing application to periodically sent Testing messages as the heart-beating messages from the Server. Then this problem disappeared.
The read() methods of java.io.InputStream are blocking calls., which means they wait "forever" if they are called when there is no data in the stream to read.
This is completely expected behaviour and as per the published contract in javadoc if the server does not respond.
If you want a non-blocking read, use the java.nio.* classes.
What's the most appropriate way to detect if a socket has been dropped or not? Or whether a packet did actually get sent?
I have a library for sending Apple Push Notifications to iPhones through the Apple gatways (available on GitHub). Clients need to open a socket and send a binary representation of each message; but unfortunately Apple doesn't return any acknowledgement whatsoever. The connection can be reused to send multiple messages as well. I'm using the simple Java Socket connections. The relevant code is:
Socket socket = socket(); // returns an reused open socket, or a new one
socket.getOutputStream().write(m.marshall());
socket.getOutputStream().flush();
logger.debug("Message \"{}\" sent", m);
In some cases, if a connection is dropped while a message is sent or right before; Socket.getOutputStream().write() finishes successfully though. I expect it's due to the TCP window isn't exhausted yet.
Is there a way that I can tell for sure whether a packet actually got in the network or not? I experimented with the following two solutions:
Insert an additional socket.getInputStream().read() operation with a 250ms timeout. This forces a read operation that fails when the connection was dropped, but hangs otherwise for 250ms.
set the TCP sending buffer size (e.g. Socket.setSendBufferSize()) to the message binary size.
Both of the methods work, but they significantly degrade the quality of the service; throughput goes from a 100 messages/second to about 10 messages/second at most.
Any suggestions?
UPDATE:
Challenged by multiple answers questioning the possibility of the described. I constructed "unit" tests of the behavior I'm describing. Check out the unit cases at Gist 273786.
Both unit tests have two threads, a server and a client. The server closes while the client is sending data without an IOException thrown anyway. Here is the main method:
public static void main(String[] args) throws Throwable {
final int PORT = 8005;
final int FIRST_BUF_SIZE = 5;
final Throwable[] errors = new Throwable[1];
final Semaphore serverClosing = new Semaphore(0);
final Semaphore messageFlushed = new Semaphore(0);
class ServerThread extends Thread {
public void run() {
try {
ServerSocket ssocket = new ServerSocket(PORT);
Socket socket = ssocket.accept();
InputStream s = socket.getInputStream();
s.read(new byte[FIRST_BUF_SIZE]);
messageFlushed.acquire();
socket.close();
ssocket.close();
System.out.println("Closed socket");
serverClosing.release();
} catch (Throwable e) {
errors[0] = e;
}
}
}
class ClientThread extends Thread {
public void run() {
try {
Socket socket = new Socket("localhost", PORT);
OutputStream st = socket.getOutputStream();
st.write(new byte[FIRST_BUF_SIZE]);
st.flush();
messageFlushed.release();
serverClosing.acquire(1);
System.out.println("writing new packets");
// sending more packets while server already
// closed connection
st.write(32);
st.flush();
st.close();
System.out.println("Sent");
} catch (Throwable e) {
errors[0] = e;
}
}
}
Thread thread1 = new ServerThread();
Thread thread2 = new ClientThread();
thread1.start();
thread2.start();
thread1.join();
thread2.join();
if (errors[0] != null)
throw errors[0];
System.out.println("Run without any errors");
}
[Incidentally, I also have a concurrency testing library, that makes the setup a bit better and clearer. Checkout the sample at gist as well].
When run I get the following output:
Closed socket
writing new packets
Finished writing
Run without any errors
This not be of much help to you, but technically both of your proposed solutions are incorrect. OutputStream.flush() and whatever else API calls you can think of are not going to do what you need.
The only portable and reliable way to determine if a packet has been received by the peer is to wait for a confirmation from the peer. This confirmation can either be an actual response, or a graceful socket shutdown. End of story - there really is no other way, and this not Java specific - it is fundamental network programming.
If this is not a persistent connection - that is, if you just send something and then close the connection - the way you do it is you catch all IOExceptions (any of them indicate an error) and you perform a graceful socket shutdown:
1. socket.shutdownOutput();
2. wait for inputStream.read() to return -1, indicating the peer has also shutdown its socket
After much trouble with dropped connections, I moved my code to use the enhanced format, which pretty much means you change your package to look like this:
This way Apple will not drop a connection if an error happens, but will write a feedback code to the socket.
If you're sending information using the TCP/IP protocol to apple you have to be receiving acknowledgements. However you stated:
Apple doesn't return any
acknowledgement whatsoever
What do you mean by this? TCP/IP guarantees delivery therefore receiver MUST acknowledge receipt. It does not guarantee when the delivery will take place, however.
If you send notification to Apple and you break your connection before receiving the ACK there is no way to tell whether you were successful or not so you simply must send it again. If pushing the same information twice is a problem or not handled properly by the device then there is a problem. The solution is to fix the device handling of the duplicate push notification: there's nothing you can do on the pushing side.
#Comment Clarification/Question
Ok. The first part of what you understand is your answer to the second part. Only the packets that have received ACKS have been sent and received properly. I'm sure we could think of some very complicated scheme of keeping track of each individual packet ourselves, but TCP is suppose to abstract this layer away and handle it for you. On your end you simply have to deal with the multitude of failures that could occur (in Java if any of these occur an exception is raised). If there is no exception the data you just tried to send is sent guaranteed by the TCP/IP protocol.
Is there a situation where data is seemingly "sent" but not guaranteed to be received where no exception is raised? The answer should be no.
#Examples
Nice examples, this clarifies things quite a bit. I would have thought an error would be thrown. In the example posted an error is thrown on the second write, but not the first. This is interesting behavior... and I wasn't able to find much information explaining why it behaves like this. It does however explain why we must develop our own application level protocols to verify delivery.
Looks like you are correct that without a protocol for confirmation their is no guarantee the Apple device will receive the notification. Apple also only queue's the last message. Looking a little bit at the service I was able to determine this service is more for convenience for the customer, but cannot be used to guarantee service and must be combined with other methods. I read this from the following source.
http://blog.boxedice.com/2009/07/10/how-to-build-an-apple-push-notification-provider-server-tutorial/
Seems like the answer is no on whether or not you can tell for sure. You may be able to use a packet sniffer like Wireshark to tell if it was sent, but this still won't guarantee it was received and sent to the device due to the nature of the service.