CM10135 / Programming II:   Lecture 15


How to Network


-I. Coursework 2 & Iterative Development.

  1. People last year were overly worried about CW2.  I want to address some possible falicies:
    1. Q: CW2 involves knowing and doing too many different things!
      1. A:  CW2 only suggests doing a lot of things.  You only need to do 3 or 4 of them to get an excellent mark.
    2. Q: you need to already know Java to do well on CW2.
      1. A: No, you can learn enough to do well if you've been keeping up with lectures & you have been putting the time you are supposed to (about 7 hours a week) into the course work.
      2. A: The reason there are so many different things you can do is that you can pick which parts of the most recent content are most interesting to you & try to get extra marks with them.  Though some things are mandatory.
    3. Q:  But it does help if you already know how to do Java.
      1. A:  Yes, and it helps your mark in Spanish if you're already fluent in Spanish / go to Spain every summer.
        1. That's the way it ought to be --- the whole point of assessing coursework is to see if you know how to program, not to find out where you learned!
        2. Though that is one reason why MIT used to teach people Scheme instead of a  more popular language.
    4. Q:  Now you've dumbed down CW2 too much, it's too easy!
      1. A:  It was always meant to be pretty easy, though I did make it easier to get 40% (threads are now optional.)
      2. A: The point isn't just to get marks.
        1. The main point is to learn.  That's why you're here.
        2. If you build something cooler than you can get marks for, you can always put it on your web page & show it off to potential employers, your parents, or (geeky) dates.
  2. The most important thing for tackling a big problem like CW2 is to develop the code iteratively.
    1. Think about what you need to do & what you want to do.
    2. Figure out a piece you can do first, that you can test without having gotten everything else working.
    3. Build that piece.
    4. Test that piece & fix it until it works.
    5. Now go back to step I.2.1!
      1. You'll have learned things about the problem and the solution, so you want to think about the problem again before you choose the next piece to do.
  3. Don't forget this is a double unit.
    1. Counts for twice as much as your other units.
    2. Should get twice as much of your time.
  4. Programming is an art.  Practicing is the only way to get better.

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

  1. These examples are taken from Learning Java
  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. However, for security reasons, many of these services are by default turned off these days.
  3. If you have installed linux on your own computer, you might want to turn these on & play with them. 
    1. However, note that you should probably turn them back off again before plugging your computer back into the internet if you do!
    2. Note that this has to be your own linux box, because you need superuser (root) access to do this.
      1.  If you don't know what this means don't worry -- there are other examples below that don't require this, that you can run, like the linelength server.
      2. But do still have a look at what the servers do, so you can understand the code of what a client does that talks to it.
    3. Here's what I had to do to turn them on for my linux box:
      [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 did on my desktop last year (alas, ai.mit.edu is no more!):
    [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 in April.

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
16 March 2005