Introduction
The Client-Server Model
Server Methods
Client Methods
Sending and Receiving Data
Client and Server Code Example
Drivers for the Client and Server Code Example
The User Interface
Next Topic: Monopoly Design
There are restrictions as far as what you can do from inside of an applet.
A client applet can only establish communication with a server running on the
host that the applet was loaded from.
There is no such restriction on an application.
Communication requests to a server are distinguished by a port number.
A port is just an integer (usually a small one, but in Java, an int is used
for a port number).
Certain port numbers are reserved.
For example, 21 is used by ftp and 2049 is reserved for nfs.
On a given machine, at most one process can act as a server for a given port, but any number of clients can use that port number.
ServerSocket s = new ServerSocket(port,queueLength);
This does the same thing as the UICI u_open or the socket calls
socket, bind and listen.
A server waits for a connection using:
Socket connection = s.accept();
This blocks until a connection request comes in from a client.
It is the similar to the UICI u_listen or the socket accept.
A server that return from the accept can then create a thread to handle the connection while it does another accept.
Each of these server methods can throw an IOException.
DataInputStream input; DataOutputStream output; input = new DataInputStream(connection.getInputStream()); output = new DataOutputStream(connection.getOutputStream());Each of these can throw an IOException so you should enclose these in a try catch.
A DataInputStream or DataOutputStream represents Unicode strings. They allow for the transfer of information in a machine-independent way.
Here are some of the methods that can be used with a dataOutputStream
public final void writeBoolean(boolean v); public final void writeByte(int v); public final void writeBytes(String s); public final void writeChar(int v); public final void writeChars(String s); public final void writeDouble(double v); public final void writeInt(int v); public final void writeLong(long v); public final void WriteShort(int v); public void write(int b); public void write(byte b[], int off, int len); public void flush();All of these can throw an IOException.
There are corresponding methods for reading from a DataInputStream.
public final boolean readBoolean(); public final byte readByte(); public final char readChar(); public final double ReadDouble(); public final int readInt(); public final long readLong(); public final short readShort(); public final int read(byte b[]); public final int read(byte b[], int off, int len);
The following pieces will be designed:
In this case the method has just a String as a parameter, say
public boolean receiveText(String str);
The return value indicates whether the parameter was processed correctly.
How does the Network Listener know where to send its output?
It must know which textReceiver to send it to.
Since in Java you cannot pass a method as a parameter, you must pass an
object which has a textReceiver method.
Question: What kind of objects have a textReceiver method?
Answer: Those that implement TextReceiver:
public interface TextReceiver { public boolean receiveText(String str); }So the Network Listener must be passed a socket and a TextReceiver.
import java.net.*; import java.io.*; public class SocketReceiver extends Thread { private final int BUFSIZE = 1024; private DataInputStream input; private TextReceiver tr; public SocketReceiver(Socket connection, TextReceiver tr) { this.tr = tr; try { input = new DataInputStream(connection.getInputStream()); start(); } catch (IOException e) {} } public void run() { byte[] buffer; int bytesread; buffer = new byte[BUFSIZE]; for ( ; ; ) { try { bytesread = input.read(buffer); } catch (IOException e) { try { input.close(); } catch (IOException e1) {} return; } if (bytesread > 0) tr.receiveText(new String(buffer,0,bytesread)); } } }
The Network Sender is called SocketSender:
import java.net.*; import java.io.*; public class SocketSender implements TextReceiver { private TextReceiver tr; private DataOutputStream output; public SocketSender(Socket connection, TextReceiver tr) { this.tr = tr; try { output = new DataOutputStream(connection.getOutputStream()); } catch (IOException e) { tr.receiveText("An IO error occurred setting up the output stream"); } } public boolean receiveText(String str) { if (output == null) return false; try { output.writeBytes(str); } catch (IOException e) { try { output.close(); } catch (IOException e1) {} return false; } return true; } }
The Server class needs a port number to listen on and
a place to put what is read from the network (TextReceiver).
It must be able to accept text to be sent to the network
(implements TextReceiver.
It creates a SocketSender for sending and a SocketReceiver
for receiving.
Here is the Server class:
import java.net.*; import java.io.*; public class Server implements TextReceiver { private TextReceiver tr; private DataOutputStream output; SocketSender sender = null; private Socket connection; public Server(int port, TextReceiver tr) { this.tr = tr; ServerSocket s; try { s = new ServerSocket(port,5); connection = s.accept(); } catch (IOException e) { tr.receiveText("An IO error occurred in the server"); return; } sender = new SocketSender(connection,tr); new SocketReceiver(connection,tr); } public String getHostName() { InetAddress inet; String remote; if (connection == null) return "None"; inet = connection.getInetAddress(); remote = inet.getHostName(); return remote; } public boolean receiveText(String str) { if (sender == null) return false; return sender.receiveText(str); } }
The Client is similar to the Server, except that it needs a host name as well as a port:
import java.net.*; import java.io.*; public class Client implements TextReceiver { private SocketSender sender = null; public Client(String host, int port, TextReceiver tr) { Socket connection; try { connection = new Socket(host,port); } catch (IOException e) { tr.receiveText("An IO error occurred in the client"); return; } sender = new SocketSender(connection,tr); new SocketReceiver(connection,tr); } public boolean receiveText(String str) { if (sender == null) return false; return sender.receiveText(str); } }
The ServerMain is an application which puts up an IOFrame and waits on a given port. Note that this class does not have to know anyting about how the network connection actually works.
import java.awt.*; class ServerMain { public static void main(String args[]) { int port; IOFrame io; Server server; if (args.length != 1) { System.out.println("Usage: ServerMain portnumber"); return; } port = Integer.parseInt(args[0]); System.out.println("Using port number "+port); io = new IOFrame("Server waiting on port "+port,400,400,null); io.setBackgrounds(Color.cyan,Color.yellow); server = new Server(port,io); io.setReceiver(server); io.setTitle("Server connected to "+server.getHostName()+" on port "+port); } }
The ClientMain is similar. It takes two command line arguments, a port number and a host name.
import java.awt.*; class ClientMain { public static void main(String args[]) { int port; IOFrame io; Client client; if (args.length != 2) { System.out.println("Usage: ClientMain host portnumber"); return; } port = Integer.parseInt(args[1]); System.out.println("Using port number "+port); io = new IOFrame("Client",400,400,null); io.setBackgrounds(Color.yellow,Color.cyan); client = new Client(args[0],port,io); io.setReceiver(client); io.setTitle("Client connected to "+args[0]+" on port "+args[1]); } }
Here is what it will look like when a client and server are running:
The User Interface
The only thing left is the User Interface.
Here is the IOFrame code:
import java.awt.*; import java.awt.event.*; public class IOFrame extends Frame implements TextReceiver, ActionListener { private TextReceiver tr; private TextField input; private TextArea sent; private TextArea received; private Label receivedLabel; private Label sentLabel; private int bytesReceived = 0; private int bytesSent = 0; private int linesReceived = 0; private int linesSent = 0; public IOFrame(String title, int w, int h, TextReceiver tr) { super(title); setSize(w,h); this.tr = tr; setupLayout(); setVisible(true); } private void setupLayout() { Panel p; Panel ReceivedPanel; Panel SentPanel; receivedLabel = new Label("Received:"); sentLabel = new Label("Sent:"); setLayout(new BorderLayout()); add(input = new TextField(),BorderLayout.SOUTH); ReceivedPanel = new Panel(); ReceivedPanel.setLayout(new BorderLayout()); ReceivedPanel.add(receivedLabel,BorderLayout.NORTH); SentPanel = new Panel(); SentPanel.setLayout(new BorderLayout()); SentPanel.add(sentLabel,BorderLayout.NORTH); p = new Panel(); p.setLayout(new GridLayout(2,1)); ReceivedPanel.add(received = new TextArea(),BorderLayout.CENTER); SentPanel.add(sent = new TextArea(),BorderLayout.CENTER); p.add(ReceivedPanel); p.add(SentPanel); received.setEditable(false); sent.setEditable(false); add(p,BorderLayout.CENTER); input.addActionListener(this); validate(); } private void setReceivedLabel() { receivedLabel.setText("Received bytes: "+bytesReceived+ " Received lines: "+linesReceived); } private void setSentLabel() { sentLabel.setText("Sent bytes: "+bytesSent+ " Sent lines: "+linesSent); } private int countLines(String str) { int count = 0; for (int i=0;i < str.length();i++) if (str.charAt(i)=='\n') count++; return count; } public void setReceiver(TextReceiver tr) { this.tr = tr; } public void setBackgrounds(Color c1, Color c2) { sent.setBackground(c1); received.setBackground(c2); } public boolean receiveText(String str) { received.append(str); bytesReceived += str.length(); linesReceived += countLines(str); setReceivedLabel(); return true; } public void actionPerformed (ActionEvent e) { String str; str = input.getText() + "\n"; input.setText(""); sent.append(str); bytesSent += str.length(); linesSent += countLines(str); setSentLabel(); if (tr != null) tr.receiveText(str); } }
We can also use these to create a client applet which can access a server running on the host that the applet was loaded from.
Here is what it might look like:
import java.awt.*; import java.applet.*; import java.awt.event.*; public class ClientApplet extends Applet implements ActionListener { String host; TextField tf; IOFrame io; public void init() { setLayout(new GridLayout(2,1)); host = getCodeBase().getHost(); add(new Label("Enter Port Number for Connection to "+host)); add(tf = new TextField()); tf.addActionListener(this); validate(); } public void stop() { if (io != null) io.setVisible(false); } public void actionPerformed (ActionEvent e) { String str; int port; Client client; port = Integer.parseInt(tf.getText()); System.out.println("Connecting to host "+host+" on port "+port); io = new IOFrame("Client",400,400,null); io.setBackgrounds(Color.yellow,Color.cyan); client = new Client(host,port,io); io.setReceiver(client); io.setTitle("Client connected to "+host+" on port "+port); } }