Header javaperspective.com
JavaPerspective.com  >   Intermediate Tutorials  >   4. Networking  >   4.3. TCP servers and clients

4.3. TCP servers and clients
Last updated: 1 February 2013.

This tutorial will show you how to write a basic multithreaded TCP server and a client for that server. Depending on the incoming request, the server will simply deliver to remote clients its JRE version or a string representing the current date and time. In the Java language, network programming revolves around sockets. A socket is an endpoint defined by an IP address, a transport protocol (TCP in this case) and a port number.


4.3.1. How to implement a TCP server

To implement a TCP server, you only need to use two classes from the Java API: ServerSocket and Socket from the package java.net. The class ServerSocket has a method named accept which accepts incoming connections and returns an instance of the class Socket representing the server side endpoint of a TCP connection. In fact, a TCP server is nothing more than a thread that waits for incoming TCP connections by calling the method accept in an infinite loop. Every time a new connection is accepted, a new server side Socket instance is created, allowing bidirectional communication with the remote client. In order to handle that communication, a new thread is created and started as shown below:

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public final class TcpServer extends Thread {

   
private ServerSocket serverSocket;
   
private int portNumber = 7777;


   
public TcpServer() throws IOException {
         
setName("TcpServer");

         
// create a ServerSocket instance and bind it to the specified port number
         
serverSocket = new ServerSocket(portNumber);
   
}


   
public void run(){
         
try{
               
while (! isInterrupted()) {

                     
// accept a new connection
                     
Socket socket = serverSocket.accept();

                     
// create a new thread to handle communication with the remote client
                     
new ConnectionHandler(socket).start();
               
}
          }
         
catch(IOException e){
               
e.printStackTrace();
         
}
         
finally{
               
try{
                     
// close the ServerSocket instance before termination
                     
serverSocket.close();
               
}
               
catch(IOException e){
                     
e.printStackTrace();
               
}
          }
    }

}

As you can see, a single instance of the class ServerSocket is created in the constructor of the class TcpServer. An IOException is thrown if the server cannot listen on the specified port number (for example, another TCP server may already be listening on that port number).

In the run method, the while statement is not exactly an infinite loop. Instead, the interrupted status of the thread is tested. That way, you can call the method interrupt from another thread, which will set the interrupted status to true. As a result, at the next iteration, the thread will exit the loop and terminate gracefully.

Within the loop, the method accept is called. If there isn't any connection request, the method accept blocks. If a connection request arrives, a new Socket instance is created upon acceptance and the connection is established. The newly created socket is bound locally to the TCP server's port. Naturally, that newly created socket's remote IP address is the client's IP address, as well as the remote port number of the socket is the client's port number. In order to handle each new connection, a ConnectionHandler thread is created and started, which allows the server to leave the communication process with each client to a separate thread and continue accepting other incoming connections.

To finish, when the thread is interrupted, the ServerSocket instance is closed in the finally block, which guarantees that it will be closed in all cases.

Note that the method accept blocks forever until a connection request arrives. If you want it to block for a given amount of time, call the method setSoTimeout(int timeout) provided by the class ServerSocket before entering the while loop in which the method accept is called. When the specified timeout expires, a SocketTimeoutException is thrown. It is usually handled inside the while loop with a try/catch block wherein the catch block is empty.


The ConnectionHandler thread :

The TCP server is multithreaded, that is, a new ConnectionHandler thread is created and started to handle each connection. After a client has successfully established a connection with the TCP server, the purpose of a ConnectionHandler thread is to respond to the requests of the client appropriately via a socket. In fact, when the connection has just been established, no data has been yet transfered through the socket by any of the parties.
Consequently, for the communication to be meaningful, the server (the ConnectionHandler thread) and the client must agree on a set of simple rules: The client can request the server's JRE version or the current date and time but it can also send a message indicating that it has finished sending requests and that the connection can be closed. Such a set of rules is actually a protocol. In every communication between a client and a server, there must be some sort of protocol. In this case, here are the rules: The ConnectionHandler thread uses the 3 following classes from the Java API: BufferedReader and InputStreamReader to read the requests sent by the client and PrintWriter to write into the socket the responses destined to the client. Here is the code:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Date;

