Second Summer 2002


Lecture Notes Twenty-Eight: Java Networking

We discuss Java networking and look at what it takes to develop a browser and a server.

First, we are going to write a network client.

/*
 * Copyright (c) 2000 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 2nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book (recommended),
 * visit http://www.davidflanagan.com/javaexamples2.
 */
import java.io.*;
import java.net.*;

/**
 * This program connects to a server at a specified host and port.
 * It reads text from the console and sends it to the server.
 * It reads text from the server and sends it to the console.
 **/
public class GenericClient {
    public static void main(String[] args) throws IOException {
        try {
            // Check the number of arguments
            if (args.length != 2) 
                throw new IllegalArgumentException("Wrong number of args");
	    
            // Parse the host and port specifications
            String host = args[0];
            int port = Integer.parseInt(args[1]);
            
            // Connect to the specified host and port
            Socket s = new Socket(host, port);
	    
            // Set up streams for reading from and writing to the server.
            // The from_server stream is final for use in the inner class below
            final Reader from_server=new InputStreamReader(s.getInputStream());
            PrintWriter to_server = new PrintWriter(s.getOutputStream());
            
            // Set up streams for reading from and writing to the console
            // The to_user stream is final for use in the anonymous class below
            BufferedReader from_user = 
                new BufferedReader(new InputStreamReader(System.in));
	    // Pass true for auto-flush on println()
	    final PrintWriter to_user = new PrintWriter(System.out, true);
            
            // Tell the user that we've connected
            to_user.println("Connected to " + s.getInetAddress() +
			    ":" + s.getPort());
            
            // Create a thread that gets output from the server and displays 
            // it to the user.  We use a separate thread for this so that we
            // can receive asynchronous output
            Thread t = new Thread() {
		    public void run() {
			char[] buffer = new char[1024];
			int chars_read;
			try { 
			    // Read characters until the stream closes
			    while((chars_read = from_server.read(buffer)) != -1) {
				// Loop through the array of characters, and 
				// print them out, converting all \n characters
				// to the local platform's line terminator.
				// This could be more efficient, but it is probably
				// faster than the network is, which is good enough
				for(int i = 0; i < chars_read; i++) {
				    if (buffer[i] == '\n') to_user.println();
				    else to_user.print(buffer[i]);
				}
				to_user.flush();
			    }
			}
			catch (IOException e) { to_user.println(e); }
			
			// When the server closes the connection, the loop above
			// will end.  Tell the user what happened, and call
			// System.exit(), causing the main thread to exit along
			// with this one.
			to_user.println("Connection closed by server.");
			System.exit(0);
		    }
		};
            
            // We set the priority of the server-to-user thread above to be
            // one level higher than the main thread.  We shouldn't have to do
            // this, but on some operating systems, output sent to the console
            // doesn't appear when a thread at the same priority level is
            // blocked waiting for input from the console.
            t.setPriority(Thread.currentThread().getPriority() + 1);
            
            // Now start the server-to-user thread
            t.start();
            
            // In parallel, read the user's input and pass it on to the server.
            String line;
            while((line = from_user.readLine()) != null) {
                to_server.print(line + "\n");
                to_server.flush();
            }
            
            // If the user types a Ctrl-D (Unix) or Ctrl-Z (Windows) to end
            // their input, we'll get an EOF, and the loop above will exit.
            // When this happens, we stop the server-to-user thread and close
            // the socket.
	    
            s.close();
            to_user.println("Connection closed by client.");
	    System.exit(0);
        }
        // If anything goes wrong, print an error message
        catch (Exception e) { 
            System.err.println(e);
            System.err.println("Usage: java GenericClient <hostname> <port>");
        }
    }
}
Let's see how it works:
burrowww.cs.indiana.edu% pwd
/nfs/paca/home/user1/dgerman/browser/server
burrowww.cs.indiana.edu% ls -ld *
-rw-r--r--   1 dgerman  faculty      4749 Aug  6 23:43 GenericClient.java
burrowww.cs.indiana.edu% javac GenericClient.java
burrowww.cs.indiana.edu% java GenericClient burrowww.cs.indiana.edu 10200
Connected to burrowww.cs.indiana.edu/129.79.245.98:10200
GET /index.html 
<html>
  <head><title>Homework One Page</title></head>
  <body bgcolor=white>

  <center>
    <img src="http://www.cs.indiana.edu/images/academics.jpg">
  </center> <p> 

  SAMPLE HOMEWORK ONE PAGE: 
  Your first homework assignment is asking you to 

