Writing code that executes on a certain device is very satisfying. But, writing code that executes on several devices that communicate with each other is simply life-affirming. This article will teach you how to connect and exchange messages over network using transmission control protocol (TCP).

In this article, you will setup an application that will connect your computer to itself and, essentially, make it crazy - talk to itself. You will also learn the difference between the two most widely used streams for networking in Java and how they function.

Data and Object Streams

Before diving into code, the difference between the two streams used in the article needs to be distinguished.

Data Streams

Data streams process primitive data types and strings. Data sent over data streams needs to be manually serialized and deserialized which makes it harder to transfer complex data. But, data streams can communicate with servers and clients written in other languages than Java. Raw streams are similar to data streams in that aspect, but data streams ensure the data is formatted in a platform independent way which is beneficial because both parties will be able to read sent data.

Object Streams

Object streams process primitive data types and objects that implement Serializable interface. Data sent over object streams is automatically serialized and deserialized which makes it easier to transfer complex data. But, object streams can only communicate with servers and clients written in Java. Also, ObjectOutputStream, upon initialization, sends a header to the InputStream of the other party which, upon initialization, blocks execution until the header is received.

Steps

  1. 1
    Create a class. Create a class and name it however you want. In this article, it will be named NetworkAppExample.
    public class NetworkAppExample {
    
    }
    
  2. 2
    Create a main method. Create a main method and declare it might throw exceptions of Exception type and any subclass of it - all exceptions. This is considered a bad practice, but is acceptable for barebone examples.
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
    
        }
    }
    
    Advertisement
  3. 3
    Declare server address. This example will use local host address and an arbitrary port number. Port number needs to be in a range from 0 to 65535 (inclusive). However, port numbers to avoid range from 0 to 1023 (inclusive) because they are reserved system ports.
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
        }
    }
    
  4. 4
    Create a server. Server is bound to the address and port and listens for incoming connections. In Java, ServerSocket represents server-side endpoint and its function is accepting new connections. ServerSocket does not have streams for reading and sending data because it does not represent connection between a server and a client.
    import java.net.InetAddress;
    import java.net.ServerSocket;
    
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
    
            ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
        }
    }
    
  5. 5
    Log server inception. For logging purposes, print to the console that server has been started.
    import java.net.InetAddress;
    import java.net.ServerSocket;
    
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
    
            ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
            System.out.println("Server started.");
        }
    }
    
  6. 6
    Create a client. Client is bound to the address and port of a server and listens for packets (messages) after connection is established. In Java, Socket represents either a client-side endpoint connected to the server or a connection (from server) to client and is used to communicate with the party on the other end.
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
    
            ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
            System.out.println("Server started.");
            Socket client = new Socket(host, port);
        }
    }
    
  7. 7
    Log connection attempt. For logging purposes, print to the console that connection has been attempted.
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
    
            ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
            System.out.println("Server started.");
            Socket client = new Socket(host, port);
            System.out.println("Connecting to server...");
        }
    }
    
  8. 8
    Establish connection. Clients will never connect unless server listens for and accepts, in other words establishes, connections. In Java, connections are established using accept() method of ServerSocket class. The method will block execution until a client connects.
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
    
            ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
            System.out.println("Server started.");
            Socket client = new Socket(host, port);
            System.out.println("Connecting to server...");
            Socket connection = server.accept();
        }
    }
    
  9. 9
    Log established connection. For logging purposes, print to the console that connection between server and client has been established.
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
    
            ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
            System.out.println("Server started.");
            Socket client = new Socket(host, port);
            System.out.println("Connecting to server...");
            Socket connection = server.accept();
            System.out.println("Connection established.");
        }
    }
    
  10. 10
    Prepare communication streams. Communication is done over streams and, in this application, raw streams of (connection from) server (to client) and client need to be chained to either data or object streams. Remember, both parties need to use the same stream type.
    • Data streams
      import java.io.DataInputStream;
      import java.io.DataOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              DataOutputStream clientOut = new DataOutputStream(client.getOutputStream());
              DataInputStream clientIn = new DataInputStream(client.getInputStream());
              DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream());
              DataInputStream serverIn = new DataInputStream(connection.getInputStream());
          }
      }
      
    • Object streams
      When multiple object streams are used, input streams have to be initialized in the same order as output streams because ObjectOutputStream sends a header to the other party and ObjectInputStream blocks execution until it reads the header.
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream());
              ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream());
              ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream());
              ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream());
          }
      }
      

      Order as specified in the code above might be easier to remember - first initialize output streams then input streams in the same order. However, another order for initialization of object streams is the following:

      ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream());
      ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream());
      ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream());
      ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream());
      
  11. 11
    Log that communication is ready. For logging purposes, print to the console that communication is ready.
    // code omitted
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
    
            ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
            System.out.println("Server started.");
            Socket client = new Socket(host, port);
            System.out.println("Connecting to server...");
            Socket connection = server.accept();
            System.out.println("Connection established.");
    
            // code omitted
            System.out.println("Communication is ready.");
        }
    }
    
  12. 12
    Create a message. In this application, Hello World text will be sent to the server either as byte[] or String. Declare a variable of the type that depends on the stream used. Use byte[] for data streams and String for object streams.
    • Data streams
      Using data streams, serialization is done by converting objects into primitive data types or a String. In this case, String is converted to byte[] instead of written using writeBytes() method to show how it would be done with other objects, such as images or other files.
      import java.io.DataInputStream;
      import java.io.DataOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              DataOutputStream clientOut = new DataOutputStream(client.getOutputStream());
              DataInputStream clientIn = new DataInputStream(client.getInputStream());
              DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream());
              DataInputStream serverIn = new DataInputStream(connection.getInputStream());
              System.out.println("Communication is ready.");
      
              byte[] messageOut = "Hello World".getBytes();
          }
      }
      
    • Object streams
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream());
              ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream());
              ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream());
              ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream());
              System.out.println("Communication is ready.");
      
              String messageOut = "Hello World";
          }
      }
      
  13. 13
    Send the message. Write data to the output stream and flush the stream to ensure the data has been written entirely.
    • Data streams
      Length of a message needs to be sent first so the other party knows how many bytes it needs to read. After the length is sent as a primitive integer type, bytes can be sent.
      import java.io.DataInputStream;
      import java.io.DataOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              DataOutputStream clientOut = new DataOutputStream(client.getOutputStream());
              DataInputStream clientIn = new DataInputStream(client.getInputStream());
              DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream());
              DataInputStream serverIn = new DataInputStream(connection.getInputStream());
              System.out.println("Communication is ready.");
      
              byte[] messageOut = "Hello World".getBytes();
              clientOut.writeInt(messageOut.length);
              clientOut.write(messageOut);
              clientOut.flush();
          }
      }
      
    • Object streams
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream());
              ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream());
              ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream());
              ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream());
              System.out.println("Communication is ready.");
      
              String messageOut = "Hello World";
              clientOut.writeObject(messageOut);
              clientOut.flush();
          }
      }
      
  14. 14
    Log sent message. For logging purposes, print to the console that message has been sent.
    • Data streams
      import java.io.DataInputStream;
      import java.io.DataOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              DataOutputStream clientOut = new DataOutputStream(client.getOutputStream());
              DataInputStream clientIn = new DataInputStream(client.getInputStream());
              DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream());
              DataInputStream serverIn = new DataInputStream(connection.getInputStream());
              System.out.println("Communication is ready.");
      
              byte[] messageOut = "Hello World".getBytes();
              clientOut.writeInt(messageOut.length);
              clientOut.write(messageOut);
              clientOut.flush();
              System.out.println("Message sent to server: " + new String(messageOut));
          }
      }
      
    • Object streams
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream());
              ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream());
              ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream());
              ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream());
              System.out.println("Communication is ready.");
      
              String messageOut = "Hello World";
              clientOut.writeObject(messageOut);
              clientOut.flush();
              System.out.println("Message sent to server: " + messageOut);
          }
      }
      
  15. 15
    Read the message. Read data from the input stream and convert it. Since we know exactly the type of sent data, we will either create a String from byte[] or cast Object to String without checking, depending on the stream used.
    • Data streams
      As length was sent first and bytes afterward, reading has to be done in the same order. In case length is zero, there is nothing to read. Object is deserialized when bytes are converted back into an instance, in this case, of String.
      import java.io.DataInputStream;
      import java.io.DataOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              DataOutputStream clientOut = new DataOutputStream(client.getOutputStream());
              DataInputStream clientIn = new DataInputStream(client.getInputStream());
              DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream());
              DataInputStream serverIn = new DataInputStream(connection.getInputStream());
              System.out.println("Communication is ready.");
      
              byte[] messageOut = "Hello World".getBytes();
              clientOut.writeInt(messageOut.length);
              clientOut.write(messageOut);
              clientOut.flush();
              System.out.println("Message sent to server: " + new String(messageOut));
      
              int length = serverIn.readInt();
              if (length > 0) {
                  byte[] messageIn = new byte[length];
                  serverIn.readFully(messageIn, 0, messageIn.length);
              }
          }
      }
      
    • Object streams
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream());
              ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream());
              ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream());
              ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream());
              System.out.println("Communication is ready.");
      
              String messageOut = "Hello World";
              clientOut.writeObject(messageOut);
              clientOut.flush();
              System.out.println("Message sent to server: " + messageOut);
      
              String messageIn = (String) serverIn.readObject();
          }
      }
      
  16. 16
    Log read message. For logging purposes, print to the console that message has been received and print its content.
    • Data streams
      import java.io.DataInputStream;
      import java.io.DataOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              DataOutputStream clientOut = new DataOutputStream(client.getOutputStream());
              DataInputStream clientIn = new DataInputStream(client.getInputStream());
              DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream());
              DataInputStream serverIn = new DataInputStream(connection.getInputStream());
              System.out.println("Communication is ready.");
      
              byte[] messageOut = "Hello World".getBytes();
              clientOut.writeInt(messageOut.length);
              clientOut.write(messageOut);
              clientOut.flush();
              System.out.println("Message sent to server: " + new String(messageOut));
      
              int length = serverIn.readInt();
              if (length > 0) {
                  byte[] messageIn = new byte[length];
                  serverIn.readFully(messageIn, 0, messageIn.length);
                  System.out.println("Message received from client: " + new String(messageIn));
              }
          }
      }
      
    • Object streams
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      import java.net.InetAddress;
      import java.net.ServerSocket;
      import java.net.Socket;
      
      public class NetworkAppExample {
          public static void main(String[] args) throws Exception {
              String host = "localhost";
              int port = 10430;
      
              ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
              System.out.println("Server started.");
              Socket client = new Socket(host, port);
              System.out.println("Connecting to server...");
              Socket connection = server.accept();
              System.out.println("Connection established.");
      
              ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream());
              ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream());
              ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream());
              ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream());
              System.out.println("Communication is ready.");
      
              String messageOut = "Hello World";
              clientOut.writeObject(messageOut);
              clientOut.flush();
              System.out.println("Message sent to server: " + messageOut);
      
              String messageIn = (String) serverIn.readObject();
              System.out.println("Message received from client: " + messageIn);
          }
      }
      
  17. 17
    Disconnect connections. Connection is disconnected when one party closes its streams. In Java, by closing the output stream, associated socket and input stream are closed, as well. Once a party on the other end finds out connection is dead, it needs to close its output stream, as well, to prevent memory leaks.
    // code omitted
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
    
            ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
            System.out.println("Server started.");
            Socket client = new Socket(host, port);
            System.out.println("Connecting to server...");
            Socket connection = server.accept();
            System.out.println("Connection established.");
    
            // code omitted
            System.out.println("Communication is ready.");
    
            // code omitted
    
            clientOut.close();
            serverOut.close();
        }
    }
    
  18. 18
    Log disconnection. For logging purposes, print to the console connections have been disconnected.
    // code omitted
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
    
            ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
            System.out.println("Server started.");
            Socket client = new Socket(host, port);
            System.out.println("Connecting to server...");
            Socket connection = server.accept();
            System.out.println("Connection established.");
    
            // code omitted
            System.out.println("Communication is ready.");
    
            // code omitted
    
            clientOut.close();
            serverOut.close();
            System.out.println("Connections closed.");
        }
    }
    
  19. 19
    Terminate server. Connections are disconnected, but server is still up and running. As ServerSocket is not associated with any stream, it needs to be explicitly closed by calling close() method.
    // code omitted
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
    
            ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
            System.out.println("Server started.");
            Socket client = new Socket(host, port);
            System.out.println("Connecting to server...");
            Socket connection = server.accept();
            System.out.println("Connection established.");
    
            // code omitted
            System.out.println("Communication is ready.");
    
            // code omitted
    
            clientOut.close();
            serverOut.close();
            System.out.println("Connections closed.");
            server.close();
        }
    }
    
  20. 20
    Log server termination. For logging purposes, print to the console server has been terminated.
    // code omitted
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class NetworkAppExample {
        public static void main(String[] args) throws Exception {
            String host = "localhost";
            int port = 10430;
    
            ServerSocket server = new ServerSocket(port, 50, InetAddress.getByName(host));
            System.out.println("Server started.");
            Socket client = new Socket(host, port);
            System.out.println("Connecting to server...");
            Socket connection = server.accept();
            System.out.println("Connection established.");
    
            // code omitted
            System.out.println("Communication is ready.");
    
            // code omitted
    
            clientOut.close();
            serverOut.close();
            System.out.println("Connections closed.");
            server.close();
            System.out.println("Server terminated.");
        }
    }
    
  21. 21
    Compile and run. Logging enabled us to know if the application was successful or not. Expected output:
    Server started.
    Connecting to server...
    Connection established.
    Communication is ready.
    Message sent to server: Hello World
    Message received from client: Hello World
    Connections closed.
    Server terminated.
    

    In case your output is not like the one above, which is unlikely to happen, there are a few solutions:

    • If the output stops at the line Connection established. and object streams are used, flush each ObjectOutputStream immediately after initialization because headers, for some reason, were not sent.
    • If the output prints java.net.BindException: Address already in use, choose a different port number because the one specified is already used.
  22. Advertisement

