Program-To-Program Communications Over IP: - Classes Supporting TCP/IP Based Client-Server Connections
Program-To-Program Communications Over IP: - Classes Supporting TCP/IP Based Client-Server Connections
java.net.*;
Classes supporting TCP/IP based client-server
You must have had something on TCP/IP in CSCI102; it cannot be that the only coverage of networking is in elective subjects.
nabg nabg
Program-to-program
IP (Internet Protocol)
communications over IP
Each machine has one (or more) IP addresses How to identify end-point program?
IP protocol defines how to get data packets across Port
Portal, a door or gateway
the Internet from one machine to another.
Ports are OS structures
But need Data buffer for input/data buffer for output
Client program communicates with server program Identified by a number
Not client machine communicates with server machine Programs ask OS for use of a port
Other protocols layered over IP allow program to Server program asks for specific port number, publishes its
program communications port number; client program uses this to identify server
OS allocates port number for client
TCP & UDP
nabg nabg
Sockets
clients and servers
network (Internet or intranet)
Actually two kinds
Data stream sockets
Read and write application data
Server sockets Client Server
Used when client wants to make connection to
server
Java Applet in Web page Java server
Client details put in server-socket input buffer
Additional I/O buffers allocated for data transfers with this Java application C++ server
client C++ or other application
Server program given all these details
No need for client and server to be implemented in same
language they can often communicate by text messages.
nabg nabg
nabg nabg
Servers Servers
The normal server (as described in TCP/IP texts) is a
Server may have to deal with many clients forking server:
Concurrency strategies:
Serial:
deal with clients one at a time, handling all requests until client server (listener) Different processes!
disconnects; then start handling next client. create socket
OS helps by maintaining a queue of clients who wish to server (child process)
bind to well known port
connect; forever()
listen (i.e. activate service) read next request
Forking forever() if bye then quit
Start a new process for each client just as was described for handle request
accept client
WebServer (this strategy rarely used with Java servers)
fork subprocess to deal with client send response
Threaded
Each client gets a thread to serve them new socket created by
accept is passed to child
nabg nabg
Threaded server
Servers All the one process (independent clients)
nabg nabg
main thread handle_client shared Java servers are rarely (if ever) forking servers
forever() data though can launch a process via Runtime object
create socket
read next request
bind to port if bye then break
Javas support for threads makes it simpler than
activate handle request usual to implement as threaded server
forever() send response handle_client
forever() all objects have associated locks, simply need to start to
accept client thread die read next request
if bye then break
handle request
send response
use these (facilitating safe use of shared data)
create new thread to handle_client thread die
thread package provides simple subset with most useful
deal with this client forever()
read next request features from a full threads implementation
start thread in if bye then break handle_client
handle_client handle request
forever() ...
read next request
send response if bye then break Java solution: have a ClientHandler class (optionally inherits Thread),
thread die handle request
send response create an instance for each client (passing new socket), let it run()
thread die
nabg nabg
Your programming of
Server provides a service
Client - server systems
You can rely on the libraries to handle: Typically, a server is a bit like an instance of a
establishment of communications link
class that has a public interface advertising a few
reliable data exchange (TCP/IP) (or the rarely useful simple
operations it can perform for a client.
packet transfer based on cheaper UDP/IP mechanisms) Example 1: HTTP server
GET, POST, OPTIONS,
What you really see at both ends is a connection
Example 2: FTP server
that supports read and write access. List directory, change directory, get file, put file,
You must invent Sometimes will have a service with only one
Application
specific operation echo, ping,
Client Application Server Application
PROTOCOL
nabg nabg
nabg nabg
Your programming of
Your programming of
Client - server systems
Client - server systems
Client (things that you should think about)
how does user select the server? Server
connection mechanism set up; how to handle failures? First, what strategy?
how does user select the commands to be sent, and how are Depends on load that you expect on your server, if
necessary input data obtained? lightly used a serial server may suffice (and is a lot
sending data; easier to implement)
getting response
switch(response code) into ... If expect to deal with many clients, then need
handle various permitted responses appropriate for last request concurrent (threaded) server
displaying response; Much of code is standardized, so can cut-copy-paste
how does user terminate session?
nabg nabg
nabg nabg
nabg nabg
nabg nabg
public static void main(String argv[]) public static void main(String argv[])
{ ... { ...
Socket s = null; BufferedReader d = new BufferedReader(new InputStreamReader(System.in));
try { s = new Socket(ina, port); } byte[] inputbuffer = new byte[2048];
catch (IOException io) { for(;;) {
System.out.println("No luck with that combination; try another host/port"); System.out.print(">");
System.exit(0); String str = null;
} try { str = d.readLine(); }
DataOutputStream writeToSocket = null; catch (IOException io) {System.exit(0); }
DataInputStream readFromSocket = null; str = str.trim();
try { if(str.equals("Quit")) break;
writeToSocket = new DataOutputStream(s.getOutputStream()); try { writeToSocket.writeBytes(str + "\n"); }
readFromSocket = new DataInputStream(s.getInputStream()); catch (IOException io) {
} System.out.println("Write to socket failed"); System.exit(0);
catch (IOException io) { }
System.out.println("Got socket, but couldn't set up read/write communications");
System.exit(0);
}
nabg nabg
nabg nabg
Examples
Simple serial server
Examples getDate etc
Threaded version of same
Internet Relay Chat program
nabg nabg
nabg nabg
nabg nabg
public static void main(String[] args) // Get I/O streams, make the ObjectStreams
{ // for serializable objects
ObjectInputStream responseStream = null;
ObjectOutputStream requestStream = null;
// Create client socket try {
Socket sock = null; requestStream = new ObjectOutputStream(
try { sock.getOutputStream());
sock = new Socket(hostName, port); requestStream.flush();
responseStream = new ObjectInputStream(
}
sock.getInputStream());
catch(Exception e) { }
// I/O, unknown host, ... catch(IOException ioe1) {
System.out.println("Failed to connect because " System.out.println("Failed to get socket streams");
System.exit(1);
+ e.getMessage());
}
System.exit(1); System.out.println("Connected!");
}
Output first, flush it, then input else system may stall; feature!
nabg nabg
nabg nabg
else
if(line.equals(Quit)) {
Sending and receiving
// Disconnect, dont expect response to Quit
requestStream.writeObject(line); Write the object
requestStream.flush();
Flush
requestStream.reset();
requestStream.close();
OS may not send small message!
It waits assuming that there will be more to send and better to
responseStream.close(); send one big package than two small ones
break;
So force sending
}
// ignore any other input commands
Reset
// they are invalid Remember those dictionaries associated with
objectstreams that remember every object written or
read?
Clear by explicitly resetting
A reset by the sender causes a reset on receiver
nabg nabg
nabg nabg
nabg nabg
for(;;) {
try {
private static void handleClient(Socket sock) { String request = (String) requests.readObject();
ObjectInputStream requests = null; if(request.equals("Date")) {
ObjectOutputStream responses = null; responses.writeObject((new Date()).toString());
try { responses.flush();
responses = new ObjectOutputStream( responses.reset();
}
sock.getOutputStream());
else
responses.flush(); if(request.equals("Fortune")) {
requests = new ObjectInputStream( responses.writeObject(getFortune());
sock.getInputStream()); responses.flush();
} responses.reset();
catch(IOException ioe1) { }
System.out.println("Couldn't open streams"); else
try { sock.close(); } catch(Exception e) {} if(request.equals("Quit")) {
try { sock.close(); } catch(Exception e) {}
return;
break;
} }
for(;;) { }
catch(Exception e) {
} try { sock.close(); } catch(Exception eclose) {}
} return;
}
Setting up of communications streams for current client }
Dispatching remote requests to service functions
}
nabg nabg
Server
private static String getFortune() {
String[] cookies = {
"The trouble with some women is they get all excited about nothing, and then they marry him.",
"It's true hard work never killed anybody, but I figure, why take the chance?",
"A successful man is one who makes more money than his wife can spend.",
"When a man brings his wife flowers for no reason, there's a reason.",
"Genius is the ability to reduce the complicated to the simple."
};
Client
nabg nabg
Multiple machines
If practical, run client and server on
different machines What about errors on server side?
Copy client .jar file to second machine
Start server on one machine (with known IP
address or known DNS name)
Start client on second machine
Command line arguments are server DNS name (or
IP address) and port
nabg nabg
nabg nabg
nabg nabg
Message request = (Message) requests.readObject();
if(request.control.equals("Fortune")) {
int choice;
Server try {
choice = Integer.parseInt(
(String) request.associateddata);
String fortune = getFortune(choice);
Fortune function responses.writeObject(new Message("OK", fortune));
private static String getFortune(int choice) responses.flush(); responses.reset();
{ }
String[] cookies = { catch(NumberFormatException nfe) {
responses.writeObject(new Message("Error" ,
"It's true hard work never killed anybody, but I ", "- non numeric data for choice"));
responses.flush(); responses.reset();
}
};
catch(IndexOutOfBoundsException ndxe) {
return cookies[choice];
responses.writeObject(new Message("Error",
}
"IndexOutOfBounds!"));
responses.flush(); responses.reset();
}
}
nabg nabg
nabg nabg
Server
Client exception handling if(request.control.equals("Fortune")) {
int choice;
try {
catch(IOException ioe2) {
System.out.println("Problems!");
choice = Integer.parseInt(
System.out.println(ioe2.getMessage()); (String) request.associateddata);
System.exit(1); String fortune = getFortune(choice);
} responses.writeObject(fortune);
catch(ClassNotFoundException cnfe) { responses.flush(); responses.reset();
System.out.println("Got something strange back from server }
causing class not found exception!");
catch(NumberFormatException nfe) {
System.exit(1);
} responses.writeObject(nfe);
catch(Exception other) { responses.flush(); responses.reset();
System.out.println( }
"Server probably returned this exception!"); catch(IndexOutOfBoundsException ndxe) {
System.out.println(other); responses.writeObject(ndxe);
} responses.flush(); responses.reset();
}
}
nabg nabg
Concurrent server
No change to client
Threaded version? Server:
! Use application specific client handler class that
implements Runnable
its public void run() method has the code from the
handle_client function (plus any auxiliary functions)
After accepting client
Create new ClientHandler with reference to Socket
Create thread
Thread runs handler
nabg nabg
nabg nabg
nabg nabg
ClientHandler ClientHandler.run()
public class ClientHandler implements Runnable Message request = (Message) requests.readObject();
{ if(request.control.equals("Date")) { }
private int count; else
private ObjectInputStream requests; if(request.control.equals("Fortune")) { }
private ObjectOutputStream responses;
else
private String getFortune(int choice) { }
if(request.control.equals("Ping")) {
public ClientHandler(Socket sock) {
responses.writeObject("Pong " + Integer.toString(++count));
// initialize, open streams etc
responses.flush();
} responses.reset();
public void run() }
{ else
for(;;) { } if(request.control.equals("Quit")) { }
}
}
nabg nabg
Micro
I RC nternet elay hat
nabg nabg
IRC? IRC
Internet relay chat: Client program
host machine runs server program connect to server
program supports multiple concurrent clients (via threading or specify hostname and port and nickname
polling mechanisms) accessing shared data
view channels, submit selection
server program provides
virtual channels
view list of participants, get display of recent messages
each channel related to a discussion topic own messages broadcast to other participants
when first connect to server, can review choices, and pick channel see any messages entered by others
discussion groups on channels
associated with each channel have
group of clients (identified by nicknames)
buffer containing last few messages exchanged
nabg nabg
Micro-IRC Canvas
allows text to scroll up like an ordinary
Misses out the channels layer. computer terminal
List of
MicroIRC server participants
multithreaded Java application
buffer for 10 recent messages
MicroIRC client Sender names, and messages
also multithreaded (listening for messages from others,
responding to local users keys/mouse actions)
List display of names of paticipants
Canvas (fake scrolling) of recent text (no local buffer)
TextField and action Button for message entry Button
TextField (edit your message)
(broadcast message)
nabg nabg
MicoIRC
NetBeans style
Three projects
Server application
Client application
Utilities library
(In /share/cs-pub/csci213 as
compressed collection of
NetBeans projects)
nabg nabg
nabg nabg
nabg nabg
nabg nabg
nabg nabg
Micro-IRC Micro-IRC
Application protocol Application protocol
Client initiated exchanges Server initiated
Post Post
client sends Message with Post and contents of text input server sends Message Post together with a Posting object
buffer containing the nickname of participant who posted message
not acknowledged and contents of message
Quit no acknowledgement from client
client sends Quit: (not acknowledged) Add_Client:
another client has connected
Remove_Client:
some other client has disconnected
determine (main aspects of) application protocol determine (main aspects of) application protocol
nabg nabg
Selection of server and establishment of Most messages from server are asynchronous!
connection server sends client a Post whenever any of users on
use command line arguments system has entered data, and sends add/remove
host_name, port number, nickname to use when connect client as others connect and disconnect
construct InetAddress with given host_name, if fail so not synchronized with actions of local user
print error report and quit
implication => Need a separate thread ready at all
socket connection opened to server, again failures
times to handle asynchronous message from server
result in program exiting.
MicroIRC Client
displaying response Canvas
allows text to scroll up like an ordinary
several possible ways of handling messages computer terminal
TextArea (disabled) in scrolling window List of
allows user to scroll back, refresh is possible after display participants
temporarily obscured
potentially large storage demands as add Strings to TextArea
array of Strings displayed in window Sender names, and messages
discard oldest if array full
limits storage, still allows some scrollback and refresh
no storage of data
messages drawn directly on Canvas
This option copyarea used to produce scrolling effect
picked for
demo cant view messages scrolled off screen, cant refresh if display
Button
TextField (edit your message)
temporarily obscured (broadcast message)
nabg nabg
nabg nabg
ClientGUI ClientGUI
nabg nabg
(As data only written to screen, information is lost if window is hidden then re-exposed)
ClientGUI
nabg nabg
nabg nabg
Client Client
nabg nabg
Client
nabg nabg
Threaded server
Micro-IRC Server
All the one process (interacting clients)
Server
main thread handle_client shared
create socket
forever() data normal mechanism
read next request
bind to port if bye then break receptionist listening at published well know port
activate handle request accepts connections (getting new port-socket combination)
forever() send response handle_client
thread die
forever()
read next request
creates ClientHandler objects (and associated threads) for clients
accept client if bye then break
nabg nabg
public SharedData() {
Multiple clients data = new Vector<Posting>();
subscribers = new Vector<ClientHandler>();
Multiple threads in server }
Possibly more than one thread trying to modify
shared data public synchronized void addClient(ClientHandler ch, String
nickname) throws MyException
Possibly more than one thread trying to use a {}
particular ClientHandler!
ClientHandlers own thread receiving a message public synchronized void removeClient(ClientHandler ch)
Some other thread trying to broadcast another users {}
posting
public synchronized void postMessage(String poster, String msg)
Need locks synchronized methods {}
}
SharedData
nabg nabg
public synchronized void addClient(ClientHandler ch, String nickname) throws public synchronized void removeClient(ClientHandler ch) {
MyException { subscribers.remove(ch);
for(ClientHandler c : subscribers) Message m = new Message("Remove_Client", ch.getName());
if(c.getName().equals(nickname)) for(ClientHandler c : subscribers)
throw new MyException("Duplicate name"); c.sendMessage(m);
// Send notification to each existing subscriber that there is a new participant }
Message m = new Message("Add_Client", nickname);
for(ClientHandler c: subscribers) public synchronized void postMessage(String poster, String msg) {
c.sendMessage(m); Posting p = new Posting(poster, msg);
// Add new client to collection data.add(p);
subscribers.add(ch); if(data.size()>10)
// Compose a response for new client data.removeElementAt(0);
String[] participants = new String[subscribers.size()]; Message m = new Message("Post", p);
int i=0; for(ClientHandler c: subscribers)
for(ClientHandler c: subscribers) c.sendMessage(m);
participants[i++] = c.getName(); }
Posting[] posts = data.toArray(new Posting[0]);
InitialData id = new InitialData(participants, posts);
Message reply = new Message("OK", id);
ch.sendMessage(reply);
} SharedData SharedData
nabg nabg
ClientHandler
nabg nabg
public ClientHandler(Socket aSocket, SharedData sd) throws IOException { private boolean handleRegister() {
mySocket = aSocket; try {
output = new ObjectOutputStream(aSocket.getOutputStream()); Message m = (Message) input.readObject();
output.flush(); String operation = m.operation;
input = new ObjectInputStream(aSocket.getInputStream()); if(!operation.equals("Register"))
shared = sd; Wrap byte level socket streams return false;
} name = (String) m.otherdata;
in object streams to allow transfer
shared.addClient(this, name);
of objects return true;
}
catch(ClassNotFoundException cnfe) { }
public synchronized void sendMessage(Message m) { catch(IOException ioe) { }
try { catch(MyException me) {
output.writeObject(m); try {
output.flush(); output.writeObject(me);
output.reset(); output.flush();
} }
catch(IOException ioe) { } catch(IOException io) {}
} }
return false;
ClientHandler } ClientHandler
nabg nabg
}
return; MicroIRC
for(;;) { As networked application, quite likely to get a break
try {
Message m = (Message) input.readObject(); in client-server link without formal logout
String operation = m.operation; client program died
if(operation.equals("Quit"))
break; server terminated
else
if(operation.equals("Post"))
...
shared.postMessage(name, (String) m.otherdata); No particular event sent to other process, but will get
}
catch(Exception e) { break; } fail at next read/write operation on socket.
}
Code tries to trap these and :
shared.removeClient(this); (if break spotted by client) terminate client process
try { mySocket.close(); } catch(IOException ioe) {}
} (if break spotted by server) destroy client handler object
ClientHandler