CS865 – Distributed
Software Development
|
The Client-Server
Model
|
Based on:
Introduction
·
The Client-Server paradigm is the most prevalent model for
distributed computing protocols.
·
It is the basis of all distributed computing paradigms at a higher
level of abstraction.
·
It is service-oriented, and employs a request-response
protocol.
The Client-Server Paradigm
·
A server process, running on a server host, provides access to a
service.
·
A client process, running on a client host, accesses the service
via the server process.
·
The interaction of the process proceeds according to a protocol.
Client-server applications and services
·
An application based on the client-server paradigm is a
client-server application.
·
On the Internet, many services are Client-server
applications. These services are often
known by the protocol that the application implements.
·
Well known Internet services include HTTP, FTP, DNS, finger,
gopher, etc.
·
User applications may also be built using the client-server
paradigm.
Client-server system architecture vs. Client-server distributed computing
·
Client-server
system architecture - terms clients and servers refer
to computers,
·
Client-server
distributed
computing paradigm -
terms refer to processes.
Client-server, an overloaded term
A protocol/service session
·
Session - the interaction between the server and one client.
·
Service managed by a server may be accessed by multiple clients
who desire the service, sometimes concurrently.
·
Each client, when serviced by the server, engages in a separate
session with the server, during which it conducts a dialog with the server
until the client has obtained the service it required
A service session
The Protocol for a Network Service
·
Protocol required to specify the rules
that must be observed by the client and the server during the conduction of a
service.
·
Rules include specifications on matters such as :
(i)
how the service is to be located,
(ii)
the sequence of interprocess
communication,
(iii)
the representation and interpretation of data
exchanged with each IPC.
·
On the Internet, such protocols are specified in the RFCs
(Requests for Coments).
·
A mechanism must be available to allow a
client process to locate a server for a given service.
·
A service can be located through the address
of the server process, in terms of the host name and protocol port number
assigned to the server process.
·
This is the scheme for Internet
services.
·
Each Internet service is assigned to a
specific port number.
·
A well-known service such as ftp, HTTP,
or telnet is assigned a default port number reserved on each Internet host for
that service.
·
At a higher level of abstraction, a
service may be identified using a logical name registered with a registry,
·
The logical name will need to be mapped to
the physical location of the server process.
·
If the mapping is performed at runtime
(that is, when a client process is run), then it is possible for the service’s
location to be dynamic, or moveable.
Implementation of a network service
·
Must adhere to the specification for the protocol, including how
the dialogs of each session should proceed.
The specification defines
(i)
which side (client or server) should speak first
the syntax and semantic of each request and response
the action expected of each side upon receiving a particular request or response.
Interprocess communications (IPC) and event synchronization
The interaction of the client and server processes follows a request-response pattern.
Session IPC examples
The dialog in each session follows a pattern prescribed in the protocol specified for the service.
·
e.g. Daytime service [RFC867]:
Client: |
Hello, <client address> here. May I have a timestamp please. |
Server: |
Here it is: (time stamp follows) World Wide Web session: |
Client: |
Hello,
<client address> here.
|
Server: |
Okay. I am a web server
and speaks protocol HTTP1.0. |
Client: |
Great, please get me the web page index.html at the root
of your document tree. |
Server: |
Okay, here’s what’s in the page: (contents follows). |
The getAddress and getPort
Methods
Client-server protocol data representation
Part of the
specification of a protocol is the syntax and semantics of each request and
response.
·
The choice of data representation depends on the nature and the
needs of the protocol.
·
Representing data using text (character strings) is common, as it
facilitates data marshalling and allows the data to be readable by human.
·
Most well known Internet protocols are client-server,
request-response, and text-base.
UML class
diagram for DaytimeClient1
import java.io.*;
/**
*
This module contains the presentaton logic of a DaytimeClient.
*
@author M. L. Liu
*/
public class DaytimeClient1 {
public static void main(String[] args)
{
InputStreamReader is = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(is);
try {
System.out.println("Welcome to the Daytime client.\n" +
"What is the
name of the server host?");
String hostName = br.readLine();
if (hostName.length() == 0) //
if user did not enter a name
hostName = "localhost";
// use the default host name
System.out.println("What is the port number of the server host?");
String portNum = br.readLine();
if (portNum.length() == 0)
portNum = "13"; // default port number
System.out.println("Here is the timestamp received from the
server"
+ DaytimeClientHelper1.getTimestamp(hostName, portNum));
} //
end try
catch (Exception ex) {
ex.printStackTrace( );
} //
end catch
} //end
main
} // end class
|
import java.net.*;
/**
*
This class is a module which provides that application logic
*
for a Daytime Client.
*
@author M. L. Liu
*/
public class DaytimeClientHelper1 {
public static String getTimestamp(String
hostName, String portNum){
String
timestamp = "";
try {
InetAddress serverHost = InetAddress.getByName(hostName);
int
serverPort = Integer.parseInt(portNum);
//
instantiates a datagram socket for both sending
//
and receiving data
MyDatagramSocket mySocket = new
MyDatagramSocket();
mySocket.sendMessage( serverHost, serverPort, "");
// now receive the timestamp
timestamp = mySocket.receiveMessage();
mySocket.close( );
} //
end try
catch (Exception
ex) {
System.out.println("There is a problem: " + ex);
}
return timestamp;
} //end getTimeStamp
} //end class
|
import java.net.*;
import java.io.*;
/**
*
A subclass of DatagramSocket which contains
*
methods for sending and receiving messages
*
@author M. L. Liu
*/
public class MyClientDatagramSocket extends DatagramSocket {
static final int MAX_LEN = 100;
MyClientDatagramSocket( ) throws SocketException{
super( );
}
MyClientDatagramSocket(int portNo)
throws SocketException{
super(portNo);
}
public void sendMessage(InetAddress receiverHost,
int receiverPort,
String message)
throws IOException {
byte[ ] sendBuffer = message.getBytes( );
DatagramPacket datagram =
new DatagramPacket(sendBuffer, sendBuffer.length,
receiverHost, receiverPort);
this.send(datagram);
} // end sendMessage
public String receiveMessage()
throws IOException {
byte[ ] receiveBuffer = new
byte[MAX_LEN];
DatagramPacket datagram =
new DatagramPacket(receiveBuffer, MAX_LEN);
this.receive(datagram);
String message = new String(receiveBuffer);
return message;
} //end receiveMessage
} //end class
|
UML Diagram for the Datagram Daytime server
DaytimeServer1.java
import java.io.*; import java.util.Date; // for obtaining a timestamp /** *
This module contains the application logic of a Daytime server * which uses a connection datagram socket for interprocess communication. * A
command-line argument is required to specify the server port. *
@author M. L. Liu */ public class DaytimeServer1
{
public static void main(String[] args) {
int serverPort =
13; // default port
if (args.length == 1 )
serverPort = Integer.parseInt(args[0]);
try {
// instantiates a datagram socket for both sending
// and receiving data MyServerDatagramSocket mySocket = new MyServerDatagramSocket(serverPort);
System.out.println("Daytime server ready.");
while (true) { // forever loop
DatagramMessage
request = mySocket.receiveMessageAndSender();
System.out.println("Request
received");
// The message received is unimportant; it is the sender's
// address that we need in order to reply. // Now obtain the timestamp from the local
system.
Date timestamp = new Date ();
System.out.println("timestamp sent:
"+ timestamp.toString());
// Now send the reply to the requestor
mySocket.sendMessage(request.getAddress(
), request.getPort(
), timestamp.toString( )); } //end while
} // end try catch (Exception ex) {
System.out.println("There is a problem:
" + ex); } // end catch }
//end main } // end class |
MyServerDatagramSocket.java
import
java.net.*; import java.io.*; /** * A
subclass of DatagramSocket which contains *
methods for sending and receiving messages *
@author M. L. Liu */ public class MyServerDatagramSocket extends DatagramSocket { static final int
MAX_LEN = 100; MyServerDatagramSocket(int portNo)
throws SocketException{
super(portNo); }
public void sendMessage(InetAddress receiverHost, int receiverPort, String message) throws IOException
{
byte[ ] sendBuffer = message.getBytes(
);
DatagramPacket
datagram =
new DatagramPacket(sendBuffer,
sendBuffer.length, receiverHost, receiverPort);
this.send(datagram); } //
end sendMessage
public String receiveMessage( ) throws
IOException {
byte[ ] receiveBuffer = new byte[MAX_LEN];
DatagramPacket datagram =
new DatagramPacket(receiveBuffer,
MAX_LEN);
this.receive(datagram);
String message = new String(receiveBuffer);
return message; }
//end receiveMessage
public DatagramMessage receiveMessageAndSender( ) throws
IOException {
byte[ ] receiveBuffer = new byte[MAX_LEN];
DatagramPacket
datagram =
new DatagramPacket(receiveBuffer,
MAX_LEN);
this.receive(datagram);
// create a DatagramMessage object, to
contain message
// received and sender's
address
DatagramMessage
returnVal = new DatagramMessage(
);
returnVal.putVal(new String(receiveBuffer), datagram.getAddress(
), datagram.getPort(
));
return returnVal; }
//end receiveMessage } //end class |
DatagramMessage.java
import
java.net.*; /** * A
class to use with MyServerDatagramSocket for *
returning a message and the sender's address *
@author M. L. Liu */ public class DatagramMessage{
private String message;
private InetAddress senderAddress;
private int senderPort;
public void putVal(String message, InetAddress addr, int port) {
this.message = message;
this.senderAddress = addr;
this.senderPort = port; }
public String getMessage( ) {
return this.message; }
public InetAddress getAddress(
) {
return this.senderAddress; }
public int getPort(
) {
return this.senderPort; } } // end class |
The Client-Server Model
Connectionless server vs. connection-oriented server
·
An echo server is a
server that echoes back whatever it receives from a client.
·
For example, if a
client sends the server the string Hello there! the
server will respond with the exact data it received from the client - Hello there!
EchoServer1.java
·
Forever
Looping
·
Server
reads line from socket and then writes the line back to the socket addressing
reply to sender
·
Since
there is no connection, server can interact with many clients interleaving
messages
import java.io.*; /** * This module contains the application logic
of an echo server * which uses a connectionless datagram
socket for interprocess * communication. * A command-line argument is required to
specify the server port. * @author M. L. Liu */ public class EchoServer1
{
public static void main(String[] args) {
int serverPort =
7; // default port
if (args.length == 1 )
serverPort = Integer.parseInt(args[0]);
try {
// instantiates a datagram socket for both sending
// and receiving data MyServerDatagramSocket
mySocket = new MyServerDatagramSocket(serverPort);
System.out.println("Echo server ready."); while (true) { // forever loop DatagramMessage
request = mySocket.receiveMessageAndSender(); System.out.println("Request
received"); String message = request.getMessage( );
System.out.println("message
received: "+ message); // Now send the echo to the
requestor mySocket.sendMessage(request.getAddress( ), request.getPort(
), message); } //end while
} // end try catch (Exception ex) {
ex.printStackTrace( ); } // end catch }
//end main } // end class |
EchoClient1.java
import java.io.*; /** *
This module contains the presentaton logic of an
Echo Client. *
@author M. L. Liu */ public class EchoClient1
{ static final String endMessage =
".";
public static void main(String[] args) {
InputStreamReader is = new InputStreamReader(System.in);
BufferedReader br
= new BufferedReader(is);
try {
System.out.println("Welcome to the Echo
client.\n" + "What is the
name of the server host?");
String hostName = br.readLine();
if (hostName.length() == 0) // if user did
not enter a name
hostName = "localhost"; //
use the default host name
System.out.println("What is the port number of the server host?");
String portNum = br.readLine();
if (portNum.length() == 0)
portNum = "7"; // default port number
EchoClientHelper1 helper =
new EchoClientHelper1(hostName,
portNum);
boolean done = false;
String message, echo;
while (!done) {
System.out.println("Enter a line to
receive an echo back from the server, " + "or a
single peroid to quit.");
message = br.readLine( );
if ((message.trim()).equals (endMessage)){ done = true; helper.done(
);
}
else {
echo = helper.getEcho( message); System.out.println(echo);
}
} // end while
} // end try
catch (Exception ex) {
ex.printStackTrace( );
} // end catch }
//end main } // end class |
EchoClientHelper1.java
import
java.net.*; import java.io.*; /** *
This class is a module which provides the application logic * for an Echo client using connectionless datagram socket. *
@author M. L. Liu */ public class EchoClientHelper1
{
private MyClientDatagramSocket
mySocket;
private InetAddress serverHost;
private int serverPort; EchoClientHelper1(String hostName,
String portNum)
throws SocketException, UnknownHostException
{ this.serverHost = InetAddress.getByName(hostName); this.serverPort
= Integer.parseInt(portNum);
// instantiates a datagram socket for both sending
// and receiving data this.mySocket =
new MyClientDatagramSocket(); }
public String getEcho( String message) throws SocketException,
IOException {
String echo = "";
mySocket.sendMessage( serverHost,
serverPort, message); // now receive the echo
echo = mySocket.receiveMessage();
return echo; }
//end getEcho
public void done( ) throws SocketException {
mySocket.close( );
} //end done } //end class |
Concurrent client sessions with EchoServer1
·
Uses stream-mode socket API
·
No
Overlapping of Sessions
Two consecutive client sessions with echo server2
EchoServer2.java
import java.io.*; import
java.net.*; /** * This module contains the application logic
of an echo server * which uses a
stream socket for interprocess communication. * A command-line argument is required to
specify the server port. * @author M. L. Liu */ public class EchoServer2
{ static final String endMessage =
".";
public static void main(String[] args) {
int serverPort =
7; // default port
String message;
if (args.length == 1 )
serverPort = Integer.parseInt(args[0]);
try {
// instantiates a stream socket for accepting //
connections ServerSocket myConnectionSocket =
new ServerSocket(serverPort); /**/
System.out.println("Daytime server ready.");
while (true) { // forever loop
// wait to accept a connection /**/
System.out.println("Waiting
for a connection.");
MyStreamSocket
myDataSocket = new MyStreamSocket (myConnectionSocket.accept(
)); /**/
System.out.println("connection
accepted");
boolean done = false;
while
(!done) { message = myDataSocket.receiveMessage(
); /**/ System.out.println("message
received: "+ message); if ((message.trim()).equals
(endMessage)){ //Session
over; close the data socket. /**/ System.out.println("Session
over."); myDataSocket.close(
); done = true; } //end if else { //
Now send the echo to the requestor myDataSocket.sendMessage(message); } //end else } //end while !done } //end while forever
} // end try catch (Exception ex) {
ex.printStackTrace( ); } }
//end main } // end class |
MyStreamSocket.java
import
java.net.*; import java.io.*; /** * A wrapper class of Socket which contains * methods for sending and receiving messages * @author M. L. Liu */ public class MyStreamSocket extends Socket {
private Socket socket; private BufferedReader
input;
private PrintWriter output; MyStreamSocket(InetAddress acceptorHost,
int acceptorPort )
throws SocketException,IOException{
socket = new Socket(acceptorHost, acceptorPort );
setStreams( ); } MyStreamSocket(Socket socket) throws IOException
{
this.socket = socket;
setStreams( ); }
private void setStreams( ) throws IOException{
// get an input stream for reading from the data socket
InputStream inStream
= socket.getInputStream();
input =
new BufferedReader(new InputStreamReader(inStream));
OutputStream outStream
= socket.getOutputStream();
// create a PrinterWriter object for
character-mode output
output =
new PrintWriter(new OutputStreamWriter(outStream)); }
public void sendMessage(String message) throws IOException
{
output.println(message);
//The ensuing flush method call is necessary for the data to
// be written to the socket data stream before the
// socket is closed.
output.flush(); }
// end sendMessage
public String receiveMessage( ) throws
IOException {
// read a line from the data stream
String message = input.readLine( );
return message; }
//end receiveMessage } //end class |
EchoClient2.java
import java.io.*; /** * This module contains the presentaton logic of an Echo Client. * @author M. L. Liu */ public class EchoClient2
{ static final String endMessage =
".";
public static void main(String[] args) {
InputStreamReader is = new InputStreamReader(System.in);
BufferedReader br
= new BufferedReader(is);
try {
System.out.println("Welcome to the Echo
client.\n" +
"What is the name of the server host?");
String hostName = br.readLine();
if (hostName.length() == 0) // if user did
not enter a name
hostName = "localhost"; //
use the default host name
System.out.println("What is the port number of the server host?");
String portNum = br.readLine();
if (portNum.length() == 0)
portNum = "7"; // default port number
EchoClientHelper2 helper =
new EchoClientHelper2(hostName,
portNum);
boolean done = false;
String message, echo;
while (!done) {
System.out.println("Enter a line to
receive an echo " + "from the server, or a
single period to quit.");
message = br.readLine( );
if ((message.trim()).equals (endMessage)){ done = true; helper.done(
);
}
else { echo = helper.getEcho(
message); System.out.println(echo);
}
} // end while
} // end try
catch (Exception ex) {
ex.printStackTrace( );
} //end catch }
//end main } // end class |
EchoClientHelper2.java
import
java.net.*; import java.io.*; /** * This class is a module which provides the
application logic * for an Echo
client using stream-mode socket. * @author M. L. Liu */ public class EchoClientHelper2
{ static final String endMessage =
".";
private MyStreamSocket mySocket; private
InetAddress serverHost;
private int serverPort; EchoClientHelper2(String hostName,String
portNum)
throws SocketException, UnknownHostException,
IOException { this.serverHost = InetAddress.getByName(hostName); this.serverPort
= Integer.parseInt(portNum);
//Instantiates a stream-mode socket and wait for a connection. this.mySocket =
new MyStreamSocket(this.serverHost,
this.serverPort); /**/
System.out.println("Connection request
made"); }
// end constructor
public String getEcho( String message) throws SocketException,
IOException{
String echo = "";
mySocket.sendMessage( message); // now receive the echo
echo = mySocket.receiveMessage();
return echo; }
// end getEcho
public void done( ) throws SocketException, IOException{
mySocket.sendMessage(endMessage);
mySocket.close( ); }
// end done } //end class |
A Concurrent, Connection-oriented Server
·
Client-side
unchanged – can use EchoClient2
Sequence diagram – EchoServer3
EchoServer3.java
import java.io.*; import
java.net.*; /** * This module contains the application logic
of an echo server * which uses a
stream-mode socket for interprocess communication. * Unlike EchoServer2, this server services
clients concurrently. * A command-line argument is required to
specify the server port. * @author M. L. Liu */ public class EchoServer3
{
public static void main(String[] args) {
int serverPort =
7; // default port
String message;
if (args.length == 1 )
serverPort = Integer.parseInt(args[0]);
try {
// instantiates a stream socket for accepting
// connections ServerSocket myConnectionSocket =
new ServerSocket(serverPort); /**/
System.out.println("Echo server ready.");
while (true) { // forever loop
// wait to accept a connection /**/
System.out.println("Waiting for a connection.");
MyStreamSocket
myDataSocket = new MyStreamSocket (myConnectionSocket.accept(
)); /**/
System.out.println("connection
accepted");
// Start a thread to handle this client's sesson Thread theThread = new Thread(new EchoServerThread(myDataSocket));
theThread.start();
// and go on to the next client
} //end while forever } // end try catch (Exception ex) {
ex.printStackTrace( ); } // end catch }
//end main } // end class |
EchoServerThread.java
import java.io.*; /** * This module is to be used with a
concurrent Echo server. * Its run method carries out the logic of a
client session. * @author M. L. Liu */ class EchoServerThread implements Runnable { static final String endMessage =
"."; MyStreamSocket myDataSocket; EchoServerThread(MyStreamSocket myDataSocket) {
this.myDataSocket = myDataSocket; }
public void run( ) {
boolean done = false;
String message;
try {
while (!done) {
message = myDataSocket.receiveMessage( ); /**/
System.out.println("message received:
"+ message);
if ((message.trim()).equals (endMessage)){ //Session over; close the
data socket. /**/ System.out.println("Session
over."); myDataSocket.close(
); done = true;
} //end if
else { // Now send the echo to the
requestor myDataSocket.sendMessage(message);
} //end else
} //end while !done
}// end try
catch (Exception ex) {
System.out.println("Exception caught in
thread: " + ex);
} // end catch }
//end run } //end class |
Echo3Server concurrent sessions
class ServerThread implements Runnable {
static
final String endMessage = ".";
MyStreamSocket myDataSocket;
ServerThread(MyStreamSocket myDataSocket) {
this.myDataSocket = myDataSocket;
}
public
void run( ) {
boolean done = false;
String message;
try
{
//add code here
}// end try
catch
(Exception ex) {
System.out.println("Exception caught in thread: " + ex);
}
} //end run
}
//end class
|
Issues
·
A
counter protocol is implemented that increments a counter by one each time the
server is contacted by a client and send it back to the client.
import java.io.*; /** * This module contains the application logic
of a Counter server * which uses a datagram socket for interprocess
communication. * A command-line argument is required to
specify the server port. * @author M. L. Liu */ public class CounterServer1 { /*
state information */ static int counter = 0;
public static void main(String[] args) {
int serverPort =
12345; // default port
if (args.length == 1 )
serverPort = Integer.parseInt(args[0]);
try {
// instantiates a datagram socket for both sending
// and receiving data MyServerDatagramSocket mySocket =
new MyServerDatagramSocket(serverPort); /**/
System.out.println("Counter server ready."); while (true) { // forever loop
DatagramMessage
request = mySocket.receiveMessageAndSender();
System.out.println("Request
received");
// The message received is unimportant; it is the sender's // address that we need in order to
reply. // Now increment the counter, then send its value to the client.
increment( ); /**/
System.out.println("counter sent:
"+ counter);
// Now send the reply to the requestor
mySocket.sendMessage(request.getAddress(
), request.getPort(
), String.valueOf(counter)); } //end while
} // end try catch (Exception ex) {
ex.printStackTrace( ); } }
//end main
static private synchronized void increment( ){
counter++; } } // end class |
CounterClient1.java
import java.io.*; /** *
This module contains the presentation logic of a counter Client. *
@author M. L. Liu */ public class CounterClient1
{
public static void main(String[] args) {
InputStreamReader is = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(is);
try {
System.out.println("Welcome to the
Counter client.\n" + "What is the
name of the server host?");
String hostName = br.readLine();
if (hostName.length() == 0) // if user did
not enter a name
hostName = "localhost"; //
use the default host name
System.out.println("Enter the port # of
the server host:");
String portNum = br.readLine();
if (portNum.length() == 0)
portNum = "12345"; // default port number
System.out.println
("Here is the counter received from the server: "
+ CounterClientHelper1.getCounter(hostName, portNum));
} // end try
catch (Exception ex) {
ex.printStackTrace( );
} // end catch }
//end main } // end class |
CounterClientHelper1.java
import
java.net.*; /** *
This class is a module which provides the application logic * for a Counter Client. *
@author M. L. Liu */ public class CounterClientHelper1
{
public static int getCounter(String hostName, String portNum){ int counter =
0;
String message = "";
try { InetAddress serverHost =
InetAddress.getByName(hostName); int serverPort = Integer.parseInt(portNum);
// instantiates a datagram socket for both sending
// and receiving data MyDatagramSocket mySocket = new MyDatagramSocket();
mySocket.sendMessage( serverHost,
serverPort, ""); // now receive the timestamp
message = mySocket.receiveMessage(); /**/
System.out.println("Message received:
" + message);
counter = Integer.parseInt(message.trim()); mySocket.close(
);
} // end try catch (Exception ex) {
ex.printStackTrace( ); } // end catch
return counter; }
//end main } //end class |