<ul> 

<li> install Apache, 
  and to 
<li> post a picture of yourself on the website, 

</ul> as I include above. 
  Please also provide links to a couple of other documents that
  describe your interests, and add more information to your page. 
  (For example my current interests are: 
<a href="http://www.cs.indiana.edu/classes/a201">A201</a> and 
<a href="http://www.cs.indiana.edu/classes/a348">A348</a>). 
Later you will provide links to your homework assignments from
this page. <p> 
  </body>
</html>
Connection closed by server.
burrowww.cs.indiana.edu% 
This is our (own) telnet. Now for the server part.

We start from this:

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

public class HTTPServer {
    public static void main(String[] args) throws Exception {
	final int httpd = Integer.parseInt(args[0]); // our port 
	ServerSocket ssock = new ServerSocket(httpd); 
	System.out.println("Have opened port " + httpd + " locally."); 
	Socket sock = ssock.accept(); 
	System.out.println("Client has made socket connection."); 
	OneConnection client = new OneConnection(sock); 
	String s = client.getRequest(); 
    }
}

class OneConnection {
    Socket sock; 
    BufferedReader in = null; 
    DataOutputStream out = null; 
    OneConnection(Socket sock) throws Exception {
	this.sock = sock;
	in = 
	    new BufferedReader
		(new InputStreamReader
		    (sock.getInputStream())); 
	out = new DataOutputStream(sock.getOutputStream()); 
    }
    String getRequest() throws Exception {
	String s = null; 
	while ((s = in.readLine()) != null) {
	    System.out.println("got: " + s); 
	}
	return s; 
    }
}
Let's see how we can use this.

We can start the server on tucotuco and connect from burrowww.

Here's a simultaneous set of screens, first the server side:

tucotuco.cs.indiana.edu% javac HTT*.java
tucotuco.cs.indiana.edu% java HTTPServer 12345
Have opened port 12345 locally.
Client has made socket connection.
got: bye
got: I am here
got: klsjsajdasldkja
got: 
got: 
got: asdkjasjdaslkd
tucotuco.cs.indiana.edu% 
And the client side:
burrowww.cs.indiana.edu% java GenericClient tucotuco.cs.indiana.edu 12345
Connected to tucotuco.cs.indiana.edu/129.79.245.110:12345
bye
I am here
klsjsajdasldkja


asdkjasjdaslkd
^Cburrowww.cs.indiana.edu% 
So far, so good.

We, of course, can also make a connection using a web browser.

tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user1/dgerman/browser/server
tucotuco.cs.indiana.edu% java HTTPServer 12345
Have opened port 12345 locally.
Client has made socket connection.
got: GET / HTTP/1.0
got: Connection: Keep-Alive
got: User-Agent: Mozilla/4.79 [en] (X11; U; SunOS 5.8 sun4u)
got: Host: tucotuco.cs.indiana.edu:12345
got: Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
got: Accept-Encoding: gzip
got: Accept-Language: en
got: Accept-Charset: iso-8859-1,*,utf-8
got: 

Of, course, we don't show the browser.

But you can try, as well, and in the process see the HTTP headers.

Let's teach our server a little HTTP:

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

public class HTTPServer {
    public static void main(String[] args) throws Exception {
	final int httpd = Integer.parseInt(args[0]); // our port 
	ServerSocket ssock = new ServerSocket(httpd); 
	System.out.println("Have opened port " + httpd + " locally."); 
	Socket sock = ssock.accept(); 
	System.out.println("Client has made socket connection."); 
	OneConnection_A client = new OneConnection_A(sock); 
	String s = client.getRequest(); 
    }
}

class OneConnection {
    Socket sock; 
    BufferedReader in = null; 
    DataOutputStream out = null; 
    OneConnection(Socket sock) throws Exception {
	this.sock = sock;
	in = 
	    new BufferedReader
		(new InputStreamReader
		    (sock.getInputStream())); 
	out = new DataOutputStream(sock.getOutputStream()); 
    }
    String getRequest() throws Exception {
	String s = null; 
	while ((s = in.readLine()) != null) {
	    System.out.println("got: " + s); 
	}
	return s; 
    }
}

