CS 4773 Object Oriented Systems
Network Communication


Previous Topic: Java Beans

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

Next Topic: Not Ready Yet


Source code for the examples are in /usr/local/course/cs4773/spring99/examples/set9

Introduction

Network communication is Java is fairly simple.
This topic will describe client-server connection-oriented communication using stream sockets.
If you took OS at UTSA recently you have seen the UICI interface.
Java network communication is very similar.

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.

The Client-Server Model

In the connection-oriented client-server model of communication, a server waits for a connection request from a client.
When the request come in, a communication channel is set up which allows the server and client to communicate.

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.

Server Methods

A server sets up a ServerSocket by specifying what port number it will listen on.

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.

Client Methods

The client is simpler than the server.
For a client to make a connection to a giver host on a given port, it is only necessary to execute:
Socket connection = new Socket("servername",port);
If this fails an IOException is thrown.

Sending and Receiving Data

One a connection is made, the server and the client can communicate.
There are many ways to do this. This simplest is to use:
   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);

Client and Server Code Example

The ClientServer class can be used to implement a client or a server.

It uses a TextReceiver interface which looks like this:

public interface TextReceiver {

   public boolean ReceiveText(String str);

}
This is used to receive strings. The ClientServer class implements this and strings which come in are set out over the network.

The ClientServer class creates a SocketReceiver thread to wait for input from the network. This thread sends the network input to another TextReceiver which was passed to ClientServer in its constructor.


Here is the SocketReceiver thread:
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));
      }
   }
}

Here is the ClientServer class. It has two constructors, one for use as a client and one for use as a server.
import java.net.*;
import java.io.*;

public class ClientServer implements TextReceiver {

   private TextReceiver tr;
   private DataOutputStream output;
   private Socket connection;

// This is the server constructor
   public ClientServer(int port, TextReceiver tr) {

      this.tr = tr;
      ServerSocket s;

      try {
         s = new ServerSocket(port,5);
         connection = s.accept();
         System.out.println("A connection has been made to the server");
         setup(connection);
      } catch (IOException e) {
         tr.ReceiveText("An IO error occurred in the server");
      }
   }

// This is the client constructor
  public ClientServer(String host, int port, TextReceiver tr) {
      this.tr = tr;

      try {
         connection = new Socket(host,port);
         System.out.println("A connection has been made by the client");
         setup(connection);
      } catch (IOException e) {
         tr.ReceiveText("An IO error occurred in the client");
      }  
   }   

   private void setup(Socket connection) {
      try {
         new SocketReceiver(connection,tr);
         output = new DataOutputStream(connection.getOutputStream());
      } catch (IOException e) {
         tr.ReceiveText("An IO error occurred setting up the output stream");
      }
   }

   public String GetHostName() {
      InetAddress inet;
      String remote;
      inet = connection.getInetAddress();
      remote = inet.getHostName();
      return remote;
   }

   public boolean ReceiveText(String str) {
      try {
         output.writeBytes(str);
      } catch (IOException e) {
         try {
            output.close();
         } catch (IOException e1) {}
         return false;
      }
      return true;
   }
}


Driver Code for the Client and Server Code Example

This section contains client and a server application programs which can be used to test the ClientServer class.

They each put up a frame which allows the user to input data to be sent to the network and contain text areas for displaying data sent and received. The frame is not specific to network communication.


Here is the the frame class used by the drivers:
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 bytes_received = 0;
   private int bytes_sent = 0;
   private int lines_received = 0;
   private int lines_sent = 0;

   public IOFrame(String title, int w, int h, TextReceiver tr) {
      super(title);
      setSize(w,h);
      this.tr = tr;

      setup_layout();
      setVisible(true);

   }

   private void setup_layout() {
      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 set_received_label() {
      ReceivedLabel.setText("Received bytes: "+bytes_received+
                         "   Received lines: "+lines_received);
   }

   private void set_sent_label() {
      SentLabel.setText("Sent bytes: "+bytes_sent+
                         "   Sent lines: "+lines_sent);
   }

   private int count_lines(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);
      bytes_received += str.length();
      lines_received += count_lines(str);
      set_received_label();
      return true;
   }

   public void actionPerformed (ActionEvent e) {
      String str;

      str = input.getText() + "\n";
      input.setText("");
      sent.append(str);
      bytes_sent += str.length();
      lines_sent += count_lines(str);
      set_sent_label();
      if (tr != null)
         tr.ReceiveText(str);
   }
}

Here is the server application:

import java.awt.*;

class ServerMain {

   public static void main(String args[]) {
      int port;
      IOFrame io;
      ClientServer 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 ClientServer(port,io);
      io.SetReceiver(server);
      io.setTitle("Server connected to "+server.GetHostName()+" on port "+port);

   }
}

Here is the client application:

import java.awt.*;

class ClientMain {

   public static void main(String args[]) {
      int port;
      IOFrame io;
      ClientServer client;

      if (args.length != 2) {
         System.out.println("Usage: ServerMain 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 ClientServer(args[0],port,io);
      io.SetReceiver(client);
      io.setTitle("Client connected to "+args[0]+" on port "+args[1]);

   }
}

This is what the IOFrame looks like when started from the server and client:


Push here to run an applet version of the client.


Here is the source for the applet client.
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;
      ClientServer 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 ClientServer(host,port,io);
      io.SetReceiver(client);
      io.setTitle("Client connected to "+host+" on port "+port);
   }
}

Next topic: Not Ready Yet