JavaData & JavaSocket Programming: TCP Client/Server Application

Socket Programming: TCP Client/Server Application

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

A client/server application model typically is viewed as a remotely located, high powered computing device that stores a large quantity of data with business logic to access them in a network. The user interface is handled by the client software on a relatively cheap machine. This idea is not distinct because any machine serving the request can potentially be called a server. Although the server waits for the client to start a conversation, in some cases the same program may act as both client and server. In that sense, a single machine can act as a network providing the communication between the client and the server program that goes through layers of a TCP/IP protocol stack. A socket is an API provided by the OS to realize the connection. The package java.net provides the necessary ingredients to implement the socket communication between two of the topmost TCP/IP layers: application and transport. The article elaborates the concept behind the client/server model with hands-on details in creating a TCP client/server application in Java.

Client Socket Basics

A socket establishes the connecting endpoints between two hosts. The Socket class provided by Java is used for both clients and servers. The basic operations area is as follows:

  • Connect to remote host.
  • Send and receive data.
  • Close a connection.
  • Bind to a port.
  • Listen to incoming data.
  • Accept remote connections on the bounded port.

The last three operations are specific to servers only; they are implemented by the ServerSocket class. The client program work flow occurs in the following manner:

  1. Create a new socket object.
  2. Attempt to connect to the remote host
  3. Once connection has succeeded, the local and remote hosts get hold of the input and output streams and can work in full duplex mode. The data received and sent can mean different things, depending on the protocol used (data sent/received from an FTP server can be different from an HTTP server). Generally, there is some kind of agreement established followed by data transmission.
  4. Sockets must be closed at both ends after transmission is completed. Some protocols, such as HTTP, make sure that the connection is closed upon each request service. FTP, on the other hands, allows multiple requests to process before closing the connection.

A Quick Example

The following code demonstrates how a client socket can be created. The application is a rudimentary chat application that enables one to chat with the server in full-duplex mode until the connection is explicitly closed.

package tcpsocketdemo;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class Client extends JFrame {

   private static final int PORT = 12345;
   private static final int BACKLOG = 100;

   private final JTextField msgField =
      new JTextField();
   private final JTextArea msgDisplayArea =
      new JTextArea();
   private ObjectOutputStream out;
   private ObjectInputStream in;
   private Socket socket;
   private final String host;
   private String msg;

   public Client(String host) {
      super("Client");
      this.host=host;
      msgField.addActionListener((ActionEvent e) -> {
         send(e.getActionCommand());
         msgField.setText("");
      });
      super.add(msgField, BorderLayout.NORTH);
      super.add(new JScrollPane(msgDisplayArea));
      super.setSize(400, 250);
      super.setVisible(true);
      super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }

   public void startClient() {
      try {
         connectCall();
         setIOStreams();
         processCall();

      } catch (IOException e) {
      }
   }

   private void setIOStreams() throws IOException {
      out = new ObjectOutputStream(socket.getOutputStream());
      out.flush();
      in = new ObjectInputStream(socket.getInputStream());
      showMsg("nI/O Stream is ready.");
   }

   private void connectCall() throws IOException {
      showMsg("nConnecting...");
      socket = new Socket(InetAddress.getByName(host), PORT);
      showMsg("Connected to " +
         socket.getInetAddress().getHostName());
   }

   private void processCall() {
      setMsgFieldEditable(true);
      do {
         try {
            msg = (String) in.readObject();
            showMsg("n" + msg);
         } catch (ClassNotFoundException | IOException ex) {
            showMsg("nInvalid object received");
         }
      } while (!msg.equals("Server# bye"));

   }

   private void showMsg(final String msg) {
      SwingUtilities.invokeLater(() -> {
         msgDisplayArea.append(msg);
      });
   }

   private void setMsgFieldEditable(final boolean flag) {
      SwingUtilities.invokeLater(() -> {
         msgField.setEditable(flag);
      });
   }

   private void send(String msg) {
      try {
         out.writeObject("nClient# " + msg);
         out.flush();
         showMsg("nClient# " + msg);
      } catch (IOException ex) {
         msgDisplayArea.append("Error: " + ex);
      }
   }

   private void endCall() {
      showMsg("nConnection closed");
      setMsgFieldEditable(false);
      try {
         out.close();
         in.close();
         socket.close();
      } catch (IOException ex) {
      }
   }

}


package tcpsocketdemo;

public class TCPSocketClient{
   public static void main(String[] args){
      String localhost="127.0.0.1";

      Client app=new Client(localhost);
      app.startClient();
   }
}

