CM10135 / Programming II:   Lecture 16


How to Network


I.  Reading & Writing Across Sockets (from last time, but we didn't get to it)

  1. These examples are taken from Learning Java
    1. Incidently, Java Programming Today is at the bookstore & should be in the library very soon.
    2. I've talked to the librarian, and hopefully they'll have copies of Learning Java, Java Programming Today AND Object Oriented Programming with Java (which is a medium complexity book) before break.
    3. Which also reminds me, those of you who failed Programming I badly and have to rework the exam should do it over break.
      1. Will help you do better in Programming II as well!
      2. Problems may not be just with programming, but also with exam taking.
  2. You might also want to see Learning Java's complete sample programs for clients & servers, though there will be more examples further below, too.
  3. Notice that most of the code below is showing various ways to read & write to the socket, only one of which you'll need for coursework 2.
  4. Notice also though that you have to be careful to always read & write in the right order!
  5. It may still be easiest to use more than one socket...
// these examples are taken from Learning Java, pp. 335--337

// to open a client socket, we need to name the machine & the port the
// socket will be on. We also have to catch a couple exceptions...

try {
Socket sock = new Socket ("wupost.wustl.edu", 25);
} catch (UnknownHostException e) {
System.out.println("Can't find host.");
} catch ( IOException e) {
System.out.println("Error connecting to host.");
}

// Here's a client that reads & writes!

try {
Socket server = new Socket("foo.bar.com", 1234);
InputStream in = server.getInputStream();
Output Stream out = server.getOutputStream();

// write a byte
out.write(42);

// write a newline or carriage return delimited string
PrintWriter pout = new PrintWriter (out, true);
pout.println("Hello!");

//read a byte
byte back = (byte) in.read();

// read a newline or carriage return delimited string
BufferedReader bin =
new Buffered Reader (new Input StreamReader(in));
String response = bin.readLine();

// send a serialized java object
ObjectOutputStream oout = new ObjectOutputStream (out);
oout.writeObject(new java.util.Date());
oout.flush();
server.close();
} catch (IOException e) {...} // every one of these operations could have died!


// server code (for the same conversation! Notice these have to be in
// order...)

// assume this is running on the machine foo.bar.com

try {
// note: ServerSocket is a subclass of Socket that knows how to listen...
ServerSocket listener = new ServerSocket (1234);

while (!finished) {
Socket client = listener.accept (); // this waits for a connection

InputStream in = client.getInputStream();
OutputStrewam out = client.getOutputStream();

// read a byte
byte = someByte = (byte)in.read();

// read a newline or carriage-return-delimited string
BufferedReader bin =
new BufferedReader(new InputStreamReader(in));
String someString = bin.readLine();

// write a byte
out.write(43);

// say goodbye
PrintWriter pout = new PrintWriter (out, true);
p.out.println("Goodbye!");

// read that serialized Java object
ObjectInputStream oin = new ObjectInputStream(in);
Date date = (Date)oin.readObject();

client.close();
}

listener.close();

} catch (IOException e) {...}
catch (ClassNotFoundException e2) {...}


II. Clients for the date & echo services:

  1. These examples use standard internet services, which are provided by at least unix / linux computers.
  2. Of course, for security reasons, many of these services are by default turned off these days.
  3. Here's what I had to do on my linux box to turn them on:
    [root@sydney etc]# telnet
    localhost 7
    Trying 127.0.0.1...
    telnet: connect to address 127.0.0.1: Connection refused
    [root@sydney etc]# /sbin/chkconfig daytime on
    [root@sydney etc]# telnet localhost 15
    Trying 127.0.0.1...
    telnet: connect to address 127.0.0.1: Connection refused
    [root@sydney etc]# less services
    [root@sydney etc]# telnet localhost 13
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    17 MAR 2004 21:50:15 GMT
    Connection closed by foreign host.
    [root@sydney etc]# less services
    [root@sydney etc]# /sbin/chkconfig echo on
    [root@sydney etc]# telnet localhost 7
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    hi
    hi
    golf
    golf
  4. Notice a couple of things here:
    1. I've tapped into the server with a program called "telnet".  
      1. Telnet basically just lets you talk to ports directly. 
      2. It used to be used to login for sessions, but it's too insecure.  Nowadays we use ssh instead.
      3. I'll be showing you how you can use telnet to fake internet / application protocols this afternoon.
    2. Mostly because of security, but also because in class I won't be networked, I'm talking to my own machine as if it's a machine on the internet.
      1. Your own machine can be accessed with the special IP address 127.0.0.1
      2. it's DNS name is "localhost" (as well as any other name you've given it.)
      3. Again, I'll probably explain DNS & how the internet routes packets this afternoon.
    3. I happen to be in the /etc directory, so the file that tells you the right port number for things is just "services".
    4. I have to be root to turn the services on.
  5. Assuming you can find a computer that will let you talk to its daytime server, then you can run this program (which is taken with only a slight modification from Object-Oriented Programming with Java by David Barnes, Chapter 19 (on Networking!):
    import java.net.*;
    import java.io.*;

    // Contact the daytime server running on hostname.
    public class DaytimeClient {
    // Contact the server at the appropriate port and set
    // the socket attribute for the run() method to use.
    public DaytimeClient(String hostname) throws Exception {
    // The well-known port of the TCP daytime service.
    final int DaytimePort = 13;
    try{
    socket = new Socket(hostname,DaytimePort);
    }
    catch(UnknownHostException e){
    throw new Exception("Unknown host: "+e.getMessage());
    }
    catch(IOException e){
    throw new Exception("IOException on socket creation: "+e.getMessage());
    }
    }

    // Obtain the time of day from the daytime server.
    public void run() {
    Socket daytime = getSocket();
    try{
    // Get the stream used by the server to send data.
    InputStream inStream = daytime.getInputStream();
    // Wrap the stream in a BufferedReader.
    BufferedReader reader = new BufferedReader(
    new InputStreamReader(inStream));

    // The server will only send a single line.
    String response = reader.readLine();
    System.out.println(response);
    // Close the connection now it is finished with.
    daytime.close();
    }
    catch(IOException e){
    System.err.println(e.getMessage());
    }
    }

    protected Socket getSocket(){
    return socket;
    }

    private final Socket socket;

    public static void main(String[] args) {
    if(args.length == 1){
    try{
    DaytimeClient client = new DaytimeClient(args[0]);
    client.run();
    }
    catch(Exception e){
    System.err.println("Exception: "+e.getMessage());
    }
    }
    else{
    System.err.println("You must supply the name of a host to contact.");
    }
    }
    }

  6. Here's one that talks to echo...

    /* author david barnes & joanna bryson
    *
    */
    import java.net.*;
    import java.io.*;


    // Contact the echo server running on hostname.
    class TCPEchoClient {
    // Contact the server at the appropriate port and set
    // the socket attribute for the run() method to use.
    public TCPEchoClient(String hostname) throws Exception {
    try{
    // The well-known port of the TCP echo service.
    final int EchoPort = 7;
    socket = new Socket(hostname,EchoPort);
    }
    catch(UnknownHostException e){
    throw new Exception("Unknown host: "+e.getMessage());
    }
    catch(IOException e){
    throw new Exception("IOException on socket creation: "
    +e.getMessage());
    }
    }

    protected Socket getSocket(){
    return socket;
    }

    private final Socket socket;


    public static void main(String[] args) {

    try {
    TCPEchoClient eClient = new TCPEchoClient("localhost");

    // now you know stdin is really a socket too!
    BufferedReader stdin =
    new BufferedReader(new InputStreamReader(System.in));
    String myEchoString;

    System.out.print("Welcome to the echo client. The prompt looks like >>."
    + "\n >> ");

    // Wrap the input stream in a BufferedReader.
    BufferedReader reader = new BufferedReader(
    new InputStreamReader(eClient.getSocket().getInputStream()));
    // Wrap the output stream in a BufferedWriter.
    BufferedWriter writer = new BufferedWriter(
    new OutputStreamWriter(eClient.getSocket().getOutputStream()));

    // read a string from stdin & send it to echo
    while ((myEchoString=stdin.readLine()) != null) {
    System.out.println("Sending: "+myEchoString);

    writer.write(myEchoString);
    writer.newLine();
    // Make sure the data is flushed to the stream.
    writer.flush();

    // Read the response.
    String response = reader.readLine();
    System.out.println("The response is: "+response);
    System.out.print("\n >> ");
    } // while strings
    // Close the connection
    eClient.getSocket().close();
    }
    catch(IOException ioe){
    ioe.printStackTrace();
    }
    catch(Exception e) {
    e.printStackTrace();
    }

    } // main()

    } // class TCPEchoClient
  7. Sorry I didn't color those creatively, but if you look at them in emacs (or eclipse) it should color them for you.
    1. If your BUCS unix server emacs isn't making pretty colors for you, you should put this in your .emacs file
      ; stick colour in old versions of emacs --

      ;; Are we running XEmacs or Emacs?
      (defvar running-xemacs (string-match "XEmacs\\|Lucid" emacs-version))

      ;; Turn on font-lock mode for Emacs
      (cond ((not running-xemacs)
      (global-font-lock-mode t)
      ))

      ; -- ( colour stuff from Adam Dziedzic ) --
    2. Notice:  emacs is written in lisp!
    3. You might want to look at ~cssjjb/.emacs for more random stuff

III. Servers

  1. There are a couple differences with servers:
    1. They have to start running first.
    2. They need an extra port -- one to listen for clients.
  2. But once a server has made the connection to the client, it's really just the same.
  3. Here is yet more code from David Barnes -- this time I left the main as he wrote it (in another class) because I'm going to demo this for you off the command line.
  4. The server class
    import java.net.*;
    import java.io.*;

    // A simple server that accepts a client connection
    // and sends back the length of the strings it
    // receives from the client.
    class LineLengthServer {
    public LineLengthServer(int port) throws Exception {
    try{
    // Listen on the given port.
    serverSocket = new ServerSocket(port);
    }
    catch(BindException e){
    throw new Exception("Failed to create a server socket: "+
    e.getMessage());
    }
    }

    // Read strings and return their length (as a String)
    // to the client.
    public void run() throws Exception {
    ServerSocket serverSocket = getServerSocket();
    Socket clientSocket = null;
    try{
    System.out.println("Listening for a client on port: "+
    serverSocket.getLocalPort());
    // Wait for a client to make contact.
    clientSocket = serverSocket.accept();
    // Contact ...
    System.out.println("A client has arrived.");

    // Wrap the input stream in a BufferedReader.
    BufferedReader reader = new BufferedReader(
    new InputStreamReader(clientSocket.getInputStream()));
    // Wrap the output stream in a BufferedWriter.
    BufferedWriter writer = new BufferedWriter(
    new OutputStreamWriter(clientSocket.getOutputStream()));

    // Read lines until the client terminates.
    String request = reader.readLine();
    while(request != null){
    // Write the length of the line as a String.
    writer.write(String.valueOf(request.length()));
    writer.newLine();
    writer.flush();
    request = reader.readLine();
    }
    }
    catch(IOException e){
    throw new Exception("IOException talking to the client: "+
    e.getMessage());
    }
    finally{
    if(clientSocket != null){
    System.out.println("The client has gone.");
    // Close the socket to the client.
    clientSocket.close();
    }
    }
    serverSocket.close();
    }

    protected ServerSocket getServerSocket(){
    return serverSocket;
    }

    // The socket on which the listening is done.
    private final ServerSocket serverSocket;
    }
  5. The "main" class
            // An example of a simple server that reads lines from
    // clients and sends back the length of each line
    // it receives.
    public class LineLengthServerMain {
    public static void main(String[] args){
    try{
    // An arbitrary port number to listen on. This should be
    // larger than 1024 for user-written servers.
    final int port = 24101;
    LineLengthServer server = new LineLengthServer(port);
    server.run();
    }
    catch(Exception e){
    System.err.println("Exception: "+e.getMessage());
    }
    }
    }

  6. Running it from linux
    [joanna@sydney Networks]$ javac LineLengthServerMain.java
    [joanna@sydney Networks]$ java LineLengthServer
    Exception in thread "main" java.lang.NoSuchMethodError: main
    [joanna@sydney Networks]$ java LineLengthServerMain
    Listening for a client on port: 24101

    [2]+ Stopped java LineLengthServerMain
    [joanna@sydney Networks]$ bg
    [2]+ java LineLengthServerMain &
    [joanna@sydney Networks]$ telnet localhost 24101
    Trying 127.0.0.1...
    A client has arrived.
    Connected to localhost.
    Escape character is '^]'.
    hi there
    8
    have you ever thought
    21
    this isn't really measuring the line length...
    46
    Ah ha!
    6
    ^]

    telnet> .
    ?Invalid command
    telnet> quit
    Connection closed.
    The client has gone.
    [2]+ Done java LineLengthServerMain
    [joanna@sydney Networks]$
    1. Notice I used telnet as a client again
    2. And also, I stopped the server process (with ^Z) and then backgrounded it (made it run, but leave me stdin to use on another program).
    3. Consequently, both programs were printing to the screen at the same time -- telent I made blue, the server red, and what I typed green.
    4. I also forgot the telnet protocol / commands -- I thought you killed it with a .
  7. If you want to make a server accept more than one connection, just put a while loop in so it looks again (see the outlined server back in section I.)
  8. If you want to have the server accept new clients while it's still servicing old ones, you need to use threads!
  9. Here's an example of that, again from Barnes (hey, he writes good examples! This is from RandomServiceListener)
        public void listen() throws Exception {
    ServerSocket serverSocket = getServerSocket();
    // Wait up to 30 minutes for new clients.
    final int timeToWait = 1*60*1000;
    boolean keepWaiting = true;

    while(keepWaiting){
    try{
    // Wait for a client to make contact.
    serverSocket.setSoTimeout(timeToWait);
    Socket clientSocket = serverSocket.accept();
    // Let a separate Thread handle it.
    new Thread(new RandomService(clientSocket)).start();
    }
    catch(InterruptedIOException e){
    // We timed out waiting for a client.
    keepWaiting = false;
    }
    catch(IOException e){
    throw new Exception("IOException: "+ e.getMessage());
    }
    }
    // No more clients, so close the socket.
    serverSocket.close();
    }
    1. Notice not only the threading, but the fact it handles interupts, so it can eventually be killed!
    2. It also sets a timeout so that it will effectively interrupt itself if it's been waiting too long (as well as accepting terminate signals, e.g. from the keyboard.)

