Saturday, June 19, 2010

Non-blocking Output Socket

My MATLAB TCP/IP example has been quite popular, and I myself use it at least once a week on various applications. However, one of the main limitations when compared to our C++ style output sockets was the limitation of only 1 client per socket.

What was worse, is that I knew it COULD be done. I had just not found the time to write it! Cue a spare Saturday!


The Problem

As per my previous server.m examples, you begin by constructing a Java ServerSocket.

>> server_socket = ServerSocket(output_port); 
>> server_socket.setSoTimeout(1000);

This attempts to bind the ServerSocket to the specified port. HOWEVER, it is not listening for incoming connections! To do this, you must tell the ServerSocket to accept().

>> output_socket = server_socket.accept();

This is a BLOCKING call. That is, the code will wait for either the timeout (set above) to occur, or a client to connect.

For a lot of what I do, the output socket exists mainly as a debugging / monitoring interface. So I don't even care if noone is listening. So what we need is something that manages the connection of clients and the distribution of any written data to each of them.

Enter OutputSocket and ListeningThread.

OutputSocket

OutputSocket provides a very simple interface:

//--------------------------------------------------------------------- 
// Description : Create an OutputSocket on the specified port

//---------------------------------------------------------------------

public
OutputSocket(int port) throws IOException

//---------------------------------------------------------------------
// Description : Write the supplied byte array to all connected clients

//---------------------------------------------------------------------

public void
write(byte[] data)


//---------------------------------------------------------------------

// Description : Close the OutputSocket. This closes all client

// connections and stops listening for new connections.
//---------------------------------------------------------------------

public void
close()



ListeningThread

The OutputSocket utilises ListeningThread to manage the accepting of new connections. ListeningThread implements the Runnable interface so can be started in its own thread (vital!).

//-------------------------------------------------------------------------
// Description : Helper class that listens for new TCP/IP client
// connections on a ServerSocket
//
// Parameters : server_socket - ServerSocket to listen on
// connection_list - List of connected client
// Sockets to update
// connection_stream_list - List of connected client
// DataOutputStreams to update
//---------------------------------------------------------------------
public ListeningThread(ServerSocket server_socket,
List connection_list,
List connection_stream_list)


//---------------------------------------------------------------------
// Description : Called on Thread.start()
//---------------------------------------------------------------------
public void
run()


//---------------------------------------------------------------------
// Description : Stop the thread
//---------------------------------------------------------------------
public void
stop()



Interaction

The OutputSocket constructor creates the ServerSocket as before and stores it as a member variable. It also creates 2 empty lists, one for the Socket client connections and one for the associated DataOutputStreams used to write to them.

It supplies a reference to these to the ListeningThread on construction and then starts it running in its own Thread. This is the important part. OutputSocket and ListeningThread both share a reference to the DataOutputStream list, so as ListeningThread creates connections and output streams, these are pushed onto the client list.

When OutputSocket::write() is called, it writes the data across all output streams (be there 0 or 100!)

When you have finished with the OutputSocket, a call to close() will stop the ListeningThread from running and close all client connections.

Usage from within MATLAB

Place the .class files in a suitable location (I've stuck them in my current working directory).
>> % Add the directory containing the .class files to your Java path
>> javaaddpath(pwd)
>> % Create the OutputSocket
>> output_socket = OutputSocket(1234)

output_socket =

OutputSocket@1c68b20

>> % Write some data to the output socket - text will do for now
>> for i = 1:100
output_socket.write(int8(sprintf('This is line %03d\r\n', i)));
pause(1);
end
>> % cleanup the socket - close connections
>> output_socket.close()
>> clear output_socket % no longer needed / useful


As you can see, multiple connections are supported. They can connect/disconnect at any time. Even if there are no connections, a call to OutputSocket::write() will be successful.


**Update**

I've included in the revised submission to MATLABCentral a wrapper MATLAB class that helps manage the lifetime of the Java object.

This is VERY useful because if you happen to clear the Java object (ie clear output_socket) before calling .close() the Thread and ServerSocket etc remain in memory (and stay connected!) until MATLAB is closed!

Suggested usage of MatlabOutputSocket below.
>> javaaddpath(pwd); % get the Java class
>> output_socket = MatlabOutputSocket(1234);
for i = 1:100
output_socket.write(int8(sprintf('Line %03d\r\n', i)));
pause(1);
end
>> output_socket.close()
>> clear output_socket

I've included the Java source file in the MATLABCentral submission, so feel free to have a look at the source code, have a fiddle, recompile.

To do this you will need to install the Java Development Kit (JDK).

To compile the classes, navigate to the directory containing the .java file and type:
>> javac OutputSocket.java
NOTE: You will have to repeat the call to javaaddpath() for the new files to be seen by MATLAB.