I am having a lot of trouble in understanding the differences about how Java handles Sockets on Windows and Linux - Particularly when one of the sides (Client or Server) closes the connection abruptly.
I have written the following very simple Server and Client classes to keep my point as simple, objective and as easy for you to understand 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 a code for properly shutting down the client or the server, but the objective, as I said, is to simulate an abrupt shutdown on either side, where no "disconnection code" could be sent or received. That's why I created these 2 very simple classes.
On Linux, it runs pretty 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, however:
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 try to ignore this Exception by modifying the following lines on my SimpleClient.java:
// Send:
try {
out.write(msg);
out.newLine();
out.flush();
}
catch(Exception e) {}
Another Exception is thrown:
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 on the code will be the ones pointed out on these Exceptions, but the first one is thrown on out.flush() and the second one on in.read().
So basically, as you can see on Linux, even after abruptly closing the server:
1. It doesn't throw an Exception when I try to send the data.
2. And more importantly, when I try to receive it, the first char is "-1" and received correctly.
On Windows, it throws Exceptions both when sending and more importantly on receiving - when calling the read() method - I cannot get the "end of the stream" (-1) code.
Which leads to some questions:
1. Why is there such a big difference on Windows x Linux? Why on Linux these Exceptions are not thrown while on Windows they are?
2. Shouldn't Java, with all its cross-platform qualities, try to minimize the differences on running in both the Systems? (by the way I'm using JDK 7 on both)
3. Is there a way to change the code for an abrupt shutdown and get it to work more "Linux-like" on Windows, without throwing all these Exceptions and getting the -1 on my in.read()??
4. If not, any external API recommended?
I've tried to search the web for hours on this specific topic but without success.
I have also tried many solutions like calling methods like isConnected(), isBound(), isClosed(), etc. in the client_socket on the client side without success. They always say that there is an active connection and no problem with it, even after shutting down the server.
Hopefully someone would take the time to answer at least one of these questions.
You have my most sincere thanks in advance for any answers.