JMU
Customizing TCP Sockets
An Introduction with Examples in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Motivation
Possible Approaches
An Example
An Example (cont.)

The Header (6 bytes)

Offset Size Type
0 1 '*' (i.e., the ASCII literal *)
1 1 Frame Type
2 2 Sequence Number
4 2 Data Length

An Example (cont.)

Valid Frame Types

Code Frame Type
1 SIGNON
2 DATA
3 ERROR (For future use)
4 SIGNOFF (For future use)
5 KEEP_ALIVE (For future use)

An Example (cont.)
An Example (cont.)

An Input Stream for a Simple Framing Protocol

javaexamples/sfp/SFPInputStream.java
        import java.io.*;


/**
 *  For reading input streams using the SFP protocol
 *
 *  @version 1.0
 *  @author  Prof. David Bernstein, James madison University
 */
public class SFPInputStream extends InputStream
{
    protected int              available, dataLength;
    protected int              frameType, sequenceNumber;
    protected DataInputStream  in;




    /**
     * Construct a new SFPInputStream
     *
     * @param in   The InputStream to use
     */

    public SFPInputStream(InputStream in)
    {
       this.in = new DataInputStream(in);
       available = 0;
    }




    /**
     * Returns the number of bytes available without blocking
     *
     */
    public int available() throws IOException
    {
       return available;
    }



    /**
     * Decode an array of bytes
     *
     * This version does nothing
     */
    protected void  decode(byte[] b)
    {
       // Do nothing
    }







    /**
     * Get the frame type of the most recently/current message
     * (See SFPConnection for valid frame types)
     *
     * @return  The frame type
     */
    public int getFrameType()
    {
       return frameType;
    }




    /**
     * Closes this input stream
     */
    public void close() throws IOException
    {
       in.close();
    }





    /**
     * Tests if this input stream supports the mark and reset methods
     */
    public boolean markSupported()
    {
       return false;
    }



    /**
     * Reads the next byte of data
     */
    public int read() throws IOException
    {
       byte[] b;
       int    retval;

       b = new byte[1];
       if (available == 0) readHeader();
       retval = read(b,0,1);
       if (retval > 0) retval = (int)(b[0]);

       return retval;
    }





    /**
     * Reads an array of bytes
     *
     * @param b   The buffer into which the data are read
     * @param off The start offset of the data
     * @param len The maximum number of bytes read
     *
     * @return    The number of bytes actually read or -1 if end-of-stream
     */
    public int read(byte[] b, int off, int len) throws IOException
    {
       byte[] tmp;
       int bytesRead, i, length;

       if (available == 0) readHeader();

       if (len < available) length = len;
       else                 length = available;

       tmp = new byte[length];
       bytesRead = in.read(tmp);
        
       // For future use
       decode(tmp);

       if (bytesRead < 0) available = 0;
       else 
       {
          available -= bytesRead;
          for (i=0; i < bytesRead; i++) 
          {
             b[i+off] = tmp[i];
          }
       }

       return bytesRead;
    }





    /**
     * Reads an array of bytes
     *
     * @param b   The buffer into which the data are read
     * @return    The number of bytes actually read or -1 if end-of-stream
     */
    public int read(byte[] b) throws IOException
    {
       if (available == 0) readHeader();
       return read(b, 0, b.length);
    }







    /**
     * Read the SFP header information
     */
    protected void readHeader() throws IOException
    {
       byte[] b;
       char   asterisk;

       b = new byte[2];


       asterisk = (char)(in.readByte());
       frameType = (int)(in.readByte());
       sequenceNumber = (int)(in.readShort());
       dataLength = (int)(in.readShort());

       available = dataLength;
    }






    /**
     * Skips over and discards a given number of bytes
     *
     * Note: Even though n is passed as a long, it is treated as an int
     *
     * @param n  The number of bytes to skip
     */
    public long skip(long n) throws IOException
    {
       byte[]  tmp;
       int     k;

       k = (int)n;
       tmp = new byte[k];


       return read(tmp);

    }

}
        
An Example (cont.)

An Output Stream for a Simple Framing Protocol

javaexamples/sfp/SFPOutputStream.java
        import java.io.*;
import java.util.*;


/**
 *  For writing output streams using the SFLP protocol
 *
 *  Note that all write operations are buffered.  
 *  The actual writing does not take place until 
 *  the flush method is called.
 *
 *  @version 1.0
 *  @author  Prof. David Bernstein, James madison University
 */
public class SFPOutputStream extends OutputStream
{
    protected int                sequenceNumber;
    protected DataOutputStream   out;
    protected LinkedList         buffer;





    /**
     * Construct a new SFPOutputStream
     *
     * @param out   The OutputStream to use
     */

    public SFPOutputStream(OutputStream out)
    {
       this.out = new DataOutputStream(out);
       sequenceNumber = 1;
       buffer = new LinkedList();
    }






    /**
     * Closes this Output stream (after first 
     * calling flush);
     */
    public void close() throws IOException
    {
       flush();
       out.close();
    }