class OneConnection_A extends OneConnection {
    OneConnection_A (Socket sock) throws Exception {
	super(sock); 
    }
    String getRequest() throws Exception {
	String s = null; 
	while ((s = in.readLine()) != null) {
	    System.out.println("got: " + s); 
	    if (s.indexOf("GET") > -1) {
		out.writeBytes("HTTP-1.0 200 OK\r\n"); 
		s = s.substring(4); 
		int i = s.indexOf(" "); 
		System.out.println("file: " + s.substring(0, i)); 
		return s.substring(0, i); 
	    }
	}
	return null; 
    }
}
Here's what happens if you start it and call with
http://tucotuco.cs.indiana.edu:12345
from Netscape (or some other browser):
tucotuco.cs.indiana.edu% pwd
/nfs/paca/home/user1/dgerman/browser/server
tucotuco.cs.indiana.edu% ls -ld *
-rw-r--r--   1 dgerman  faculty      4749 Aug  6 23:43 GenericClient.java
-rw-r--r--   1 dgerman  faculty      1477 Aug  7 00:36 HTTPServer.java
tucotuco.cs.indiana.edu% javac *Server*.java
tucotuco.cs.indiana.edu% java HTTPServer 12345
Have opened port 12345 locally.
Client has made socket connection.
got: GET / HTTP/1.0
file: /
tucotuco.cs.indiana.edu%
Now we're getting so much closer:
import java.io.*; 
import java.net.*; 

public class HTTPServer {
    public static void main(String[] args) throws Exception {
	final int httpd = Integer.parseInt(args[0]); // our port 
	ServerSocket ssock = new ServerSocket(httpd); 
	System.out.println("Have opened port " + httpd + " locally."); 
	Socket sock = ssock.accept(); 
	System.out.println("Client has made socket connection."); 
	OneConnection_B client = new OneConnection_B(sock); 
	String s = client.getRequest(); 
        client.sendFile(s); 
    }
}

class OneConnection {
    Socket sock; 
    BufferedReader in = null; 
    DataOutputStream out = null; 
    OneConnection(Socket sock) throws Exception {
	this.sock = sock;
	in = 
	    new BufferedReader
		(new InputStreamReader
		    (sock.getInputStream())); 
	out = new DataOutputStream(sock.getOutputStream()); 
    }
    String getRequest() throws Exception {
	String s = null; 
	while ((s = in.readLine()) != null) {
	    System.out.println("got: " + s); 
	}
	return s; 
    }
}

class OneConnection_A extends OneConnection {
    OneConnection_A (Socket sock) throws Exception {
	super(sock); 
    }
    String getRequest() throws Exception {
	String s = null; 
	while ((s = in.readLine()) != null) {
	    System.out.println("got: " + s); 
	    if (s.indexOf("GET") > -1) {
		out.writeBytes("HTTP-1.0 200 OK\r\n"); 
		s = s.substring(4); 
		int i = s.indexOf(" "); 
		System.out.println("file: " + s.substring(0, i)); 
		return s.substring(0, i); 
	    }
	}
	return null; 
    }
}

class OneConnection_B extends OneConnection_A {
    OneConnection_B(Socket sock) throws Exception {
	super(sock); 
    }
    void sendFile(String fname) throws Exception {
	String where = "htdocs" + fname; 
	if (where.indexOf("..") > -1) 
	    throw new SecurityException("No access to parent dirs"); 
	System.out.println("Looking for " + where); 
        File f = new File(where); 
	DataInputStream din = new DataInputStream(new FileInputStream(f)); 
	int len = (int) f.length(); 
	byte[] buf = new byte[len]; 
	din.readFully(buf);
	out.writeBytes("Content-Length: " + len + "\r\n"); 
	out.writeBytes("Content-type: text/html\r\n\r\n"); 
	out.write(buf); 
	out.flush();
	out.close(); 
    }
}
Assume now you call
http://tucotuco.cs.indiana.edu:12345/one.html
using Netscape. Then the result is as follows.

First, the server reports all:

