user1196937
user1196937

Reputation:

Permanent and persistent Socket connection in java

I've created a client-server connection, something like a chat system. Previously I was using a while loop on the client side, and it was waiting to read a message from the console every time (of course server has a while loop as well to serve forever). But now, I'm trying to first create a connection at the beginning of the session, and then occasionally send a message during the session, so to maintain a permanent and persistent connection.

Currently, without the while loop, the client closes the connection and I don't know how to find a workaround.

Here is the client code:

import java.net.*;
import java.io.*;
public class ControlClientTest {
    private Socket socket = null;
//  private BufferedReader console = null;  
    private DataOutputStream streamOut = null;

    public static void main(String args[]) throws InterruptedException {
        ControlClientTest client = null;

        String IP="127.0.0.1";
        client = new ControlClientTest(IP, 5555);
    }

    public ControlClientTest(String serverName, int serverPort) throws InterruptedException {
        System.out.println("Establishing connection. Please wait ...");
        try {
            socket = new Socket(serverName, serverPort);
            System.out.println("Connected: " + socket);
            start();
        } catch (UnknownHostException uhe) {
            System.out.println("Host unknown: " + uhe.getMessage());
        } catch (IOException ioe) {
            System.out.println("Unexpected exception: " + ioe.getMessage());
        }

        String line = "";
//      while (!line.equals(".bye")) {
            try {
                Thread.sleep(1000);
                //TODO get data from input
//              line = console.readLine();
                line="1";
                if(line.equals("1"))
                    line="1,123";
                streamOut.writeUTF(line);
                streamOut.flush();
            } catch (IOException ioe) {
                System.out.println("Sending error: " + ioe.getMessage());
            }
//      }
    }

    public void start() throws IOException {
//      console = new BufferedReader(new InputStreamReader(System.in));
        streamOut = new DataOutputStream(socket.getOutputStream());
    }

}

And here is the Server code:

import java.awt.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ControlServer {
    private Socket socket = null;
    private ServerSocket server = null;
    private DataInputStream streamIn = null;

    public static void main(String args[]) {

        ControlServer server = null;
        server = new ControlServer(5555);
    }

    public ControlServer(int port) {
        try {
            System.out
            .println("Binding to port " + port + ", please wait  ...");
            server = new ServerSocket(port);
            System.out.println("Server started: " + server);
            System.out.println("Waiting for a client ...");
            socket = server.accept();
            System.out.println("Client accepted: " + socket);
            open();
            boolean done = false;
            while (!done) {
                try {
                    String line = streamIn.readUTF();
                    // TODO get the data and do something
                    System.out.println(line);

                    done = line.equals(".bye");
                } catch (IOException ioe) {
                    done = true;
                }
            }
            close();
        } catch (IOException ioe) {
            System.out.println(ioe);
        }

    }


    public void open() throws IOException {
        streamIn = new DataInputStream(new BufferedInputStream(
                socket.getInputStream()));
    }
public void close() throws IOException {
    if (socket != null)
        socket.close();
    if (streamIn != null)
        streamIn.close();
}

}

Upvotes: 6

Views: 20371

Answers (3)

bvdb
bvdb

Reputation: 24770

I would like to summarize some good practices regarding the stability of TCP/IP connections which I apply on a daily basis.

Good practice 1 : Built-in Keep-Alive

socket.setKeepAlive(true);

It automatically sends a signal after a period of inactivity and checks for a reply. The keep-alive interval is operating system dependent though, and has some shortcomings. But all by all, it could improve the stability of your connection.

Good practice 2 : SoTimeout

Whenver you perform a read (or readUTF in your case), your thread will actually block forever. In my experience this is bad practice for the following reasons: It's difficult to close your application. Just calling socket.close() is dirty.

A clean solution, is a simple read time-out (e.g. 200ms). You can do this with the setSoTimeoutmethod. When the read() method timeouts it will throw a SocketTimeoutException. (which is a subclass of IOException).

