Sockets

What's a Socket?

A socket is way of connecting to another machine. The internet is basically built out of sockets. Sockets are relatively painless in Java, as opposed to other languages like C. This is because Java comes with a standard Socket class that defaults to all the right settings for what you normally would want to do.

There are two main protocols used on the internet. The first, which is what we'll use, is TCP/IP (Transmission Control Protocal / Internet Protocol). This is a connection-oriented protocol, which works just like a telepone. One program (called the client) requests a connection from the other program (called the server), who "answers" by accepting the connection. Once the connection is thus established, both parties can "talk" by sending bytes to the other. This is a very reliable protocal. Extensive measures are taken to ensure that all bytes sent arrive and arrive in order.

The other protocol is UDP ("Unreliable Datagram Protocol"). This protocol does not keep a connection and does not take any measures to ensure that the bytes you send get there. It justs sends them out and hopes. The advantage of this protocol is that it has very little overhead and so can be much faster. We'll stick to TCP/IP.


The Socket and ServerSocket Classes

The Socket class is used to interact with a TCP/IP socket. On the client side (the program that "makes the call"), you construct it with two arguments, a string containing the hostname or IP address of the machine the server resides on, and an integer containing the port that that server is listening on. Any computer might have many connections to the outside and ports are just a way of enumerating these connections. Note that if there is no server on that machine at that port, the constructor will bail by throwing an IOException and if the hostname you give cannot be resolved to an IP address, it bails by throwing an UnknownHostException. You don't need to catch the second, but must be prepared to catch the first.

The server constructs a ServerSocket with one argument, an integer containing the port it should listen on. A call to the ServerSocket's accept method then waits for a client to connect, then returns a Socket to that client. Here you need to be prepared to catch an IOException also.

Once you are connected, then the communication is symmetric. You both have a socket with an InputStream and an OutputStream. This is like a phone - to start the call, someone calls and someone answers, but after you connect, you're both just talking. These streams are normal byte streams and can be wrapped with the usual Readers, Writers, etc.

What follows is an example of a specific case, a server that sits and waits for a connection and then writes whatever comes in over that connection to a logfile. The filename and port can be specified on the commandline, but default to "log" and 2468. The client connects to the server and sends out whatever is typed to the console. The server's host and port can be specified on the command line or default to port 2468 on the local machine. To finish the log entry, enter CTRL-D on an empty line and press return. This is the standard end-of-file.


The Client

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

public class LogClient
{
  public static void main(String[] args)
  {
    // host defaults to localhost and port to 2468
    String serverhost = (args.length == 0) ? "localhost" : args[0];
    int serverport = 2468;

    if (args.length > 1) 
      try { serverport = Integer.parseInt(args[1]); }
      catch(NumberFormatException e) {}
    
    System.out.print("connecting to server...");

    try
    {
      // connect to server
      Socket server = new Socket(serverhost,serverport);
      System.out.println("connected.  Enter log info.  End with EOF");

      // prepare for console input
      BufferedReader console = 
        new BufferedReader(new InputStreamReader(System.in));

      // prepare for output to server
      PrintWriter logOut = new PrintWriter(server.getOutputStream());

      String line;

      // read lines and write them to server until null
      while ((line = console.readLine()) != null)	
	logOut.println(line);

      logOut.flush();
      server.close();
    }
    catch(IOException e) {System.out.println(e);}
  }
}

The Server

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

public class LogServer
{
  public static void main(String[] args)
  {
    // logfile name defaults to log
    String logfile = (args.length == 0) ? "log" : args[0];

    // port defaults to 2468
    int port = 2468;
    if (args.length > 1)
      try { port = Integer.parseInt(args[1]); }
      catch(NumberFormatException e) {}

    try
    {
      // create listener for connections
      ServerSocket server = new ServerSocket(port);

      while (true)
      {
	// wait for connection from client
	Socket client = server.accept();

	System.out.println("new connection");

	//prepare text file output (append to file if exists)
	FileOutputStream textFileOut = new FileOutputStream(logfile,true);
	PrintWriter logOut = new PrintWriter(textFileOut);

	// prepare for input from client
	BufferedReader logIn = 
	  new BufferedReader( new InputStreamReader( 
            client.getInputStream() ) );

	String line;

	// read lines from client and write to file until null
	while ((line = logIn.readLine()) != null)
	  logOut.println(line);

	// close connection to client
	client.close();

	// close logfile
	logOut.flush();
	textFileOut.close();
      }
    }
    catch(IOException e) {System.out.println(e);}
  }
}