TCP

Java

Socket address type

Java has the type InetSocketAddress to represent an IP address and a port. There are several constructors including


      InetSocketAddress​(InetAddress addr, int port)
      InetSocketAddress​(String hostname, int port)
  
We won't use these much.

Client

A client creates a socket that is intended for connection to a server socket somewhere. The most common constructors are


public Socket​(String host,
              int port)
       throws UnknownHostException,
              IOException;
public Socket​(InetAddress address,
              int port)
       throws IOException
  

Once a socket is created, it exchanges messages with a server as in

The example that follows is a simple "echo" client, intended to connect to an "echo" server discussed later. The client read strings from the console, sends them to the server as binary data, which echoes them back to the client. The client then reads another line, etc. The client terminates the conection when the user types "BYE", and then the server closes its connection.

Converting between strings and bytes is discussed in detail in the chapter on Text handling. To make it simple here, we wrap the input and output streams in BufferedReader an PrintStream and read and write lines of text. The read and write routines will look after converting to bytes.

Java has an exception mechanism to catch errors. Sometimes this rather gets in the way. This version is one that basically ignores all possible errors and is not recommended BadEchoClient.java:


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

public class BadEchoClient {

    public static final int SERVER_PORT = 2000;
    public static String CR_LF = "\r\n";
    
    public static void main(String[] args){
	try {

	    InetAddress address = InetAddress.getByName(args[0]);

	    Socket sock = new Socket(address, SERVER_PORT);

	    InputStream in = sock.getInputStream();

	    OutputStream out = sock.getOutputStream();

	    BufferedReader socketReader = new BufferedReader(new InputStreamReader(in));

	    PrintStream socketWriter = new PrintStream(out);

	    BufferedReader consoleReader =  
		new BufferedReader(new InputStreamReader(System.in));

	    String line = null;
	    while (true) {
		System.out.print("Enter line:");
		line = consoleReader.readLine();
		if (line.equals("BYE")
		    break;
		socketWriter.println(line);
		System.out.println(socketReader.readLine());
	    }
	} catch (Exception e) {
	    System.out.println("Something went wrong somewhere");
	}
    } // BadEchoClient
}

But the version with appropriate error handling is rather gruesome. Is it necessary? Yes. Network programs are susceptible to far more errors than standalone programs. Ignore them at your peril. The revised client is EchoClient.java:


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

public class EchoClient {

    public static final int SERVER_PORT = 2000;
    
    public static void main(String[] args){

	if (args.length != 1) {
	    System.err.println("Usage: Client address");
	    System.exit(1);
	}

	InetAddress address = null;
	try {
	    address = InetAddress.getByName(args[0]);
	} catch(UnknownHostException e) {
	    e.printStackTrace();
	    System.exit(2);
	}

	Socket sock = null;
	try {
	    sock = new Socket(address, SERVER_PORT);
	} catch(IOException e) {
	    e.printStackTrace();
	    System.exit(3);
	}

	InputStream in = null;
	try {
	    in = sock.getInputStream();
	} catch(IOException e) {
	    e.printStackTrace();
	    System.exit(4);
	}

	OutputStream out = null;
	try {
	    out = sock.getOutputStream();
	} catch(IOException e) {
	    e.printStackTrace();
	    System.exit(5);
	}

	BufferedReader socketReader = new BufferedReader(new InputStreamReader(in));

	PrintStream socketWriter = new PrintStream(out);

	BufferedReader consoleReader =  
                   new BufferedReader(new InputStreamReader(System.in));

	String line = null;
	while (true) {
	    line = null;
	    try {
		System.out.print("Enter line:");
		line = consoleReader.readLine();
		System.out.format("Read line: '%s'\n", line);
	    } catch(IOException e) {
		e.printStackTrace();
		System.exit(6);
	    }

	    if (line.equals("BYE"))
		break;
	    
	    try {
		socketWriter.println(line);
	    } catch(Exception e) {
		e.printStackTrace();
		System.exit(7);
	    }

	    try {
		line = socketReader.readLine();
		System.out.format("Echoed: '%s'\n", line);
	    } catch(IOException e) {
		e.printStackTrace();
		System.exit(8);
	    }
	}
	
	System.exit(0);
    }
} // EchoClient

The commmand takes a single argument, a host identifier. This can be a hostname such as jan.newmarch.name, an IPv4 address such as 127.0.0.1 or an IPv4 address such as ::1. It is run by


