Networking

Achieve seamless socket programming in Java

Programming client and server applications in Java is easier than doing it in other languages, such as C. Get a first-hand look at how the java.net package provides a powerful and flexible infrastructure for network programming.

One main reason why the Java platform is successful is because networking support has been available from the beginning. In this article, I describe how to utilize this fundamental networking ability by using Java sockets.

What are sockets?

The TCP/IP family of protocols is an important tool in organizing distributed computations and communication within enterprise applications. The TCP protocol handles transport tasks for application, while the IP protocol serves as an envelope for TCP packet data, providing networking and addressing capabilities. There is another transport layer protocol called the User Datagram Protocol (UDP) that is encapsulated into the IP packet and serves as a connectionless datagram protocol. Since the UDP packet transmission does not require connection, it is a one-way message without confirmation. It is faster but less reliable.

Get developer tips in your inbox
Delivered each Thursday, our free Java newsletter provides insight and hands-on tips you need to unlock the full potential of this programming language.
Automatically sign up today!

At both ends of a live TCP connection must be opened sockets before any data can be transmitted. From a Java programmer's point of view, a socket is a channel for sending and receiving data from another application located on a different machine.

In order to initiate a TCP connection, you can use standard Java library functions from classes that are located in a java.net package. This package contains classes for working with sockets on any operating environment, so your Java remains truly platform independent. Calls from java.net classes are translated into operating system API calls, and all the details of the TCP/IP implementation are hidden from the application developer. For TCP sockets, you will use InetAddress, Socket, and ServerSocket classes. For UDP sockets, you will use classes DatagramSocket and DatagramPacket. You can even create new network protocols based on TCP or UDP. For these purposes, you can use special classes and interfaces, including SocketImplFactory, SocketImpl, and DatagramSocketImpl.

Server side

I'll start by writing a small client/server application consisting of a server that continuously listens for incoming connections; I'll also write a client application that connects to a server process and transmits data. These two programs run independently and can be located at different hosts.

The server program begins by creating a new ServerSocket object to listen on a specific port. (When writing a server application, choose a port that is not already dedicated to some other service.) In the example below, the server listens on port 4444:

import java.io.*;
import java.net.*;

try {
    serverSocket = new ServerSocket(4444);
} catch (IOException e) {
    System.out.println("Could not listen on port: 4444");
    System.exit(-1);
}

ServerSocket is a java.net class that provides a system-independent implementation of the server side of a client/server socket connection. The constructor for ServerSocket throws an exception if it can't listen on the specified port (for example, the port is already being used). If the server successfully binds to its port, the ServerSocket object is created and the server continues to the next step -- accepting a connection from a client.

Socket clientSocket = null;
try {
    clientSocket = serverSocket.accept();
} catch (IOException e) {
    System.out.println("Accept failed: 4444");
    System.exit(-1);
}

The accept method waits until a client starts and requests a connection on the host and port of this server. When a connection is requested and successfully established, the accept method returns a new Socket object, which is bound to the same local port and has its remote address and remote port set to that of the client. The server can communicate with the client over this new Socket and continue to listen for client connection requests on the original ServerSocket. (This particular version of the program doesn't listen for more client connection requests.)

Multiple client requests can come into the same port and, consequently, into the same ServerSocket. Client connection requests are queued at the port, so the server must accept the connections sequentially; however, the server can service them simultaneously through the use of threads (i.e., one thread per client connection). This logic is used in all multi-connection Java server applications. See the following example:

while (true) {
    accept a connection ;
    create a thread to deal with the client ;
end while

The new thread reads from and writes to the client connection as necessary. After the server successfully establishes a connection with a client, it communicates with the client using this code:

PrintWriter out = new PrintWriter(
                      clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
                        new InputStreamReader(
                            clientSocket.getInputStream()));
String inputLine, outputLine;
while ((inputLine = in.readLine()) != null) {  
    outputLine = inputLine;
    out.println(outputLine);
    if outputLine.equals("Bye."))
        break;
}

In this case, the server will duplicate all incoming information in responses until it receives the "Bye." string. As long as the client and server still have something to say to each other, the server reads from and writes to the socket, sending messages back and forth between the client and the server. After that, the server will terminate the connection and all the streams.

out.close();
in.close();
clientSocket.close();
serverSocket.close();

Client side

When you start the client program, the server should already be running and listening to the port, waiting for a client to request a connection. The first thing the client program does is open a socket that is connected to the server running on the hostname and port specified:

import java.io.*;
import java.net.*;

theSocket = new Socket("hostname.example.com", 4444);
out = new PrintWriter(theSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(
                            theSocket.getInputStream()));

When you run this program, change the host name in the Socket constructor to the name of a machine on your network. This is the machine on which you will run the server application. The program also specifies the port number 4444 when creating its socket. This is a remote port number (i.e., the number of a port on the server machine) and is the port to which the server is listening. The client's socket is bound to any available local port (i.e., a port on the client machine).

Next comes the while loop, which implements the communication between the client and the server. The client application will read the input from the keyboard (or standard input) and send it to a server, so the server will reply with the same string.

BufferedReader stdIn = new BufferedReader(
                                   new InputStreamReader(System.in));
String userInput;
String fromServer = "";
while ((userInput = stdIn.readLine()) != null) {
  out.println(userInput);
  fromServer = in.readLine();
  System.out.println("echo: " + fromServer);
  if (fromServer.equals("Bye."))
    break;
}

Note that the client will terminate the connection when the server says "Bye.". To do so, you will need to say the same string from the client side, after which it will be transmitted to the server and then replied back. The client must close all streams and connections.

out.close();
in.close();
stdIn.close();
theSocket.close();

Conclusion

The java.net package provides a powerful and flexible infrastructure for network programming. I encourage you to refer to that package if you want to know the classes that are provided.

Resources for further reading

Peter V. Mikhalenko is a Sun certified professional who works for Deutsche Bank as a business consultant.

2 comments
rehanali
rehanali

Shouldn't "in.readLine()" be stored in the variable "fromServer"? We are checking if "fromServer" is "Bye.", but we do not set its value to what we receive from the server. Should the code look like: String fromServer = ""; while ((userInput = stdIn.readLine()) != null) { out.println(userInput); fromServer = in.readLine(); System.out.println("echo: " + fromServer); if (fromServer.equals("Bye.")) break; } Or am I missing something?

peter_econ
peter_econ

Thank you, you're absolutely right. Corrected.

Editor's Picks