Customizing TCP Sockets
An Introduction with Examples in Java |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
Offset | Size | Type |
0 | 1 | '*' (i.e., the ASCII literal *) |
1 | 1 | Frame Type |
2 | 2 | Sequence Number |
4 | 2 | Data Length |
Code | Frame Type |
1 | SIGNON |
2 | DATA |
3 | ERROR (For future use) |
4 | SIGNOFF (For future use) |
5 | KEEP_ALIVE (For future use) |
Socket
class uses streams, so we need
to take that into considerationSocket
Socket
,
we're adding functionality to the stream
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); } }
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(); } }
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; } }
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; } }
Use a Factory