Reputation: 2365
I have a client program that sends messages typed in console to the server. Following some advices, I introduced a check for a closed socket with Socket.checkError()
. Nevertheless, for some reason it indicates error only after second failed attempt to send a message.
My code:
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
while (true)
try (
Socket clientSocket = new Socket(hostname, port);
PrintWriter socketOut = new PrintWriter(clientSocket.getOutputStream(), true);
) {
String input;
while ((input=stdIn.readLine())!=null) {
socketOut.println(input);
if (socketOut.checkError()) {
System.out.println("Got socket error");
break;
}
}
} catch (IOException e) {
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {}
}
I shut down (manually) my server side after receiving 'message1'. Therefore, I expect to get the error while trying to send the very next message. Nevertheless it occurs only one message after:
message1
message2
message3
Got socket error
Can anyone explain this behavior and advise me a method to get notification right on the first attempt to send a message in void?
Upvotes: 2
Views: 1799
Reputation: 2365
From the answer of @Davide Lorenzo MARINO I got the idea of employing read()
. The only problem that it is blocking. However, one can always run it in another thread, which would modify a class global variable, when read()
finally returns -1:
static boolean socketIsAlive;
...
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
while (true)
try (
Socket clientSocket = new Socket(hostname, port);
PrintWriter socketOut = new PrintWriter(clientSocket.getOutputStream(), true);
) {
socketIsAlive=true;
new ConnectionChecker(clientSocket).start();
String input;
while (true) {
if ((input=stdIn.readLine())!=null)
socketOut.println(input);
if (!socketIsAlive) {
System.out.println("Got socket error");
break;
}
}
} catch (IOException e) {
try {
Thread.sleep(5000);
} catch (InterruptedException e1) {}
}
}
...
static public class ConnectionChecker extends Thread{
Socket socket;
public ConnectionChecker(Socket socket) {
this.socket=socket;
}
@Override
public void run() {
try {
if (socket.getInputStream().read()==-1)
socketIsAlive=false;
} catch (IOException e) {}
}
}
Upvotes: -1
Reputation: 26926
In ths java library there is no method to check if connection is opened or not. Method like isConnected()
and isClosed()
check only one side of the connection (where you invoked the method).
From javadoc:
Note: Closing a socket doesn't clear its connection state, which means this method will return true for a closed socket (see isClosed()) if it was successfuly connected prior to being closed.
To check if the connection has been really closed simply invoke the read()
method (or equivalent) and check if it returns -1.
Note: also if isConnected
will work as you like (giving false if the other side of the socket closed the connection or if there is a network problem or similar) the sequence:
if (socket.isConnected()) {
int x = socked.read();
}
will not grant that the x has a value different from -1 or throws an IOException, because the connection could be closed after the isConnected test and before the read operation.
The following code to show how any kind of check on the socket cannot guarantee that a subsequent read will give a valid result.
// Return true because socket communication is enabled
if (myFunctionToCheckIfSocketIsOpen(socket)) {
// Here the peer closed the socket or the network shutdown
// This read will give -1 or throws IOException also if the previous check returned true
int x = socket.read();
}
Upvotes: 1
Reputation: 310913
Following some advices, I introduced a check for a closed socket with
Socket.checkError().
There is no such method. Clearly you are referring to PrintWriter.checkError().
Nevertheless, for some reason it indicates error only after second failed attempt to send a message.
The reason is that there is both a socket send buffer at the sender and a socket receive buffer at the receiver, and that sending is asynchronous: it therefore isn't possible for the first send to detect an error.
Can anyone explain this behavior and advise me a method to get notification right on the first attempt to send a message in void?
There isn't one. That's the nature of TCP. What you are attempting indicates an application protocol error, and the answer lies in the realm of the application protocol as well: don't have the peer close the socket while this end could still be sending data, OR don't allow this end to send data after the peer has indicated, via the application protocol, that it won't be reading any more data.
Don't use PrintWriter
over the network. It suppresses the actual exception. Use BufferedWriter
and the write()
and newLine()
methods.
Upvotes: 1