Chat server writing to closed sockets - java

Essentially I have a server class and a client class, the client creates a socket and sends whatever you type into the server, which gets written to the output streams of a vector of sockets from all the existing clients. It works well except when you close a chat client, after which the next message sent gives the following exception:
java.net.SocketException: Software caused connection abort: socket write error
I think what's happening is that the socket closed by the client is either not closing the socket in the server's vector of sockets, or that even when it's closed, it remains in the vector and then tries to write to a closed socket. Does this sound like what might be happening? I don't understand exactly what the socket.close() method does regarding the socket it's connected to.

You close the socket on the client side, but on the server side it is not closed and this is why you get this exception.
In a graceful close you should send a CLOSE message from your client which will close the socket on the server side.
If a socket on the client was closed then you must handle your exception on the server side e.g. by removing it from your vector of sockets.

If the client closes his socket you will read EOS at the server (read() returns -1, readLine() returns null,readXXX() throws EOFException for any other X), or get an IOException: connection reset by peer when writing, probably not on the first write. If either of these things happens you must close the socket in the server and forget about that client in all ways.

I don't think it's like both sides of connection are holding together by hands, and if you split them they always will feel it. Check if socket is closed before sending and catch exceptions to solve this problem.

Related

Java: How to properly close a socket connection using ServerSocket and Socket

I currently have a simple instant messaging program which is utilizing Java's Socket and ServerSocket classes. It is functioning as intended but when I attempt to close the connection it is not using the 4 way handshake TCP teardown to close the connection. Instead it is closing the connection abruptly with an RST packet.
The way in which I am closing the connection is sending a string from the client to the server which the server will recognize as the command to close the connection. I then use the ServerSocket.close() method on the server and the Socket.close() method on the client.
What is the correct way and/or order of events to properly close a TCP connection utilizing these classes?
Client side disconnect code:
//Disconnects from remote server
//Returns true on success, false on failure
public boolean disconnect(){
try{
this.clientOut.println("0x000000");
this.clientRemoteSocket.close();
this.isConnected = false;
return true;
}catch(Exception e){
return false;
}
}
Server side disconnect code:
//Check to see if the client wants to close the connection
//If yes, then close the connection and break out of the while loop
if(incoming.equals("0x000000")){
serverLocalSocket.close();
break;
}
EDIT:
The code works perfectly fine. I'm just trying to learn socket programming in Java and know that a proper TCP teardown process is to include a 4 way handshake. A FIN packet to the remote host, then an ACK packet from the remote host back. Then a FIN packet from the remote host, then an ACK packet to the remote host. When monitoring the traffic via Wireshark I am not getting that. Instead I am getting a FIN to the remote server, then a RST/ACK back from the server.
This image depicts a proper TCP 4 way teardown process.
So far everything I've found suggest that all one needs is a call to close() or to just let Java's Try-with-resources statement handle the clean up. I can't see Java implementing functionality which does not comply with the standard TCP specifications though. It is very possible I may be calling certain lines in an incorrect order or something of the sort, I'm just unaware of it.
If you are resetting your own connection on close, either:
You haven't read all the pending incoming data that was sent by the peer, or
You had already written to the connection which had previously already been closed by the peer.
In both cases, an application protocol error.
The great part about TCP is if you close your socket, your partner will automatically know and throw an error on reading.
So all you have to do in the client is:
clientRemoteSocket.close();
And with the server, just add an error case to your normal reading of data:
try {
// Read from the socket:
incoming = socketInputStream.read();
// Handle the data here
} catch (IOException e) {
// Client has disconnected
}
There might be a more specfic exception you can catch, I'm not sure, it's been a while. But that should work. Good luck!

How to detect network disconnection from client side(JAVA) [duplicate]

