Internet and Distributed Processing

Many interesting and wide spread applications depend on the Internet (process to process communication) for their functionality. Examples include:

Most of these applications follow the client/server architecture. Bittorrent follows the peer-to-peer architecture.

Client/Server architecture

The client/server architecture separates the application into a:

server
an application that waits at a well known network address for a client to connect and make a request.
and a client
an application that usually connects to a server to request services for a user. The client contains a user interface.

Layered architecture

Layer-n
Layer-(n-1)
Layer-(n-2)
...
Layer-1

The software that runs the Internet is based on a layered architecture. The software is partitioned into packages grouped into layers. The components are only coupled to their adjacent layers. Layer-n only knows about layer-(n-1). Layer-i, where i is in the middle, knows about layer-(i+1) and layer-(i-1).

Layered architectures are very common in software systems. The system calls provided by an operating system is usually the bottommost layer. System libraries are built upon system calls, and end user application are build on system libraries.

In Java, the JVM is the bottommost layer, the Java API is next, the user's application is the topmost layer.

Layered architecture

Application
Transport
Network
Data link
Physical

The Internet is often described as a 5 layered architecture.

Application
This layer contains the end user application. Web browsers and web servers are typical application at the layer. These applications use the services provided by the transport layer.
Transport
The transport layer provides:
  • a reliable end-to-end communication
  • an unreliable datagram communication
Network
Routing of packets between nodes is the main service provided by this layer.
Data link
The data link layer provides reliable node to node communication.
Physical
The physical layer is responsible for the actual communication. Communication is done with electromagnetic signals.

Layers and network interconnections

Home, academic, and business networks are connected with: hubs, switches, routers and gateways.

Term definitions

host
a computer connected to the Internet, normally does not route messages.
IP
Internet Protocol, defines how messages are routed between nodes.
packets, datagrams
a fixed sized chunk of data sent between nodes.
TCP
Transmission Control Protocol, a protocol that provides reliable delivery of data streams using datagrams.
UDP
User Datagram Protocol, the protocol for delivery on datagrams between hosts. A UDP datagram can be one to many.
IP number
A 4 byte (32-bit) network address. Each datagram contains a source and destination IP number. IP numbers are often expressed as four decimal integers separated by periods (134.153.1.1).
domain name
A symbolic name that is mapped to an IP number by a DNS server.
port number
Communication on the Internet occurs between programs that run on the attached hosts. While IP number identifies the host, the port number identifies which process running on the host is to receive the datagram. All datagrams include the port number of the sending and the port number of the receiver program. Port numbers use 16 bits.
socket
An abstraction of one end of the network connection developed by Berkeley. It provides the connection between the transport layer and the application layer. Sockets are identified with an IP and port number. A socket connection can use either TCP or UDP. A TCP connection contains outgoing and incoming data. These two streams are independent.

Well known port numbers

Certain servers are assigned to well known port numbers. Some examples are:

On Unix systems, the file /etc/services contains a list of well known services and port numbers.

Java Network API

The java.net package contains the Java application programmers interface to the transport layer of the Internet. Most of the concepts used in the Internet have equivalent classes in the Java package. Some of the important ones are:

java.net.InetAddress
represents an Internet address. This class is the superclass of Inet4Address and Inet6Address. DNS services are also provided.
java.net.ServerSocket
listens for incoming connections.
Socket
provides a TCP connection. Each Socket has an input stream and an output stream.
java.net.DatagramSocket
provides an UDP end point. This socket can send and receive datagrams.
java.net.DatagramPacket
a container for data and network address.

InetAddress

The static method, getAllByName, can be used to translate a domain name into an IP address. An exception gets thrown if the domain name lookup fails. If the IP number cannot be found, then the original string is returned. A Java program that uses this class is:

NSLookup.java
import java.net.InetAddress;
import java.net.UnknownHostException;

public class NSLookup {
    public static void main( String[] args ) {
        if ( args.length != 1 ) {
            System.out.println( "usage: java NSLookup host-name" );
            System.exit( 1 ); // indicate an error
        }
        try {
            InetAddress[] addrs =
                InetAddress.getAllByName( args[0] );
            for ( InetAddress addr : addrs ) {
                System.out.println( addr.getHostAddress() );
            }
        }
        catch(  UnknownHostException ex ) {
            System.out.println( ex.getMessage() );
            System.exit( 1 ); 
        }
    }
}

Line Server/Client

Construct a client program that transmits lines to a server program across the Internet. The server program should capitialize the lines and send them back to the client.

LineServer

The LineServer class demonstrates the network API by accepted incoming connections and echoing any incoming data after converting it to upper case. After the connection is dropped the server waits for another client to connect.

A server socket with an OS picked port is created with:

	    ServerSocket listen = new ServerSocket( 0 );

The server's port is found with:

	    System.out.println("Server port is " + listen.getLocalPort() );

The server waits for a connection with the following. This method blocks until a connection is made by a client. Notice that the listen socket is only used to receive a connection, and the accept method returns a new socket.

		Socket sock = listen.accept();

The data input and output streams of the socket are accessed with:

                /* data from client */
		BufferedReader rd = new BufferedReader(
		    new InputStreamReader( sock.getInputStream() ));

                /* data to client */
                BufferedWriter bw = new BufferedWriter( 
                    new OutputStreamWriter( sock.getOutputStream() ));

Information about the connection is printed with:

		System.out.println("Accepted connection from "
		     + sock.getInetAddress() + " at port " 
		     + sock.getPort() );

Reading the incoming lines and replying is done by:

		String line = null;
		while ( (line=rd.readLine()) != null ) {
                    line = line.toUpperCase();
                    bw.write( line + "\n" );
                    bw.flush();
                    System.out.println( line );
		}

Sockets should always be cleaned up with:

                System.out.println( "closing" + sock );
		sock.close(); // clean up required

Complete LineServer

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

class LineServer {
    public static void main( String[] args ) {
	try {
	    ServerSocket listen = new ServerSocket( 0 );
	    System.out.println("Server port is " + listen.getLocalPort() );

            /* handle one client at a time */
	    while ( true ) {
		Socket sock = listen.accept();

                /* data from client */
		BufferedReader rd = new BufferedReader(
		    new InputStreamReader( sock.getInputStream() ));

                /* data to client */
                BufferedWriter bw = new BufferedWriter( 
                    new OutputStreamWriter( sock.getOutputStream() ));

		System.out.println("Accepted connection from "
		     + sock.getInetAddress() + " at port " 
		     + sock.getPort() );

		String line = null;
		while ( (line=rd.readLine()) != null ) {
                    line = line.toUpperCase();
                    bw.write( line + "\n" );
                    bw.flush();
                    System.out.println( line );
		}
                System.out.println( "closing" + sock );
		sock.close(); // clean up required
	    }
	}
	catch( IOException e ) {
	    System.out.println("error: " + e );
	}
    }
}

LineClient

The LineClient allows a users to send lines of text the server. The server will then echo the line back, and the client will output the line to the screen.

The client must specify the host and port number of the server. This is done with:

	    /* determine the address of the server and connect to it */
	    InetAddress server = InetAddress.getByName( host );
	    Socket sock = new Socket( server, port );

The return value will contain a socket connected to the server. An exception is thrown if the connection fails. The PrintWriter is constructed with the true flag to turn on auto-flushing. The output is flushed whenever one of the println, printf, or format is invoked. The data streams are retrieved and converted to a reader and a writer with:

	    /* get the input stream */
	    InputStream in = sock.getInputStream();
	    /* get the output stream */
	    OutputStream out = sock.getOutputStream();
	    /* attach it to a print writer */
	    PrintWriter wr = new PrintWriter( out, true );
            BufferedReader rr =
                new BufferedReader(new InputStreamReader( in ));
	    /* get an input reader */
	    BufferedReader rd
	         = new BufferedReader( new InputStreamReader(System.in));

Are there any problems with this?

The following loop accepts input from the user, sends it to the server, prints the server's reply.

	    String line = null;
	    while ( (line=rd.readLine()) != null ) {
	        wr.println( line );
                wr.flush();
                System.out.println( rr.readLine() );
	    }
	    /* terminate the connection */
	    sock.close();

Complete LineClient

LineClient.java
import java.io.*;
import java.net.*;
/*
 * The client program can send text lines to a server program.
 */
class LineClient {
    public static void main( String[] args ) {
        if ( args.length != 2 ) {
	    System.out.println("usage: java LineClient host port");
	    System.exit(1);
        }
	int port = 0;
	String host = null;
	try {
	    host = args[0];
	    port = Integer.parseInt( args[1] );
	}
	catch( NumberFormatException e ) {
	    System.out.println("bad port number");
	    System.exit(1);
	}

        try {
	    /* determine the address of the server and connect to it */
	    InetAddress server = InetAddress.getByName( host );
	    Socket sock = new Socket( server, port );
	    /* get the input stream */
	    InputStream in = sock.getInputStream();
	    /* get the output stream */
	    OutputStream out = sock.getOutputStream();
	    /* attach it to a print writer */
	    PrintWriter wr = new PrintWriter( out, true );
            BufferedReader rr =
                new BufferedReader(new InputStreamReader( in ));
	    /* get an input reader */
	    BufferedReader rd
	         = new BufferedReader( new InputStreamReader(System.in));
            
	    String line = null;
	    while ( (line=rd.readLine()) != null ) {
	        wr.println( line );
                wr.flush();
                System.out.println( rr.readLine() );
	    }
	    /* terminate the connection */
	    sock.close();
	}
	catch( UnknownHostException e ) {
	    System.out.println("bad host name");
	    System.exit(0);
	}
	catch( IOException e ) {
	    System.out.println("io error:" + e);
	    System.exit(0);
	}
    }
}

client/server example

A transcript of the server output is:

% java LineServer
Server port is 33233
Accepted new connection from /127.0.0.1 at port 33236
HELLO WORLD
THE SECOND LINE
Connection closed
Accepted new connection from /127.0.0.1 at port 33271
ANOTHER CLIENT
Connection closed

A transcript of the client session is:

% java LineClient localhost 33233
Hello World
HELLO WORLD
The second line
THE SECOND LINE
^D
% java LineClient localhost 33233
another client
ANOTHER CLIENT
^D

line protocol

A possible message exchange between a LineClient and LineServer after the client has connected to the server is:

Client:
Hello World
Server:
HELLO WORLD
Client:
The second line
Server:
THE SECOND LINE

In this protocol the client sends the first line, then waits for a line in reply, and then either terminates the connection, or sends another line and expects a reply. The protocol will fail if the message sequence is altered.

MultiLineServer

The LineServer accepts only one client, at a time. Multiple clients can be handled by starting a new thread for every client connection.

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

class MultiLineServer extends Thread {
    private Socket sock;

    public MultiLineServer( Socket sock ) {
        this.sock = sock;
        start();
    }

    public void run() {
        try {
            /* data from client */
            BufferedReader rd  =
                new BufferedReader(
                    new InputStreamReader( sock.getInputStream() ));

            /* data to client */
            BufferedWriter bw = 
                new BufferedWriter( 
                    new OutputStreamWriter( sock.getOutputStream() ));

            String line = null;
            while ( (line=rd.readLine()) != null ) {
                line = line.toUpperCase();
                bw.write( line + "\n" );
                bw.flush();
                System.out.println( line );
            }
            System.out.println( "closing" + sock );
            sock.close(); // clean up required
            rd.close();
            bw.close();
        }
	catch( IOException e ) {
	    System.out.println("error: " + e );
	}
    }

    private static void dumpThreads() {
        Thread[] threads = new Thread[100];
        int num = Thread.enumerate( threads );
        for( int i = 0 ; i < num; i++ ) {
            System.out.println( threads[i] );
        }
    }

    public static void main( String[] args ) {
	try {
	    ServerSocket listen = new ServerSocket( 0 );
	    System.out.println("Server port is " + listen.getLocalPort() );

	    while ( true ) {
		Socket sock = listen.accept();
                new MultiLineServer( sock );
                dumpThreads();
	    }
	}
	catch( IOException e ) {
	    System.out.println("error: " + e );
	}
    }
}

C-language server code

A mostly equivalent code in C-language that implements a server is:

server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <netinet/in.h>
#include <unistd.h>

int
main( int ac, char *av[] ) {
    int sock;
    int newsock;
    char buf[1024];
    struct sockaddr_in server_addr;
    int sa_len;
    struct sockaddr_in addr;
    struct sockaddr_in newaddr;
    int newsize, datasize;

                /* domain,  type,        protocol */
    sock = socket( PF_INET, SOCK_STREAM, 0 );

    if ( sock == -1 ) {
	perror("socket" );
	exit( 1 );
    }
    addr.sin_family = AF_INET;
    addr.sin_port = htons( 0 );
    addr.sin_addr.s_addr = INADDR_ANY;

    if ( -1 == bind( sock, &addr, sizeof(addr) ) ) {
	perror("bind");
	exit( 1 );
    }

    sa_len = sizeof(server_addr);
    if ( getsockname(sock, (struct sockaddr *)&server_addr, &sa_len) < 0 ) {
	perror("listen");
	exit( 1 );
    }
    printf("port number = %d\n", ntohs(server_addr.sin_port));

    if ( -1 == listen( sock, 5) ) {
	perror("listen");
	exit( 1 );
    }

    newsize = sizeof( newaddr );
    newsock = accept( sock, &newaddr, &newsize );
    if ( newsock == -1 ) {
	perror("accept" );
	exit( 1 );
    }

    datasize = read( newsock, buf, sizeof(buf) );
    if ( datasize == -1 ) {
	perror("read" );
	exit( 1 );
    }

    /* assume that data send is a nul terminated character string */
    printf("mesg = %s\n", buf );

    close( newsock );
    close( sock );
    return 0;
}

C-language client code

The C-language client's code is:

client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>

int
main( int ac, char *av[] ) {
    int sock;
    int sock_status;
    char buf[1024];
    struct sockaddr_in addr;
    struct sockaddr_in destaddr;
    struct hostent *hp;  
    int datasize;
    int server_port;

    if ( ac != 2 ) {
        printf("usage: client port\n");
        exit( 0 );
    }
    server_port = atoi( av[1] );

    sock = socket( PF_INET, SOCK_STREAM, 0 );

    if ( sock == -1 ) {
	perror("socket" );
	exit( 1 );
    }
    addr.sin_family = AF_INET;
    addr.sin_port = htons( 0 ); /* OS picks port */
    addr.sin_addr.s_addr = INADDR_ANY;

    if ( -1 == bind( sock, &addr, sizeof(addr) ) ) {
	perror("bind");
	exit( 1 );
    }

    hp = gethostbyname( "localhost" );
    if ( hp == 0 ) {
	fprintf(stderr,"name lookup failed\n" );
	exit( 1 );
    }
 
    destaddr.sin_family = AF_INET;
    destaddr.sin_port = htons( server_port );
    memcpy(&destaddr.sin_addr, hp->h_addr, hp->h_length);   
    sock_status = connect( sock, &destaddr, sizeof(destaddr) );
    if ( sock_status == -1 ) {
	perror("connect" );
	exit( 1 );
    }

    datasize = write( sock, "hello world\n", 13 );
    if ( datasize != 13 ) {
	perror("read" );
	exit( 1 );
    }

    close( sock );
    return 0;
}