Examples

Network applications that use blocking input/output need to use threads. The following examples show a minimalistic server and client implementation with threads. Networking code is essentially the same as in the article except some snippets were synchronized, moved into threads and exceptions are handled.

Server.java

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * The class {@code Server} represents a server end-point in a network. {@code Server} once bound to a certain IP
 * address and port, establishes connections with clients and is able to communicate with them or disconnect them.
 * <br><br>
 * This class is threadsafe.
 *
 * @version 1.0
 * @see Client
 * @see Connection
 */
public class Server implements Runnable {
    private ServerSocket server;
    private List<Connection> connections;
    private Thread thread;

    private final Object connectionsLock = new Object();

    /**
     * Constructs a {@code Server} that interacts with clients on the specified host name and port with the specified
     * requested maximum length of a queue of incoming clients.
     *
     * @param host Host address to use.
     * @param port Port number to use.
     * @param backlog Requested maximum length of the queue of incoming clients.
     * @throws NetworkException If error occurs while starting a server.
     */
    public Server(String host, int port, int backlog) throws NetworkException {
        try {
            server = new ServerSocket(port, backlog, InetAddress.getByName(host));
        } catch (UnknownHostException e) {
            throw new NetworkException("Host name could not be resolved: " + host, e);
        } catch (IllegalArgumentException e) {
            throw new NetworkException("Port number needs to be between 0 and 65535 (inclusive): " + port);
        } catch (IOException e) {
            throw new NetworkException("Server could not be started.", e);
        }
        connections = Collections.synchronizedList(new ArrayList<>());
        thread = new Thread(this);
        thread.start();
    }

