Differences in Java sockets between Windows and Linux - how to handle them?

I am having trouble understanding the differences in how Java handles sockets on Windows and Linux - especially when one of the parties (client or server) closes the connection suddenly.

I wrote the following very simple server and client classes so that my meaning is simple, objective, and easy for you to be as clear as possible:

SimpleClient.java:

import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.Socket; public class SimpleClient { public static void main(String args[]) { try { Socket client_socket = new Socket("127.0.0.1", 9009); // Used to read from a terminal input: BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); // Used for client/server communication: BufferedReader in = new BufferedReader(new InputStreamReader(client_socket.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client_socket.getOutputStream())); while(true) { System.out.print("Command: "); String msg = br.readLine(); // Send: out.write(msg); out.newLine(); out.flush(); // Receive: int ifirst_char; char first_char; if((ifirst_char = in.read()) == -1) { // Server Closed System.out.println("Server was closed on the other side."); break; } first_char = (char) ifirst_char; msg = String.valueOf(first_char); msg += in.readLine(); // Shows the message received from the server on the screen: System.out.println(msg); } } catch(Exception e) { e.printStackTrace(); } } } 


SimpleServer.java:

 import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; public class SimpleServer { public static void main(String args[]) { try { ServerSocket server_socket = new ServerSocket(9009); Socket client_socket = server_socket.accept(); while(true) { BufferedReader in = new BufferedReader(new InputStreamReader(client_socket.getInputStream())); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client_socket.getOutputStream())); // Receive: int ifirst_char; char first_char; if((ifirst_char = in.read()) == -1) { // Client Closed System.out.println("Client was closed on the other side."); break; } first_char = (char) ifirst_char; String msg = msg = String.valueOf(first_char); msg += in.readLine(); msg = "Server Received: " + msg; // Send: out.write(msg); out.newLine(); out.flush(); } } catch(Exception e) { e.printStackTrace(); } } } 


Of course, I could implement the code to properly close the client or server, but the goal, as I said, is to simulate a sharp shutdown on both sides, where the “disconnect code” cannot be sent or received. That is why I created these 2 very simple classes.

On Linux, this works very well:

 $ java SimpleClient Command: echo Server Received: echo Command: test Server Received: test Command: (server now was closed on the other side) Server was closed on the other side. $ 


On Windows:

 C:\simplesocket>java SimpleClient Command: echo Server Received: echo Command: test Server Received: test Command: (server now was closed on the other side) java.net.SocketException: Connection reset by peer: socket write error at java.net.SocketOutputStream.socketWrite0(Native Method) at java.net.SocketOutputStream.socketWrite(Unknown Source) at java.net.SocketOutputStream.write(Unknown Source) at sun.nio.cs.StreamEncoder.writeBytes(Unknown Source) at sun.nio.cs.StreamEncoder.implFlushBuffer(Unknown Source) at sun.nio.cs.StreamEncoder.implFlush(Unknown Source) at sun.nio.cs.StreamEncoder.flush(Unknown Source) at java.io.OutputStreamWriter.flush(Unknown Source) at java.io.BufferedWriter.flush(Unknown Source) at SimpleClient.main(SimpleClient.java:32) 


Let's say I'm trying to ignore this exception by changing the following lines on my SimpleClient.java:

 // Send: try { out.write(msg); out.newLine(); out.flush(); } catch(Exception e) {} 


Another exception:

 C:\simplesocket>java SimpleClient Command: echo Server Received: echo Command: test Server Received: test Command: (server now was closed on the other side) java.net.SocketException: Connection reset at java.net.SocketInputStream.read(Unknown Source) at java.net.SocketInputStream.read(Unknown Source) at sun.nio.cs.StreamDecoder.readBytes(Unknown Source) at sun.nio.cs.StreamDecoder.implRead(Unknown Source) at sun.nio.cs.StreamDecoder.read(Unknown Source) at java.io.InputStreamReader.read(Unknown Source) at java.io.BufferedReader.fill(Unknown Source) at java.io.BufferedReader.read(Unknown Source) at SimpleClient.main(SimpleClient.java:42) 


I don’t know if the corresponding lines of code will contain the ones indicated in these exceptions, but the first one is selected on out.flush () , and the second one on .read () .

So basically, as you can see on Linux, even after the server shuts down suddenly:

1. It does not throw an exception when I try to send data.
2. And more importantly, when I try to get it, the first char is "-1" and received correctly.

On Windows, it throws exceptions both when sending, and, more importantly, when receiving - when the read () method is called - I cannot get the code "end of stream" (-1).

This leads to some questions:

1. Why is there such a big difference in Windows x Linux? Why are these Exceptions not thrown on Linux when they are on Windows?

2 .. Should Java, with all its cross-platform qualities, try to minimize differences when working on both systems? (by the way, I'm using JDK 7 on both)

3. Is there a way to change the code for a sudden shutdown and make it work more "Linux-like" on Windows without throwing all these Exceptions and getting -1 on my in.read () ??

4. If not, is it recommended to use any external API?


I tried searching the Internet for hours on this particular topic, but to no avail.

I have also tried many solutions like calling methods isConnected () , isBound () , isClosed () , etc. In client_socket, on the client side without success. They always say that there is an active connection and no problems with it, even after closing the server.

I hope someone finds the time to answer at least one of these questions.

For any answers you thank me in advance.

+7
java sockets
source share
1 answer

Your code does not close, so I assume that you actually mean that one end process is stopped, aka killed.

Unix socket sd are “just” fd and when the Unix process ends without closing fd, including the case when the JVM stops and you did not call close (or shutdown-WR), fd is closed by the operating system, which is for a TCP socket (at least tries) normal aka graceful close: FIN / ACK exchange with FIN-WAIT and TIME-WAIT. The only way I know to make a Unix socket is to make graceless close at the TCP (RST) level is to set it to linger to 0 before closing (either explicitly or by exiting). It is also possible and often for an intermediate block to force disconnect your RST connection; For example, I saw firewalls that RST you after 15 minutes of inactivity. I also more rarely saw middle fields that fake FIN, or that try, but do it wrong.

Windows sockets (WinSock) are different APIs than files. If the Windows process terminates without calling closesocket (similar but separate from closing) or at least shutdown-WR, Winsock does RST. To get a graceful shutdown (FIN) on Windows, you (via the JVM) must name one of them. The JVM can presumably track java.net.Sockets (but not in the JNI) and do it for you on the JVM output, but it is not; You can request a raise. Even this may not work if you killed it from the outside using TaskMgr or similar, and may not work correctly if you push a JVM error: the JVM tries to catch the errors and give minidump, which will be the place to try for cleaning sockets, but if there is a JVM error , it may fail again - and IME most JVM errors are caused by JVM errors.

If this is enough to handle code errors (leaks) and signals, but not JVM errors and crashes, you can simply subclass Socket so that if forces (graceful) are closed on .finalize and on output using Runtime.addShutdownHook and use it instead.

On Unix or Windows sockets, the resulting FIN is treated as the end of the file, like any other file, such as a file on disk. The resulting RST is returned as a [WSA] ECONNRESET error for the JVM, which throws an exception. It would be nice to hide this difference, because for applications other than yours, it can be significant - it is enough that some protocols have to be changed to prevent fake-FIN from being a security vulnerability, in particular SSLv2 and HTTP / 0.9.

If you are also considering cases of a peer-to-peer system failure (not just the JVM) or certain parts of the network, or your network interface is failing, the exceptions you might get are a bit more varied. IMHO do not try to cope with them, just let me know what you see, and let system administrators and non-administrators understand. I saw cases when a programmer received Exception X due to a P problem in laboratory conditions and encoded for it, but in the real world, an exception X occurred for very different reasons and the “useful” processing actually made it difficult to solve the problem.

Aseda: the server must create a BufferedReader before it is inside the while (true) do-a-line loop; if you ever get / want a client that sends more than one row at a time, the code shown will lose data. You do not need this hair if first_char == - 1 else convert to String; just use in.readLine, it returns null exactly the same case when the initial in.read returns -1, which for (TCP) Socket is when FIN is received. Conversely, the readLine client from System.in should be checked; if someone dials ^ Z or ^ D or that you get NPE.

+6
source share

All Articles