tucotuco.cs.indiana.edu% javac HTTPServer.java
tucotuco.cs.indiana.edu% du -a htdocs
1       htdocs/one.html
2       htdocs
tucotuco.cs.indiana.edu% cat htdocs/one.html
This is a very
simple file, 
               really... 
:-)

tucotuco.cs.indiana.edu% java HTTPServer 12345
Have opened port 12345 locally.
Client has made socket connection.
got: GET /one.html HTTP/1.0
file: /one.html
Looking for htdocs/one.html
tucotuco.cs.indiana.edu% 
Then, the file is shown in the browser.

(One can also verify that with our GenericClient).

Last improvement we need to make: turn the server in a multithreaded server.

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

public class HTTPServer {
    public static void main(String[] args) throws Exception {

	final int httpd = Integer.parseInt(args[0]); // our port 

	ServerSocket ssock = new ServerSocket(httpd); 

	while (true) {
	    Socket sock = ssock.accept(); 
	    System.out.println("** Client has made socket connection.");
	    OneConnection_C client = new OneConnection_C(sock); 
	    new Thread(client).start(); 
	}
    }
}

class OneConnection {
    Socket sock; 
    BufferedReader in = null; 
    DataOutputStream out = null; 
    OneConnection(Socket sock) throws Exception {
	this.sock = sock;
	in = 
	    new BufferedReader
		(new InputStreamReader
		    (sock.getInputStream())); 
	out = new DataOutputStream(sock.getOutputStream()); 
    }
    String getRequest() throws Exception {
	String s = null; 
	while ((s = in.readLine()) != null) {
	    System.out.println("got: " + s); 
	}
	return s; 
    }
}

class OneConnection_A extends OneConnection {
    OneConnection_A (Socket sock) throws Exception {
	super(sock); 
    }
    String getRequest() throws Exception {
	String s = null; 
	while ((s = in.readLine()) != null) {
	    System.out.println("got: " + s); 
	    if (s.indexOf("GET") > -1) {
		out.writeBytes("HTTP-1.0 200 OK\r\n"); 
		s = s.substring(4); 
		int i = s.indexOf(" "); 
		System.out.println("file: " + s.substring(0, i)); 
		return s.substring(0, i); 
	    }
	}
	return null; 
    }
}

class OneConnection_B extends OneConnection_A {
    OneConnection_B(Socket sock) throws Exception {
	super(sock); 
    }
    void sendFile(String fname) throws Exception {
	String where = "htdocs" + fname; 
	if (where.indexOf("..") > -1) 
	    throw new SecurityException("No access to parent dirs"); 
	System.out.println("Looking for " + where); 
        File f = new File(where); 
	DataInputStream din = new DataInputStream(new FileInputStream(f)); 
	int len = (int) f.length(); 
	byte[] buf = new byte[len]; 
	din.readFully(buf);
	out.writeBytes("Content-Length: " + len + "\r\n"); 
	out.writeBytes("Content-type: text/html\r\n\r\n"); 
	out.write(buf); 
	out.flush();
	out.close(); 
    }
}

class OneConnection_C extends OneConnection_B implements Runnable {
    OneConnection_C(Socket sock) throws Exception {
	super(sock); 
    }
    public void run() {
	try {
	    String filename = getRequest(); 
	    sendFile(filename); 
	} catch (Exception e) {
	    System.out.println("Exception: " + e); 
	}
    }
}
This way it can receive and service more than one call.

tucotuco.cs.indiana.edu% java HTTPServer 12345
** Client has made socket connection.
got: GET /one.html HTTP/1.0
file: /one.html
Looking for htdocs/one.html
** Client has made socket connection.
got: GET /two.html HTTP/1.0
file: /two.html
Looking for htdocs/two.html
Exception: java.io.FileNotFoundException: htdocs/two.html (No such file or directory)
** Client has made socket connection.
got: GET /one.html HTTP/1.0
file: /one.html
Looking for htdocs/one.html
^Ctucotuco.cs.indiana.edu%
The output above was generated by calling the server three times:
http://tucotuco.cs.indiana.edu:12345/one.html
http://tucotuco.cs.indiana.edu:12345/two.html
http://tucotuco.cs.indiana.edu:12345/one.html
One of the files is missing, hence the exception.

I have two more references for I/O and networking.


Last updated: Aug  6, 2002 by Adrian German for A348/A548