    /**
     * Constructs a {@code Server} that interacts with clients on the specified host name and port.
     *
     * @param host Host address to bind.
     * @param port Port number to bind.
     * @throws NetworkException If errors occurs while starting a server.
     */
    public Server(String host, int port) throws NetworkException {
        this(host, port, 50);
    }

    /**
     * Listens for, accepts and registers incoming connections from clients.
     */
    @Override
    public void run() {
        while (!server.isClosed()) {
            try {
                connections.add(new Connection(server.accept()));
            } catch (SocketException e) {
                if (!e.getMessage().equals("Socket closed")) {
                    e.printStackTrace();
                }
            } catch (NetworkException | IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Sends data to all registered clients.
     *
     * @param data Data to send.
     * @throws IllegalStateException If writing data is attempted when server is offline.
     * @throws IllegalArgumentException If data to send is null.
     */
    public void broadcast(Object data) {
        if (server.isClosed()) {
            throw new IllegalStateException("Data not sent, server is offline.");
        }
        if (data == null) {
            throw new IllegalArgumentException("null data");
        }

        synchronized (connectionsLock) {
            for (Connection connection : connections) {
                try {
                    connection.send(data);
                    System.out.println("Data sent to client successfully.");
                } catch (NetworkException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * Sends a disconnection message and disconnects specified client.
     *
     * @param connection Client to disconnect.
     * @throws NetworkException If error occurs while closing connection.
     */
    public void disconnect(Connection connection) throws NetworkException {
        if (connections.remove(connection)) {
            connection.close();
        }
    }

    /**
     * Sends a disconnection message to all clients, disconnects them and terminates the server.
     */
    public void close() throws NetworkException {
        synchronized (connectionsLock) {
            for (Connection connection : connections) {
                try {
                    connection.close();
                } catch (NetworkException e) {
                    e.printStackTrace();
                }
            }
        }
        connections.clear();

        try {
            server.close();
        } catch (IOException e) {
            throw new NetworkException("Error while closing server.");
        } finally {
            thread.interrupt();
        }
    }

    /**
     * Returns whether or not the server is online.
     *
     * @return True if server is online. False, otherwise.
     */
    public boolean isOnline() {
        return !server.isClosed();
    }

    /**
     * Returns an array of registered clients.
     */
    public Connection[] getConnections() {
        synchronized (connectionsLock) {
            return connections.toArray(new Connection[connections.size()]);
        }
    }
}

Client.java

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

/**
 * The class {@code Client} represents a client end-point in a network. {@code Client}, once connected to a certain
 * server, is guaranteed to only be able to communicate with the server. Whether or not other clients receive the data
 * depends on the server implementation.
 * <br><br>
 * This class is threadsafe.
 *
 * @version 1.0
 * @see Server
 * @see Connection
 */
public class Client {
    private Connection connection;

    /**
     * Constructs a {@code Client} connected to the server on the specified host and port.
     *
     * @param host Host address to bind.
     * @param port Port number to bind.
     * @throws NetworkException If error occurs while starting a server.
     */
    public Client(String host, int port) throws NetworkException {
        try {
            connection = new Connection(new Socket(host, port));
        } catch (UnknownHostException e) {
            throw new NetworkException("Host name could not be resolved: " + host, e);
        } catch (IllegalArgumentException e) {
            throw new NetworkException("Port number needs to be between 0 and 65535 (inclusive): " + port);
        } catch (IOException e) {
            throw new NetworkException("Server could not be started.", e);
        }
    }

    /**
     * Sends data to the other party.
     *
     * @param data Data to send.
     * @throws NetworkException If writing to output stream fails.
     * @throws IllegalStateException If writing data is attempted when connection is closed.
     * @throws IllegalArgumentException If data to send is null.
     * @throws UnsupportedOperationException If unsupported data type is attempted to be sent.
     */
    public void send(Object data) throws NetworkException {
        connection.send(data);
    }

    /**
     * Sends a disconnection message to, and closes connection with, the server.
     */
    public void close() throws NetworkException {
        connection.close();
    }

    /**
     * Returns whether or not the client is connected to the server.
     *
     * @return True if client is connected. False, otherwise.
     */
    public boolean isOnline() {
        return connection.isConnected();
    }

    /**
     * Returns the {@link Connection} instance of the client.
     */
    public Connection getConnection() {
        return connection;
    }
}

Connection.java

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketException;

/**
 * The class {@code Connection} represents either a connection from server to client or a client end-point in a network
 * {@code Connection}, once connected, is able to exchange data with other party or parties, depending on a server
 * implementation.
 * <br><br>
 * This class is threadsafe.
 *
 * @version 1.0
 * @see Server
 * @see Client
 */
public class Connection implements Runnable {
    private Socket socket;
    private DataOutputStream out;
    private DataInputStream in;
    private Thread thread;

    private final Object writeLock = new Object();
    private final Object readLock = new Object();

    /**
     * Constructs {@code Connection} using streams of a specified {@link Socket}.
     *
     * @param socket Socket to fetch the streams from.
     */
    public Connection(Socket socket) throws NetworkException {
        if (socket == null) {
            throw new IllegalArgumentException("null socket");
        }

        this.socket = socket;
        try {
            out = new DataOutputStream(socket.getOutputStream());
        } catch (IOException e) {
            throw new NetworkException("Could not access output stream.", e);
        }
        try {
            in = new DataInputStream(socket.getInputStream());
        } catch (IOException e) {
            throw new NetworkException("Could not access input stream.", e);
        }
        thread = new Thread(this);
        thread.start();
    }

    /**
     * Reads messages while connection with the other party is alive.
     */
    @Override
    public void run() {
        while (!socket.isClosed()) {
            try {
                int identifier;
                byte[] bytes;
                synchronized (readLock) {
                    identifier = in.readInt();
                    int length = in.readInt();
                    if (length > 0) {
                        bytes = new byte[length];
                        in.readFully(bytes, 0, bytes.length);
                    } else {
                        continue;
                    }
                }
                switch (identifier) {
                    case Identifier.INTERNAL:
                        String command = new String(bytes);
                        if (command.equals("disconnect")) {
                            if (!socket.isClosed()) {
                                System.out.println("Disconnection packet received.");
                                try {
                                    close();
                                } catch (NetworkException e) {
                                    return;
                                }
                            }
                        }
                        break;
                    case Identifier.TEXT:
                        System.out.println("Message received: " + new String(bytes));
                        break;
                    default:
                        System.out.println("Unrecognized data received.");
                }
            } catch (SocketException e) {
                if (!e.getMessage().equals("Socket closed")) {
                    e.printStackTrace();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Sends data to the other party.
     *
     * @param data Data to send.
     * @throws NetworkException If writing to output stream fails.
     * @throws IllegalStateException If writing data is attempted when connection is closed.
     * @throws IllegalArgumentException If data to send is null.
     * @throws UnsupportedOperationException If unsupported data type is attempted to be sent.
     */
    public void send(Object data) throws NetworkException {
        if (socket.isClosed()) {
            throw new IllegalStateException("Data not sent, connection is closed.");
        }
        if (data == null) {
            throw new IllegalArgumentException("null data");
        }

        int identifier;
        byte[] bytes;
        if (data instanceof String) {
            identifier = Identifier.TEXT;
            bytes = ((String) data).getBytes();
        } else {
            throw new UnsupportedOperationException("Unsupported data type: " + data.getClass());
        }
        try {
            synchronized (writeLock) {
                out.writeInt(identifier);
                out.writeInt(bytes.length);
                out.write(bytes);
                out.flush();
            }
        } catch (IOException e) {
            throw new NetworkException("Data could not be sent.", e);
        }
    }

    /**
     * Sends a disconnection message to, and closes connection with, the other party.
     */
    public void close() throws NetworkException {
        if (socket.isClosed()) {
            throw new IllegalStateException("Connection is already closed.");
        }

        try {
            byte[] message = "disconnect".getBytes();
            synchronized (writeLock) {
                out.writeInt(Identifier.INTERNAL);
                out.writeInt(message.length);
                out.write(message);
                out.flush();
            }
        } catch (IOException e) {
            System.out.println("Disconnection message could not be sent.");
        }

        try {
            synchronized (writeLock) {
                out.close();
            }
        } catch (IOException e) {
            throw new NetworkException("Error while closing connection.", e);
        } finally {
            thread.interrupt();
        }
    }

    /**
     * Returns whether or not the connection to the other party is alive.
     *
     * @return True if connection is alive. False, otherwise.
     */
    public boolean isConnected() {
        return !socket.isClosed();
    }
}

Identifier.java

/**
 * The class {@code Identifier} contains constants used by {@link Connection} for serializing and deserializing the data
 * sent over the network.
 *
 * @version 1.0
 * @see Connection
 */
public final class Identifier {
    /**
     * Identifier for internal messages.
     */
    public static final int INTERNAL = 1;
    /**
     * Identifier for textual messages.
     */
    public static final int TEXT = 2;
}

NetworkException.java

/**
 * The class {@code NetworkException} indicates an error related to network.
 */
public class NetworkException extends Exception {
    /**
     * Constructs a {@code NetworkException} with {@code null} as its message.
     */
    public NetworkException() {
    }

    /**
     * Constructs a {@code NetworkException} with the specified message.
     *
     * @param message A message to describe error.
     */
    public NetworkException(String message) {
        super(message);
    }

    /**
     * Constructs a {@code NetworkException} with the specified message and cause.
     *
     * @param message A message to describe error.
     * @param cause A cause of error.
     */
    public NetworkException(String message, Throwable cause) {
        super(message, cause);
    }

    /**
     * Constructs a {@code NetworkException} with the specified cause.
     *
     * @param cause A cause of error.
     */
    public NetworkException(Throwable cause) {
        super(cause);
    }
}

UsageExample.java

/**
 * The class {@code UsageExample} shows the usage of {@link Server} and {@link Client}. This examples uses
 * {@link Thread#sleep(long)} to ensure every segment is executed because quickly starting and closing causes some
 * segments to not execute.
 *
 * @version 1.0
 * @see Server
 * @see Client
 */
public class UsageExample {
    public static void main(String[] args) throws Exception {
        String host = "localhost";
        int port = 10430;

        Server server = new Server(host, port);
        Client client = new Client(host, port);
        Thread.sleep(100L);

        client.send("Hello.");
        server.broadcast("Hey, fella!");
        Thread.sleep(100L);

        server.disconnect(server.getConnections()[0]); // or client.close() to disconnect from client-side
        server.close();
    }
}


About This Article

wikiHow is a “wiki,” similar to Wikipedia, which means that many of our articles are co-written by multiple authors. To create this article, 10 people, some anonymous, worked to edit and improve it over time. This article has been viewed 28,936 times.
How helpful is this?
Co-authors: 10
Updated: February 19, 2022
Views: 28,936
Categories: Java
Advertisement