socket.setSoTimeout(timeoutInterval);

Here is an example to implement the loop. Please note the shutdown condition. Just set it to true, and your thread will die peacefully.

while (!shutdown)
{
  try
  {
    // some method that calls your read and parses the message.
    code = readData();
    if (code == null) continue; 
  }
  catch (SocketTimeoutException ste)
  {
    // A SocketTimeoutExc. is a simple read timeout, just ignore it.
    // other IOExceptions will not be stopped here.
  }
}

Good practice 3 : Tcp No-Delay

Use the following setting when you are often interfacing small commands that need to be handled quickly.

try
{
  socket.setTcpNoDelay(true);
}
catch (SocketException e)
{
}

Good practice 4 : A heartbeat

Actually there are a lot of side scenario's that are not covered yet.

One of them for example are server applications that are designed to only communicate with 1 client at a time. Sometimes they accept connections and even accept messages, but never reply to them.

Another one: sometimes when you lose your connection it actually can take a long time before your OS notices this. Possibly due to the shortcomings described in good practice 3, but also in more complex network situations (e.g. using RS232-To-Ethernet converters, VMware servers, etc) this happens often.

The solution here is to create a thread that sends a message every x seconds and then waits for a reply. (e.g. every 15 seconds). For this you need to create a second thread that just sends a message every 15 seconds. Secondly, you need to expand the code of good practice 2 a little bit.

  try
  {
    code = readData();

    if (code == null) continue; 
    lastRead = System.currentTimeMillis();

    // whenever you receive the heart beat reply, just ignore it.
    if (MSG_HEARTBEAT.equals(code)) continue;

    // todo: handle other messages
  }
  catch (SocketTimeoutException ste)
  {
    // in a typical situation the soTimeout is about 200ms
    // the heartbeat interval is usually a couple of seconds.
    // and the heartbeat timeout interval a couple of seconds more.
    if ((heartbeatTimeoutInterval > 0) &&
        ((System.currentTimeMillis() - lastRead) > heartbeatTimeoutInterval))
    {
      // no reply to heartbeat received.
      // end the loop and perform a reconnect.
      break;
    }
  }

You need to decide if your client or server should send the message. That decision is not so important. But e.g. if your client sends the message, then your client will need an additional thread to send the message. Your server should send a reply when it receives the message. When your client receives the answer, it should just continue (i.e. see code above). And both parties should check: "how long has it been?" in a very similar way.

Upvotes: 22

Sanj
Sanj

Reputation: 850

Make the client socket connection wrapped around a thread. Use a blocking queue to wait for messages. There should only be a single sender queue throughout your application, so use a singleton pattern.

e.g.

QueueSingleton queue = QueueSingleton.getSenderQueue();
Message message = queue.take() // blocks thread
send(message); //send message to server

When you need to send a message to the server, you can use the blocking queue to send the message.

QueueSingleton queue = QueueSingleton.getSenderQueue();
queue.put(message)

The client thread will wake up and process the message.

For maintaining the connection, use a timer task. This is special type of thread that calls a run method repetitively at specified periods. You can use this to post a message, a ping message, every so often.

For processing the received message, you could have another thread, waiting for messages on another blocking queue (receiver queue). The client thread will put the received message on this queue.

Upvotes: 1

NinePlanFailed
NinePlanFailed

Reputation: 395

You could wrap a thread around the connection and have it periodically send a status to keep the line open, say every 30 seconds or whatever. Then, when it actually has data to send it would reset the keep alive to be 30 seconds after the last transmission. The status could be helpful to see if the client is still alive anyway, so at least it can be a useful ping.

Also, you should change your server code, you appear to only handle one connection at the moment. You should loop and when a socket connection comes in spawn a thread to handle the client request and go back to listening. I may be reading to much into what may just be your test code, though.

Upvotes: 1

Related Questions