Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In java network programming, is there a way to keep the Server side open even when the Client side shuts down?

Suppose that we have a simple Echo client/server pair in Java. As I understand it, once one side of the socket breaks then the whole connection is gone.

But what if I want a Server that can always stay alive, even if the client side dies. I want to be able to resume a broken connection.

Echo Server :

import java.net.Socket;
import java.net.ServerSocket;

public class EchoServer {

    public static void main(String[] args) throws Exception {

        // create socket
        int port = 4444;
        ServerSocket serverSocket = new ServerSocket(port);
        System.err.println("Started server on port " + port);

        // repeatedly wait for connections, and process
        while (true) {

            // a "blocking" call which waits until a connection is requested
            Socket clientSocket = serverSocket.accept();
            System.err.println("Accepted connection from client");

            // open up IO streams
            In  in  = new In (clientSocket);
            Out out = new Out(clientSocket);

            // waits for data and reads it in until connection dies
            // readLine() blocks until the server receives a new line from client
            String s;
            while ((s = in.readLine()) != null) {
                out.println(s);
            }

            // close IO streams, then socket
            System.err.println("Closing connection with client");
            out.close();
            in.close();
            clientSocket.close();
        }
    }
}

any tips appreciated thanks

like image 754
Caffeinated Avatar asked Jul 21 '15 06:07

Caffeinated


2 Answers

What you're missing is the concept of a 'session'. Think about the position of a server, when some bits arrive 'on the wire'. What to do with these? With TCP/IP, there is some information already present on the wire, namely:

  • src addr
  • src port
  • dest addr
  • dest port
  • and the message payload itself
  • and some other stuff (like sequence counters, to ensure 'packets' are not jumbled during transmission)

The operating system of the server uses the src/dest addr/port information to decide if this is 'a conversation which is already in progress' or not. Mainly it considers the dest port (since the message already made it to the machine itself, through the internet and the firewall) to decide if it is for your Java program listening on the 'dest port'. However, it uses the entire src-addr/src-port/dest-addr/dest-port to try and deliver the payload to your program in the order the sender sent them (which might not be the order in which they arrived, because of the intervening inter-net). Notice that a single 'message' might actually be split up into multiple packets. Your operating system's TCP/IP stack is doing a fair bit of work for you.

However, notice that in order to perform this function on your behalf, the operating system has to devote some amount of resources to 'tracking the state' of the TCP/IP session. At the very least, for each set of port/addr src/dest, there needs to be a counter of the last well-received 'packet', some buffer space to hold the packets until your program is ready to consume them, and so forth.

Now the question facing the TCP/IP stack implementor is "how long should I 'hang on' to this state for"? Is 10 seconds enough? 20 minutes? At some point, after not hearing from the client for a while, the session must 'timeout'. If there were more packets to send in a sequence, and the client starts sending them again, the server has to be able to say "sorry, you're sending me packet 234 of some previous message, but because I didn't hear from you for a while, I threw out packets 1-233. Could we start again?".

So in that sense, there's no way to prevent the client from 'disconnecting'. Sure, you can keep listening on the socket, in case the client recovers and sends some more data. But you and your client will need a way to 'pick up where we left off'.

In HTTP, that's achieved using a 'session cookie' - a long unique string that the server gave to the client, and the client re-sends with each fresh request (whether it happens over the same TCP-level session or not). This is an 'application level session' which has a lifetime longer than that of the TCP session.

Since you're writing an application, and it sounds like you have some degree of control over what the client and server get to say to each other (the 'protocol'), you have more options about how you will agree on what is a session, how to deal with things if the client 'goes away' (timeout the session), and how both sides will recover and 'pick up where we left off' (re-establish the session). Don't forget about authentication will you!

As others have said, it depends a great deal on what you're trying to achieve.

like image 132
David Bullock Avatar answered Sep 21 '22 12:09

David Bullock


When you want to create a Server and multiple Clients, what we normally do is to create a Thread responsible for the communication between the server and one of the clients, so everytime a client connects to the server, the server will start a new Thread and will give it the corresponding socket.

The code should be something like this:

import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

public class EchoServer {

    public static void main(String[] args) throws Exception {

        ArrayList<ClientHandler> clientHandlersList = new ArrayList<ClientHandler>();

        // create socket
        int port = 4444;
        boolean isOver = false;
        ServerSocket serverSocket = new ServerSocket(port);
        System.err.println("Started server on port " + port);

        // repeatedly wait for connections, and process
        while (!isOver) {

            // a "blocking" call which waits until a connection is requested
            Socket clientSocket = serverSocket.accept();
            System.err.println("Accepted connection from client");

            ClientHandler handler = new ClientHandler(clientSocket);
            handler.start();
            clientHandlersList.add(handler);
        }

        serverSocket.close();
    }
}

And the ClientHandler Class:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;


public class ClientHandler extends Thread {

    private Socket socket;
    private ObjectInputStream in;
    private ObjectOutputStream out;
    private boolean disconnect = false;


    public ClientHandler(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            in = new ObjectInputStream(socket.getInputStream());
            out = new ObjectOutputStream(socket.getOutputStream());
            while(!disconnect){
                Object message = readMessage();

                //DO something
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Object readMessage(){
        try {
            Object obj = in.readObject();
            return obj;

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    public void writeMessage(Object obj){
        try {
            out.writeObject(obj);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void disconnect(){
        try {
            disconnect = false;
            out.close();
            in.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
like image 27
Lourenco Avatar answered Sep 21 '22 12:09

Lourenco