« Back to Home

How to implement TCP server in Java (1)

In these days I feel comfortable Spring, but it has been suddenly very cold from last week. That's why I have a sore throat.

By the way, I don't know why, but I have to implement a server from scratch using sockets recently. So I write about how to implement TCP server in Java, in addition to organizing my ideas.

Handling server sockets in Java

There are three ways to handle server sockets in Java SE 7.

To use java.net.ServerSocket

At first we should start from basic point. Let's try writing echo-server using ordinary socket API that was already active when I was high school student. Although it's called "ordinary socket API", the others also handle "ordinary socket" but in defferent way.

This API is very simple. See following code.

try (ServerSocket listener = new ServerSocket();) {
    listener.setReuseAddress(true);
    listener.bind(new InetSocketAddress(8080));
    System.out.println("Server listening on port 8080...");
    while (true) {
        try (Socket socket = listener.accept();) {
            InputStream from = socket.getInputStream();
            OutputStream to = socket.getOutputStream();
            Bytes.copy(from, to);
        }
    }
} catch (IOException e) {
    e.printStackTrace();
}

The proccess is,

  1. To instantiate a ServerSocket and bind to a port.
  2. Waiting for an incomming connection from the client, (or the thread is blocked at the method ServerSocket#accept() calling) and obtain a Socket by ACCEPTing the connection.
  3. To do something over the Socket's streams.
  4. Closing the Socket.
  5. Continuing from waiting for new connection.

Then, run this.

$ java SocketServer
Server listening on port 8080...

Good, our server is running. Let's try connecting from another terminal.

$ telnet localhost 8080
Trying ::1...
Connected to localhost.
Escape character is '^]'.

It's OK. Now we have a connection to our server. Input something to the terminal, and get output as is. Congratulations! ...But this server has a big problem.

Multithreading

The problem is that the server can handle only one connection at the same time. To get it, connect from some terminals. The others' connections are not handled unless one's is closed.

That's because we handle a Socket one by one in the previous code. Therefore, we make it multithreaded.

ExecutorService worker = Executors.newCachedThreadPool();
try (ServerSocket listener = new ServerSocket();) {
    listener.setReuseAddress(true);
    listener.bind(new InetSocketAddress(8081));
    System.out.println("Server listening on port 8081...");
    while (true) {
        final Socket socket = listener.accept();
        worker.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    InputStream from = socket.getInputStream();
                    OutputStream to = socket.getOutputStream();
                    Bytes.copy(from, to);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        socket.close();
                    } catch (IOException e) {
                    }
                }
            }
        });
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    worker.shutdown();
}

It's done. Now we have a very practical echo-server which is able to handle connections at the same time.

Do you have peace of mind? It's too early. Remember that there are the two other ways to handle server sockets. They have some good points which this "ordinary socket API" don't have. (But also have some bad points) Next time, let's check how to use the second, "Non-blocking" API.