views:

136

answers:

6

I have the following problem. My client program monitor for availability of server in the local network (using Bonjour, but it does not rally mater). As soon as a server is "noticed" by the client application, the client tries to create a socket: Socket(serverIP,serverPort);.

At some point the client can loose the server (Bonjour says that server is not visible in the network anymore). So, the client decide to close the socket, because it is not valid anymore.

At some moment the server appears again. So, the client tries to create a new socket associated with this server. But! The server can refuse to create this socket since it (server) has already a socket associated with the client IP and client port. It happens because the socket was closed by the client, not by the server. Can it happen? And if it is the case, how this problem can be solved?

Well, I understand that it is unlikely that the client will try to connect to the server from the same port (client port), since client selects its ports randomly. But it still can happen (just by chance). Right?

+1  A: 

Short answer: yes, you should close socket on both ends.

Although the answer is simple, in reality it may be very hard to detect that the peer has stopped responding if you don't build some ACK/NACK scheme into your client-server protocol.

Even with your protocol ACKs your processing thread may be hanging waiting for ACKs that will never come from the client or vice versa.

If you use Blocking I/O, I would start by setting read timeouts on a socket. Unfortunately, if the peer becomes unresponsive, there is no corresponding time-out for the writes. One blunt instrument that I found has value in our environment is to create blocking Sockets through java.nio methods and then interrupt the processing thread at configurable intervals.

Interrupting processing thread will close socket, but if you pick the timeout large enough you will know that there is a trouble. We chose this approach because application was written initially with blocking I/O and a cost to transition it to non-blocking was very high.

With the non-blocking I/O, though, you may check at a finer-grained interval the status of your connection and react more intelligently to the slow/unresponsive connections.

Though non-blocking I/O requires a higher upfront investment, I think it will pay better dividends in terms of reliability and better throughput later on.

Alexander Pogrebnyak
+1  A: 

the client operating system will not allocate the same port to a new socket so soon. there are several mechanism that prevents it. one of which is the TIME_WAIT state that reserves the port for some time after the connection is closed.

I wouldn't worry about it.

if you really need to detect disconnection you will have to implement ping/pong protocol, initiated by both the client and the server.

Omry
+2  A: 

Yes, close the socket, as soon as you detect a failure.

The socket will be "stuck" in "close_wait" if not closed properly. Even if the socket is closed, it's state will be in time_wait for a short period.

However, if You design the application to use a different local port for each new connection, there is no need to wait for the old socket to be closed.

(As you are then creating a completly different socket, since a socket is identified by the remote-ip, remote port, local ip and local port.)

KarlP
'if You design the application to use a different local port for each new connection'As that's what happens automatically, there is no need to design for it. Just avoid specifying local ports completely when constructing Sockets.
EJP
Oh yes, what I meant was that one shouldn't design an application to rely on what port a request is coming FROM.
KarlP
A: 

It sounds like your client is detecting loss of connectivity to the server (using Bonjour), but you don't have the corresponding capability in the other direction.

You're certainly going to want some kind of timeout for inactive connections on the server side as well, otherwise dead connections will hang around forever. Beyond the problem of potential IP address/port # collisions you mention, there's also the fact that the dead connections are consuming OS and application resources (such as open file descriptors)

Conversely, you might also want to consider not being too aggressive in closing a connection from the client side when Bonjour says the service is no longer visible. If you're in a wireless scenario, a transient loss of connectivity isn't that uncommon, and it's possible for a TCP connection to remain open and valid after connectivity is restored (assuming the client still has the same IP address). The optimum strategy depends on what kind of connection you're talking about. If it's a relatively stateless connection where the cost of discarding the connection and retrying is low (like HTTP), then it makes sense to toss the connection at the first sign of trouble. But if it's a long-lived connection with significant user state (like an SSH login session), it makes sense to try harder to keep the connection alive.

David Gelhar
+2  A: 

A quick/ dirty illustration of why this can't happen (note the client forcibly uses the same local port in its connection):

public class Server{

public static void main(String[] args) throws Exception {
    new Thread(){
        java.net.ServerSocket server = new java.net.ServerSocket(12345);
        java.util.ArrayList<java.net.Socket> l = new java.util.ArrayList<java.net.Socket>();
        public void run() {
            try{
            while(true){
                 java.net.Socket client = server.accept();
                System.out.println("Connection Accepted: S: "+client.getLocalPort()+", C: "+client.getPort());
                l.add(client);
            }
            }catch(Exception e){e.printStackTrace();}
        }
    }.start();
}

and a client (replace server address with something valid):

import java.net.InetAddress;
import java.net.Socket;


public class SocketTest {
    public static void main(String[] args) throws Exception {
        InetAddress server = InetAddress.getByName("192.168.0.256");
        InetAddress localhost = InetAddress.getLocalHost();
        Socket s = new Socket(server, 12345, localhost, 54321);
        System.out.println("Client created socket");
        s.close();
        s = null;
        System.gc();
        System.gc();
        Thread.sleep(1000);
        s = new Socket(server, 12345, localhost, 54321);
        System.out.println("Client created second socket");
        s.close();
        System.exit(55);
    }
}

If you start the server and then try to run the client the first connection will succeed, but the second will fail with a "java.net.BindException: Address already in use: connect"

M. Jessup
A: 

If you close server socket only in case of blocking socket then client socket will be closed but not vice versa.

otherwise it would be better socket in both end. Because socket is a heavy weigt for you system. It will use a local port and a remote port of your system forever.

Thanks Sunil Kumar Sahoo

Deepak