public final class ConnectionHandler extends Thread {

   
private Socket socket;


   
public ConnectionHandler(Socket socket){
         
setName("ConnectionHandler");
         
this.socket = socket;
   
}


   
public void run(){
         
BufferedReader in = null;
          PrintWriter out =
null;

          String request;

         
try{
               
// Get the input and output streams
               
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                out =
new PrintWriter(socket.getOutputStream(), true);

               
while( (request = in.readLine()) != null){

                     
// Deliver the current date and time if the client sends GET_TIME
                     
if(request.equals("GET_TIME"))
                           
out.println(new Date().toString());

                     
// Deliver the JRE version if the client sends GET_JAVA_VERSION
                     
else if(request.equals("GET_JAVA_VERSION"))
                           
out.println(System.getProperty("java.version"));

                     
// Exit the loop if the client sends CLOSE
                     
else if(request.equals("CLOSE"))
                           
break;

               
}
          }
         
catch(IOException e){
               
e.printStackTrace();
         
}
         
finally{
               
try{
                     
if(socket != null)
                           
socket.close(); // Close the socket (closing the socket also closes the input and output streams)
               
}
               
catch(IOException e){
                     
e.printStackTrace();
               
}
          }
    }
}

In the code above, the thread ConnectionHandler simply reads the requests coming from the client. If the request is GET_TIME or GET_JAVA_VERSION, the server sends back the current date and time or the Java version respectively and if the request is CLOSE, the thread exits the loop, closes the socket and dies.

Last but not least, a class containing a main method has to be created in order to start the TCP server:

import java.io.IOException;

public final class TcpServerStarter {

   
public static void main(String[] args){
         
try{
               
TcpServer tcpServer = new TcpServer();
                tcpServer.start
();
         
}
         
catch(IOException e){
               
e.printStackTrace();
         
}
    }
}


4.3.2. How to implement a TCP client

To implement a TCP client, just use the class Socket to establish a TCP connection with the server. Assuming that the server and the client are running on the same computer, the class TcpClient shown below contains a main method and a method named process which establishes a TCP connection with the server running locally on the port number 7777. After the connection has been established, the client sends GET_TIME and GET_JAVA_VERSION before sending a CLOSE request:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

public final class TcpClient {

   
private void process() {
         
Socket socket = null;

          BufferedReader in =
null;
          PrintWriter out =
null;

         
try{
               
/* Establish a TCP connection with the server running locally on the port number 7777.
                 * You can write "localhost" instead of "127.0.0.1".
                 * If the server is running on a remote computer, replace "127.0.0.1" with the server's IP address or hostname
                 */
               
socket = new Socket("127.0.0.1", 7777);

               
// Get the input and output streams
               
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                out =
new PrintWriter(socket.getOutputStream(), true);

               
// Send a GET_TIME request and print the response to the standard output
               
out.println("GET_TIME");
                System.out.println
(in.readLine());

               
// Send a GET_JAVA_VERSION request and print the response to the standard output
               
out.println("GET_JAVA_VERSION");
                System.out.println
(in.readLine());

               
// Send a CLOSE request to the server to end communication
               
out.println("CLOSE");
         
}
         
catch(IOException e){
               
e.printStackTrace();
         
}
         
finally{
               
try{
                     
if(socket != null)
                           
socket.close(); // Close the socket (closing the socket also closes the input and output streams)
               
}
               
catch(IOException e){
                     
e.printStackTrace();
               
}
          }
    }


   
public static void main(String[] args){
         
new TcpClient().process();
   
}
}

The server must be started before the client. If you run the client, the current date and the server's JRE version will be displayed.

The next tutorial will show you how to implement a UDP server and client.


You are here :  JavaPerspective.com  >   Intermediate Tutorials  >   4. Networking  >   4.3. TCP servers and clients
Next tutorial :  JavaPerspective.com  >   Intermediate Tutorials  >   4. Networking  >   4.4. UDP servers and clients

Copyright © 2013. JavaPerspective.com. All rights reserved.  ( Terms | Contact | About ) 
Java is a trademark of Oracle Corporation
Image 1 Image 2 Image 3 Image 4 Image 5 Image 6 Image 7