Server Socket Basics

The ServerSocket class provides the niche to write everything about servers in Java. The main job of a server socket is to wait for incoming calls and respond accordingly. ServerSocket runs on the server bounded by a port and listens to incoming TCP connections. When a client Socket attempts to connect to that port, the server wakes up to negotiate the connection by opening a Socket between two hosts. This Socket object is used to send data to the clients. The work flow of the server program can be defined as follows:

  1. Create server socket on a particular port.
  2. Listen to any attempt of connection to that port.
  3. If a connection attempt succeeds, get the host of stream objects from the Socket and communicate with the client.
  4. The communication, however, is established according to the agreed protocol.
  5. Close the connection.
  6. Wait further for any more communication attempts (return to Step 2).

A Quick Example

The following code demonstrates how a Server socket can be created. Observe that most of the functionality is very similar to the client program created earlier. The element that designates this class as the server is the ServerSocket object.

package tcpsocketdemo;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class Server extends JFrame {
   private static final int PORT = 12345;
   private static final int BACKLOG = 100;

   private final JTextField msgField =
      new JTextField();
   private final JTextArea msgDisplayArea =
      new JTextArea();
   private ObjectOutputStream out;
   private ObjectInputStream in;
   private ServerSocket server;
   private Socket socket;

   public Server() {
      super("Server");
      msgField.setEditable(false);
      msgField.addActionListener((ActionEvent e) -> {
         send(e.getActionCommand());
         msgField.setText("");
         throw new UnsupportedOperationException
            ("Not supported yet.");
      });
      super.add(msgField, BorderLayout.NORTH);
      super.add(new JScrollPane(msgDisplayArea));
      super.setSize(400, 250);
      super.setVisible(true);
      super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }

   public void startServer() {
      try {
         server = new ServerSocket(PORT, BACKLOG);
         while (true) {
            try {
               waitForCall();
               setIOStreams();
               processCall();
            } catch (EOFException ex) {
               showMsg("nServer connection closed");
            } finally {
               endCall();
            }
         }
      } catch (IOException e) {
      }
   }

   private void setIOStreams() throws IOException {
      out = new ObjectOutputStream(socket.getOutputStream());
      out.flush();
      in = new ObjectInputStream(socket.getInputStream());
      showMsg("nI/O Stream is ready.");
   }

   private void waitForCall() throws IOException {
      showMsg("nWaiting for call...");
      socket = server.accept();
      showMsg("Connection accepted from " +
         socket.getInetAddress().getHostName());
   }

   private void processCall() {
      String msg = "Connection established successfully.";
      send(msg);
      setMsgFieldEditable(true);
      do {
         try {
            msg = (String) in.readObject();
            showMsg("n" + msg);
         } catch (ClassNotFoundException | IOException ex) {
            showMsg("nInvalid object received");
         }
      } while (!msg.equals("Client# bye"));
   }

   private void showMsg(final String msg) {
      SwingUtilities.invokeLater(() -> {
         msgDisplayArea.append(msg);
      });
   }

   private void setMsgFieldEditable(final boolean flag) {
      SwingUtilities.invokeLater(() -> {
         msgField.setEditable(flag);
      });
   }

   private void send(String msg) {
      try {
         out.writeObject("nServer# " + msg);
         out.flush();
         showMsg("nServer# " + msg);
      } catch (IOException ex) {
         msgDisplayArea.append("Error: " + ex);
      }
   }

   private void endCall() {
      showMsg("nConnection closed");
      setMsgFieldEditable(false);
      try {
         out.close();
         in.close();
         socket.close();
      } catch (IOException ex) {
      }
   }
}

packagetcpsocketdemo;
public class TCPSocketServer{

   public static void main(String[] args) {
      Server app=new Server();
      app.startServer();
   }
}

Points to be Noted

Here we have been discussing connection-oriented, stream-based transmission represented by TCP packets. There is another side of the story of client/server interaction which is connectionless, called a datagram, represented by UDP packets. More details will be provided when we create an UDP client/server application in the next article.

A hint: New Java IO (NIO.2) can be interestingly merged with Socket programming. These are advanced features. Here, we have covered the basic more so than in future articles.

Conclusion

As we have seen, creating client/server communication through sockets is not that difficult. Interested readers may focus on the ServerSocket class in particular, because this class is very powerful and can be leveraged to create your own customized server or reinvent an HTTP, FTP server. Intriguingly, creating your own HTTP server can be a great learning experience and it’s also not that difficult, at least in Java.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories