Internet and Distributed Processing
Many interesting and wide spread applications depend
on the Internet (process to process communication)
for their functionality. Examples include:
- web browsers and web servers
- email clients and mail servers
- NFS clients and servers (network file system)
- X-windows clients and a X-window server
- bittorrent
- news servers
- chat servers
- time servers
- DNS servers
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.
-
A hub connects nodes at the physical (electrical) layer.
All connected nodes share the same bus and bandwidth.
-
A switch connects nodes at the data link layer.
The data link layer is also called the media access control (MAC)
layer for Ethernet networks (Actually, it is the lower half
of the data link layer). A switch allows each connected
node to operate at full speed.
-
A router connects nodes at the network layer.
Routers can also act as firewalls, and they can perform NATing.
-
Gateways work at the application layer (e.g.,
HTTP proxies).
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:
- ftp-data: 20
- ftp-control: 21
- ssh: 22
- telnet: 23
- smtp: 25
- time: 37
- domain: 53
- finger: 79
- http: 80
- pop2: 109
- pop3: 110
- sunrpc: 111
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:
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
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
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.
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:
#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:
#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;
}