    /**
     * Flushes this Output stream
     */
    public void flush() throws IOException
    {
       byte            b;
       Byte            byteObject;
       ListIterator    e;

       // Write the header
       writeHeader(SFPConnection.DATA, buffer.size());

       // Write the contents of the buffer
       e = buffer.listIterator();
       while (e.hasNext()) 
       {
          byteObject = (Byte)e.next();
          b = byteObject.byteValue();
          out.write(b);
       }

       // Flush the output stream
       out.flush();

       // Clear the buffer
       buffer.clear();
    }




    /**
     * Writes a single byte of data
     *
     * @param b   The byte to write 
     */
    protected void write(byte b) throws IOException
    {
       buffer.addLast(new Byte(b));
    }






    /**
     * Writes a single byte of data
     *
     * @param b   The 1-byte integer to write 
     */
    public void write(int b) throws IOException
    {
       write((byte)b);
    }





    /**
     * Writes an array of bytes
     *
     * @param b   The bytes to write
     * @param off The start offset of the data
     * @param len The maximum number of bytes read
     */
    public void write(byte[] b, int off, int len) throws IOException
    {
       int   i;

       for (i=0; i < len; i++) write(b[i+off]);
    }





    /**
     * Writes an array of bytes
     *
     * @param b   The bytes to write
     */
    public void write(byte[] b) throws IOException
    {
       int   i;

       for (i=0; i < b.length; i++) write(b[i]);
    }






    /**
     * Write the SFP header information
     *
     * @param frameType  The frame type
     * @param length     The length
     */
    protected void writeHeader(int frameType, int length) throws IOException
    {

       out.writeByte((int)('*'));
       out.writeByte(frameType);
       out.writeShort(sequenceNumber);
       out.writeShort(length);

       sequenceNumber++;
    }




    /**
     * Write a SIGNON header
     */
    protected void writeSIGNON() throws IOException
    {
       writeHeader(SFPConnection.SIGNON, 0);
       out.flush();
    }




}
        
An Example (cont.)

A Socket for a Simple Framing Protocol

javaexamples/sfp/SFPSocket.java
        import java.net.*;
import java.io.*;


/**
 * A Socket that uses the SFP protocol
 *
 * @version 1.0
 * @author  Prof. David Bernstein, James Madison University
 */
public class SFPSocket extends Socket
{
    protected SFPInputStream    in;
    protected SFPOutputStream   out;



    /**
     * Constructor for server-side sockets
     */
    public SFPSocket() throws SocketException
    {
        super();

        in  = null;
        out = null;
    }





    /**
     * Explicit value constructor for client-side sockets
     *
     * @param host      The host name
     * @param port      The host port
     */
    public SFPSocket(String host, int port) throws IOException
    {
        super(host, port);


        // Start the client side of the sign-on process
        DataOutputStream   os;
        int                i;
        InputStream        is;


        is = super.getInputStream();
        in  = new SFPInputStream(is);
        os  = new DataOutputStream(super.getOutputStream());


        // Write initial message (with header)
        //
        os.writeByte((int)('*'));
        os.writeByte(SFPConnection.SIGNON);
        os.writeShort(0);
        os.writeShort(SFPConnection.sfpon.length);
        os.write(SFPConnection.sfpon, 0, SFPConnection.sfpon.length);
        os.flush();
        
        
        // Read the SIGNON frame from the host 
        // (in binary; network byte order)
        //
        in.readHeader();
        if (in.getFrameType() != SFPConnection.SIGNON) {
            
            throw(new IOException("No SIGNON from host"));
        }
    }




    /**
     * Returns an input stream for this socket
     */
    public InputStream getInputStream() throws IOException
    {
        if (in == null) in  = new SFPInputStream(super.getInputStream());

        return in;
    }



    /**
     * Returns an output stream for this socket
     */
    public OutputStream getOutputStream() throws IOException
    {
        if (out == null) out  = new SFPOutputStream(super.getOutputStream());


        return out;
    }

}
        
An Example (cont.)

A Server Socket for a Simple Framing Protocol

javaexamples/sfp/SFPServerSocket.java
        import java.net.*;
import java.io.*;

/**
 * A Socket that uses the SFP protocol
 *
 * @version 1.0
 * @author  Prof. David Bernstein, James Madison University
 */
public class SFPServerSocket extends ServerSocket
{

    /**
     * Default Constructor
     */
    public SFPServerSocket() throws IOException
    {
       super(SFPConnection.DEFAULT_PORT);
    }



    /**
     *  Listens for a connection to be made to this socket and 
     *  accepts it. The method blocks until a connection is made. 
     */
    public Socket accept() throws IOException
    {
       boolean            ok;
       byte[]             initial;  
       int                i, len;
       SFPInputStream     in;
       SFPOutputStream    out;
       SFPSocket   socket;


       socket = new SFPSocket();
       // Complete the connection (see the javadocs)
       implAccept(socket);

       // Start the server side of the sign-on process
       in =  (SFPInputStream)socket.getInputStream();
       out = (SFPOutputStream)socket.getOutputStream();

       // Read the initial message from the client
       initial = new byte[9];
       in.read(initial);

       // Check the initial message and reply
       ok = true;
       i = 0;
       while ( (i < initial.length)) 
       {
          if (initial[i] != SFPConnection.sfpon[i]) ok = false;
          i++;
       }
        
       if (ok) out.writeSIGNON();
       else    throw(new IOException("No SFPON from client"));

       return socket;
    }


}
        
A More Flexible Design

Use a Factory

images/custom_sockets_java.gif