IV.  Finding out what your local machine's address is:

  1. Another useful program potentially for your coursework, again from David Barnes (the bit that matters is in blue):
    // Print out Internet address information about the local machine.
    import java.net.*;

    public class PrintInetDetails {
    public static void main(String[] args){
    if(args.length == 0){
    try{
    InetAddress myDetails = InetAddress.getLocalHost();
    System.out.println("The local host is called: "+
    myDetails.getHostName()+
    " and its address is: "+
    myDetails.getHostAddress());
    }
    catch(UnknownHostException e){
    System.err.println("Unknown host: "+e.getMessage());
    }
    }
    else{
    for(int i = 0; i < args.length; i++){
    try{
    InetAddress details = InetAddress.getByName(args[i]);
    System.out.println("The host is called: "+
    details.getHostName()+
    " and its address is: "+
    details.getHostAddress());
    }
    catch(UnknownHostException e){
    System.err.println("Unknown host: "+e.getMessage());
    }
    }
    }
    }
    }

  2. When/if I show it to you running on my laptop, it only says lamely the standard localhost address, but here's what it does on my desktop:
    [jjb@jjb Networks]$ java PrintInetDetails
    The local host is called: jjb.cs.bath.ac.uk and its address is: 172.16.33.1
    [jjb@jjb Networks]$ java PrintInetDetails horsepower.ai.mit.edu soggy-fibers.ai.mit.edu
    The host is called: horsepower.ai.mit.edu and its address is: 128.52.37.26
    The host is called: soggy-fibers.ai.mit.edu and its address is: 128.52.32.78
    [jjb@jjb Networks]$ java PrintInetDetails wjh.harvard.edu www.google.com
    The host is called: wjh.harvard.edu and its address is: 140.247.94.249
    The host is called: www.google.com and its address is: 66.102.11.99
  3. Notice that the two MIT AI lab addresses are very similar -- again, more on that this afternoon.

V. Summary

  1. Mostly a lot of examples of clients & servers
  2. A little bit of a feel for how to use unix/linux, and how telnet can be used to fake one side of a client.
  3. This afternoon, no code, just how stuff works.

page author: Joanna Bryson
17 March 2004