      java EchoClient localhost
  

Server

A server creates a ServerSocket. A constructor can take a port number on which the server listens. It then goes into a blocking state waiting for a client to connect, using the method accept(). This returns with a Socket connected to the client. Input and output then takes place on this client socket. When finished, the client socket should be closed.

A ServerSocket on a dual stack server (both IPv4 and IPv6) will listen on both the IPv4 and IPv6 ports using an IPv6 server socket (see Oracle Networking IPv6 User Guide. This means that both IPv4 and IPv6 clients can connect without any code changes.

A echo server for the echo client follows. The first version is the BadEchoServer.java as it ignores all errors (but is readable):


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

public class BadEchoServer {
    
    public static int MYECHOPORT = 2000;

    public static void main(String argv[]) throws Exception {
	ServerSocket s = null;

	s = new ServerSocket(MYECHOPORT);

	while (true) {
	    Socket incoming = null;
	    incoming = s.accept();
	    System.out.println("Connected");
	    incoming.setSoTimeout(10000); // 10 seconds
		
	    handleSocket(incoming);
	}  
    }
    
    public static void handleSocket(Socket incoming) throws IOException {
	BufferedReader reader =
	    new BufferedReader(new InputStreamReader(
						     incoming.getInputStream()));
	PrintStream out =
	    new PrintStream(incoming.getOutputStream());
	
	boolean done = false;
	while ( ! done) {
	    String str = reader.readLine();
	    if (str == null) {
		done = true;
		System.out.println("Null received");
	    }
	    else {
		out.println(str);
		if (str.trim().equals("BYE")) {
		    done = true;
		    System.out.println("Disconnecting");
		    incoming.close();
		}
	    }	    
	}
    }
}

A version with better error handling is EchoServer.java. Note that when an error occurs in handling a client, the server does not exit, but carries on for the next client.


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

public class EchoServer {
    
    public static int MYECHOPORT = 2000;
    //public static String CR_LF = "\r\n";

    public static void main(String argv[]) {
	ServerSocket s = null;
	try {
	    s = new ServerSocket(MYECHOPORT);
	} catch(IOException e) {
	    System.out.println(e);
	    System.exit(1);
	}
	while (true) {
	    Socket incoming = null;
	    try {
		incoming = s.accept();
		System.out.println("Connected");
	    } catch(IOException e) {
		System.out.println(e);
		continue;
	    }

	    handleSocket(incoming);
	}
    }
    
    public static void handleSocket(Socket incoming) {

	byte[] data = new byte[1024];
	InputStream in;
	OutputStream out;
	try {
	    in = incoming.getInputStream();
	    out = incoming.getOutputStream();
	}  catch(IOException e) {
	    System.err.println(e.toString());
	    return;
	}
	int numRead = 0;

	try {
	    while (true) {
		try {
		    numRead = in.read(data);
		} catch(IOException e) {
		    System.err.println(e.toString());
		    return;
		}
		if (numRead == -1) {
		    System.out.println("Disconnecting");
		    return;
		}
		try {
		    out.write(data, 0, numRead);
		} catch(IOException e) {
		    System.err.println(e.toString());
		    return;
		}
	    }		
	} finally {
	    System.out.println("Closing all");
	    try {
		in.close();
		out.close();
		incoming.close();
	    } catch(IOException e) {
		// nothing to do really
	    }
	}
    }
}

Multi-threaded server

The echo server has a serious flaw: it can only handle one client at a time. If another tries to connect while the first is still active, it will get blocked. In java it is easy to run client handlers concurrently by using threads. The change is minor, creating a SocketHandler class which extends Thread: EchoServer.java:


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

public class MultiEchoServer {
    
    public static int MYECHOPORT = 2000;

    public static void main(String argv[]) {
	ServerSocket s = null;
	try {
	    s = new ServerSocket(MYECHOPORT);
	} catch(IOException e) {
	    System.out.println(e);
	    System.exit(1);
	}
	System.out.println("Listening");

	while (true) {
	    Socket incoming = null;
	    try {
		incoming = s.accept();
		System.out.println("Connected");
	    } catch(IOException e) {
		System.out.println(e);
		continue;
	    }

	    new SocketHandler(incoming).start();

	}  
    }
}

class SocketHandler extends Thread {

    Socket incoming;

    SocketHandler(Socket incoming) {
	this.incoming = incoming;
    }

    public void run() {
	byte[] data = new byte[1024];
	InputStream in;
	OutputStream out;
	try {
	    in = incoming.getInputStream();
	    out = incoming.getOutputStream();
	}  catch(IOException e) {
	    System.err.println(e.toString());
	    return;
	}
	int numRead = 0;

	try {
	    while (true) {
		try {
		    numRead = in.read(data);
		} catch(IOException e) {
		    System.err.println(e.toString());
		    return;
		}
		if (numRead == -1) {
		    System.out.println("Disconnecting");
		    return;
		}
		try {
		    out.write(data, 0, numRead);
		} catch(IOException e) {
		    System.err.println(e.toString());
		    return;
		}
	    }		
	} finally {
	    System.out.println("Closing all");
	    try {
		in.close();
		out.close();
		incoming.close();
	    } catch(IOException e) {
		// nothing to do really
	    }
	}
    }
}

Socket options

Resources


Copyright © Jan Newmarch, jan@newmarch.name
Creative Commons License
" Network Programming using Java, Go, Python, Rust, JavaScript and Julia" by Jan Newmarch is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License .
Based on a work at https://jan.newmarch.name/NetworkProgramming/ .

If you like this book, please contribute using PayPal