This question already has answers here:
Detecting TCP Client Disconnect
(9 answers)
Does a TCP socket connection have a "keep alive"?
(8 answers)
Closed 7 years ago.
I have written java client server socket connection code. In that the server accepts the client connection, then the server sends the data using PrintStream , and client reads the data in readline(). After this, the client will wait for the server in readline. If there is a network disconnection, the client keeps waiting in the readline and never exits from readline. Is there is a way to detect the disconnection from client side itself? Or whether I have to write new heartbeat thread to check network disconnection?
Turn on socket option SO_KEEPALIVE on the socket. This will enable a heartbeat feature built into the TCP protocol.
Here's a simple way to turn this on.
Socket socket = ...; // Your socket on the client side
socket.setKeepAlive(true);
If the other side of the connection stops responding to the heartbeats, you will get an IOException on your call to read from the socket.
However, the timeout for this is 2 hours. This may be too long for your needs (you didn't specify)
Disconnection from the client side:
clientSocket.isConnected(): this is wrong, this will not return true and will keep returning false until the server has also disconnected
You can try to send data through an output stream and you will get an exception, specifically IOException (Broken Pipe)
You can also use socket.keepAlive(true) this will use the heartbeat feature built into the socket and you will get an exception because of it.
Cheers.

Detect a server side FIN packet

I'm got the following Java code:
Socket s = new Socket();
s.connect(mySockAddr, myTimeout);
Assuming I don't use the socket, I need to detect a server side connection close (FIN or RST packet) as soon as it happens.
For instance, though a thread which checks socket status, or intercepting the FIN/RST packets...
How can I detect it?
I've tried with printWriter.checkError(), socket.isConnected(), socket.isClosed() methods but nothing works.
The only way i know of to detect whether the other side has closed the connection is by attempting to read from the input stream. A read from a shut-down socket will return -1. That's your notification that there won't be any more to read.
As far as the other functions go, s.isConnected() tells you whether you've successfully connect()ed the socket, and s.isClosed() would tell you whether you closed it. It tells you nothing about what the other side has done.

Something to know about socket and multithreading in java

I want to send a message to a socket on a gateway(client), and then wait for a message to recieve from a gateway.
While waiting, on other hand I again send a message from server socket to gateway which is client but on accept()(ServerSocket) method my connection blocks.
I was wondering is this because of the previous request which was waiting to recieve something by using inputStream. I used threads to maintain this and use setReuseAddress() to do this.
I am not posting a code right now as I just want to know is this logic is fine or is there any logic to handle multiple requests(messages) to be send from a Server-Socket to a client-Socket and I also dont want to close the socket because the socket has to be connected through out the application.
On the client end, you use a Socket object to connect to a server which uses a single ServerSocket. Once the connection is established you will have a Socket at both ends and you can use this Socket to Serversocket connection to send data in either direction. i.e.
A single server socket is needed for new connections only.
See: here

How can I force the server socket to re-accept a request from a client?

For those who do not want to read a long question here is a short version:
A server has an opened socket for a client. The server gets a request to open a socket from
the same client-IP and client-port. I want to fore the server not to refuse such a request but to close the old socket and open a new one. How can I do ti?
And here is a long (original) question:
I have the following situation. There is an established connection between a server and client. Then an external software (Bonjour) says to my client the it does not see the server in the local network. Well, client does nothing about that because of the following reasons:
If Bonjour does not see the server it does not necessarily means that client cannot see the server.
Even if the client trusts the Bonjour and close the socket it does not improve the situation ("to have no open socket" is worser that "to have a potentially bad socket").
So, client do nothing if server becomes invisible to Bonjour. But than the server re-appears in the Bonjour and Bonjour notify the client about that. In this situation the following situations are possible:
The server reappears on a new IP address. So, the client needs to open a new socket to be able to communicate with the server.
The server reappears on the old IP address. In this case we have two subcases:
2.1. The server was restarted (switched off and then switched on). So, it does not remember the old socket (which is still used by the client). So, client needs to close the old socket and open a new one (on the same server-IP address and the same server-port).
2.2. We had a temporal network problem and the server was running the whole time. So, the old socket is still available for the use. In this case the client does not really need to close the old socket and reopen a new one.
But to simplify my life I decide to close and reopen the socket on the client side in any case (in spite on the fact that it is not really needed in the last described situation).
But I can have problems with that solution. If I close the socket on the client side and than try to reopen a socket from the same client-IP and client-port, server will not accept the call for a new socket. The server will think that such a socket already exists.
Can I write the server in such a way, that it does not refuse such calls. For example, if it (the server) sees that a client send a request for a socket from the same client-IP and client-port, it (server) close the available socket, associated with this client-IP and client-port and than it reopens a new socket.
You can't "reopen" a socket on your server. If the socket already exists and the client is trying to reconnect then you should get an BindException (see your previous question). The scenario that may be possible:
Client Shuts down socket
Server OS "notices" socket is dead on client side and shuts its side down
Client reconnects on the same port, but with a "new" socket
In this case you may consider it be the "same" socket, but it really isn't. That said a strategy you may wish to adopt is to have some sort of map (hash of client IP/port) to whatever mechanism you are using to service the socket or some kind of persistent state data, so that it can simulate a continuation of a previous socket (in the same vein as http sessioning). Something along the lines of:
HashMap<Client, State> sessions = ...;
public void server(){
...
while(true){
Socket socket = server.accept();
Client client = new Client(socket);
State s = sessions.get(client);
if(s == null){
s = new State();
sessions.put(client, s);
}
client.setState(s);
service(client);
}
...
}
and you can adjust the map lookup to define what a "session" means within your application (same client IP, same client IP & client port, some sessionid sent over the wire, etc).
If you are just trying to make it possible for the client to reconnect and force the server to "notice" the client is disconnected, the only real way in Java is to try and read/write data, and if it has been shutdown then it should throw an exception. Therefore as was mentioned in your other question you could add some kind of ack/nak feature to your protocol and add some type of check if you believe the client is disconnected (for example if you haven't read any data in the last N milliseconds, send a message the client must echo within M milliseconds, otherwise it is assumed to be disconnected). You can also try isConnected, isInputShutdown, isOutputShutdown, but I have found those to be unreliable in my own code to indicate the socket state, unless you have closed the socket (i.e. the one you are testing on the server).
The situation you describe is impossible. You can't get a new connect request from the same remote IP:port as an existing connection. The client will not permit it to occur.
Based on the comments:
You cannot write the server in a way that it will close a socket it still thinks is connected and automatically accept the new connection, as application code does not have that kind of control over the TCP stack, nor is there a way to reopen a connection.
The chance of the port numbers being the same between your client restarts is very small.
But still, if that happens, the server will note that that you're trying to set up an already connected socket, and refuse your new connection. There's not much else your client can do in this case besides close your socket, create a new one and try to connect again - and another random port will be selected.
additional note, your server should take some form of action to detect and close dead sockets, if all your server does is read incoming data, the "dead" sockets will never be
closed as they will never be detected as dead.(enabling tcp keepalive is one cheap measure to take against dead sockets staying up for months, though it will take a couple of hours to detect them as such by default.)

Categories