Skip to content

Commit

Permalink
Updated to latest Java-WebSocket and cleaned up code
Browse files Browse the repository at this point in the history
- SSL now works
- Restarting server on same port on Windows now works
- Tests work with SSL on Windows, should work on Linux and MacOS
- JAR file updated
  • Loading branch information
jebej committed Apr 2, 2017
1 parent cadfc95 commit a15dd15
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 104 deletions.
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
MatlabWebSocket
===============

MatlabWebSocket is a simple library consisting of a websocket server and client for Matlab built on [Java-WebSocket](https://github.com/TooTallNate/Java-WebSocket), a java implementation of the websocket protocol by Nathan Rajlich. It currently does support encryption, but there appears to be a bug that make the server crash once a client disconnects. This bug might not exist on Linux/macOS.
MatlabWebSocket is a simple library consisting of a websocket server and client for MATLAB built on [Java-WebSocket](https://github.com/TooTallNate/Java-WebSocket), a java implementation of the websocket protocol. Encryption is supported with self-signed certificates made with the java keytool.

The non-secure server and client work fine, although there is a bug on Windows which prevents servers from being restarted on the same port. The workaround is to restart MATLAB, or change port.

Installation
Installation and Uninstallation
------------
The required java library is a jar file located in the `/jar/` folder. It must be placed on the static java class path in Matlab. See the [Matlab Documentation](http://www.mathworks.com/help/matlab/matlab_external/bringing-java-classes-and-methods-into-matlab-workspace.html).
First, download the latest release on GitHub or MATLAB Central and extract to contents where you want.

The required java library is a jar file located in the `/jar/` folder. It must be placed on the static java class path in MATLAB . See the [MATLAB Documentation](http://www.mathworks.com/help/matlab/matlab_external/static-path.html). Note that after adding the jar file to the java class path, MATLAB will need to be restarted.

You must now add the `/src/` folder to the MATLAB path. If you want to run the examples, add the `/examples/` folder as well.

You must also add the `/src/` folder to the Matlab path.
Simply undo these operations to uninstall MatlabWebSocket.

Usage
------------

The `WebSocketServer.m` file is an abstract Matlab class. The behaviour of the server must therefore be defined by creating a subclass that implements the following methods:
The `WebSocketServer.m` file is an abstract MATLAB class. The behaviour of the server must therefore be defined by creating a subclass that implements the following methods:

```matlab
onOpen(obj,conn,message)
onTextMessage(obj,conn,message)
onBinaryMessage(obj,conn,message)
onError(obj,conn,message)
onClose(obj,conn,message)
onOpen(obj,conn,message)
onTextMessage(obj,conn,message)
onBinaryMessage(obj,conn,message)
onError(obj,conn,message)
onClose(obj,conn,message)
```

`obj` is the object instance of the subclass, it is implicitly passed by MATLAB (see the object-oriented programming documentation of MATLAB).

`message` is the message received by the server.

`conn` is a WebSocketConnection object representing the client connection that cause the event. For example, if a message is received, the `conn` object will represent the client that sent this message.
`conn` is a WebSocketConnection object representing the client connection that caused the event. For example, if a message is received, the `conn` object will represent the client that sent this message. You can send messages to that client through this object.

The `WebSocketCLient.m` is very similar to the server, except that no `conn` object is passed to the `onSomething` methods.
The `WebSocketClient.m` class is very similar to the server, except that no `conn` object is passed to the `onSomething` methods.

The server supports a variety of methods to help talk to clients, look in the MATLAB class file to see what methods are available.

Expand All @@ -40,7 +42,7 @@ Example
------
The example is an echo server, it returns to the client whatever was received.

Run the echo server by making sure that the file is on the MATLAB path and executing:
Run the echo server by making sure that the file is on the MATLAB path and executing
```matlab
server = EchoServer(30000)
```
Expand All @@ -64,7 +66,7 @@ clientCode = server.Connections(1).HashCode
server.sendTo(clientCode,'hi, this is the server!')
```

To close the server, go back to Matlab and type:
To close the server, type:
```matlab
stop(server); % or server.stop()
clear server;
Expand Down
2 changes: 1 addition & 1 deletion examples/SimpleClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ function onTextMessage(obj,message)
function onBinaryMessage(obj,bytearray)
% This function simply displays the message received
fprintf('Binary message received:\n');
display(bytearray);
display(size(bytearray));
end

function onError(obj,message)
Expand Down
Binary file not shown.
Binary file added jar/matlab-websocket-1.0.jar
Binary file not shown.
9 changes: 7 additions & 2 deletions src/WebSocketClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
end

properties (Access = private)
UseKeyStore = false
KeyStore % Location of the keystore
StorePassword % Keystore password
KeyPassword % Key password
Expand All @@ -33,8 +34,9 @@
% Constructor, create a client to connect to the deisgnated
% server, the URI must be of the form 'ws://localhost:30000'
obj.URI = URI;
if any(regexpi(URI,'wss')); obj.Secure = true; end
if nargin>1
obj.Secure = true;
obj.UseKeyStore = true;
obj.KeyStore = keyStore;
obj.StorePassword = storePassword;
obj.KeyPassword = keyPassword;
Expand All @@ -57,7 +59,10 @@ function open(obj)
% Create the java client object in with specified URI
if obj.Status; warning('Connection is already open!');return; end
uri = handle(java.net.URI(obj.URI));
if obj.Secure
if obj.Secure && ~obj.UseKeyStore
import io.github.jebej.matlabwebsocket.MatlabWebSocketSSLClient;
obj.ClientObj = handle(MatlabWebSocketSSLClient(uri),'CallbackProperties');
elseif obj.Secure && obj.UseKeyStore
import io.github.jebej.matlabwebsocket.MatlabWebSocketSSLClient;
obj.ClientObj = handle(MatlabWebSocketSSLClient(uri,obj.KeyStore,obj.StorePassword,obj.KeyPassword),'CallbackProperties');
else
Expand Down
5 changes: 3 additions & 2 deletions src/matlab-websocket/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<groupId>io.github.jebej.matlabwebsocket</groupId>
<artifactId>matlab-websocket</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>

<name>matlab-websocket</name>
<url>http://maven.apache.org</url>
Expand All @@ -32,6 +32,7 @@
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
</configuration>
<executions>
<execution>
Expand All @@ -57,7 +58,7 @@
<dependency>
<groupId>com.github.TooTallNate</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>58d1778695</version>
<version>b168941011</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package io.github.jebej.matlabwebsocket;

import java.nio.ByteBuffer;
import java.util.Set;

import org.java_websocket.WebSocket;

// Object given to MATLAB when an event occur. The "this.something" assignments
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.java_websocket.WebSocket;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;

Expand All @@ -21,39 +19,35 @@ public MatlabWebSocketClient( URI serverURI ) {
public void onOpen( ServerHandshake handshakedata ) {
String openMessage = "Connected to server at " + getURI();
MatlabEvent matlab_event = new MatlabEvent( this, openMessage );
Iterator<MatlabListener> listeners = _listeners.iterator();
while (listeners.hasNext() ) {
( (MatlabListener) listeners.next() ).Open( matlab_event );
for (MatlabListener _listener : _listeners) {
(_listener).Open(matlab_event);
}
}

// This function gets executed on text message receipt
@Override
public void onMessage( String message ) {
MatlabEvent matlab_event = new MatlabEvent( this, message );
Iterator<MatlabListener> listeners = _listeners.iterator();
while (listeners.hasNext() ) {
( (MatlabListener) listeners.next() ).TextMessage( matlab_event );
for (MatlabListener _listener : _listeners) {
(_listener).TextMessage(matlab_event);
}
}

// Method handler when a byte message has been received from the client
@Override
public void onMessage( ByteBuffer blob ) {
MatlabEvent matlab_event = new MatlabEvent( this, blob );
Iterator<MatlabListener> listeners = _listeners.iterator();
while (listeners.hasNext() ) {
( (MatlabListener) listeners.next() ).BinaryMessage( matlab_event );
for (MatlabListener _listener : _listeners) {
(_listener).BinaryMessage(matlab_event);
}
}

// This method gets executed on error
@Override
public void onError( Exception ex ) {
MatlabEvent matlab_event = new MatlabEvent( this, ex.getMessage() );
Iterator<MatlabListener> listeners = _listeners.iterator();
while (listeners.hasNext() ) {
( (MatlabListener) listeners.next() ).Error( matlab_event );
for (MatlabListener _listener : _listeners) {
(_listener).Error(matlab_event);
}
// If the error is fatal, onClose will be called automatically
}
Expand All @@ -64,14 +58,13 @@ public void onError( Exception ex ) {
public void onClose( int code, String reason, boolean remote ) {
String closeMessage = "Disconnected from server at " + getURI();
MatlabEvent matlab_event = new MatlabEvent( this, closeMessage );
Iterator<MatlabListener> listeners = _listeners.iterator();
while (listeners.hasNext() ) {
( (MatlabListener) listeners.next() ).Close( matlab_event );
for (MatlabListener _listener : _listeners) {
(_listener).Close(matlab_event);
}
}

// Methods for handling MATLAB as a listener, automatically managed.
private List<MatlabListener> _listeners = new ArrayList<MatlabListener>();
private final List<MatlabListener> _listeners = new ArrayList<MatlabListener>();
public synchronized void addMatlabListener( MatlabListener lis ) {
_listeners.add( lis );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,43 @@
import javax.net.ssl.TrustManagerFactory;

import org.java_websocket.WebSocketImpl;
import org.java_websocket.server.DefaultSSLWebSocketServerFactory;

public class MatlabWebSocketSSLClient extends MatlabWebSocketClient {
// The constructor creates a new SSL WebSocketServer with the wildcard IP,
// accepting all connections on the specified port
public MatlabWebSocketSSLClient( URI serverURI, String keystore, String storePassword, String keyPassword ) throws Exception {
super( serverURI );

WebSocketImpl.DEBUG = true;

// Load up the key store
String STORETYPE = "JKS";
String KEYSTORE = keystore;
String STOREPASSWORD = storePassword;
String KEYPASSWORD = keyPassword;
//WebSocketImpl.DEBUG = true;

// Load up the key store
KeyStore ks = KeyStore.getInstance( STORETYPE );
File kf = new File( KEYSTORE );
ks.load( new FileInputStream( kf ), STOREPASSWORD.toCharArray() );

File kf = new File( keystore );
ks.load( new FileInputStream( kf ), storePassword.toCharArray() );
// Initialize KMF and TMF
KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
kmf.init( ks, KEYPASSWORD.toCharArray() );
kmf.init( ks, keyPassword.toCharArray() );
TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
tmf.init( ks );

// Initialize SSLContext
SSLContext sslContext = null;
sslContext = SSLContext.getInstance( "TLS" );
sslContext.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null );

SSLSocketFactory factory = sslContext.getSocketFactory();
// Apply SSL context to client
this.setSocket( factory.createSocket() );
}

public MatlabWebSocketSSLClient( URI serverURI ) throws Exception {
super( serverURI );
//WebSocketImpl.DEBUG = true;

// Initialize SSLContext with java's default key and trust store
SSLContext sslContext = null;
sslContext = SSLContext.getInstance( "TLS" );
sslContext.init( null, null, null );
SSLSocketFactory factory = sslContext.getSocketFactory();
// Apply SSL context to client
this.setSocket( factory.createSocket() );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,23 @@ public class MatlabWebSocketSSLServer extends MatlabWebSocketServer {
// accepting all connections on the specified port
public MatlabWebSocketSSLServer( int port, String keystore, String storePassword, String keyPassword ) throws Exception {
super( port );

WebSocketImpl.DEBUG = true;

// Load up the key store
String STORETYPE = "JKS";
String KEYSTORE = keystore;
String STOREPASSWORD = storePassword;
String KEYPASSWORD = keyPassword;
//WebSocketImpl.DEBUG = false;

// Load up the key store
KeyStore ks = KeyStore.getInstance( STORETYPE );
File kf = new File( KEYSTORE );
ks.load( new FileInputStream( kf ), STOREPASSWORD.toCharArray() );

File kf = new File( keystore );
ks.load( new FileInputStream( kf ), storePassword.toCharArray() );
// Initialize KMF and TMF
KeyManagerFactory kmf = KeyManagerFactory.getInstance( "SunX509" );
kmf.init( ks, KEYPASSWORD.toCharArray() );
kmf.init( ks, keyPassword.toCharArray() );
TrustManagerFactory tmf = TrustManagerFactory.getInstance( "SunX509" );
tmf.init( ks );

// Initialize SSLContext
SSLContext sslContext = null;
sslContext = SSLContext.getInstance( "TLS" );
sslContext.init( kmf.getKeyManagers(), tmf.getTrustManagers(), null );

// Apply SSL context to server
this.setWebSocketFactory( new DefaultSSLWebSocketServerFactory( sslContext ) );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.java_websocket.WebSocket;
Expand All @@ -24,39 +23,35 @@ public void onOpen( WebSocket conn, ClientHandshake handshake ) {
String add = conn.getRemoteSocketAddress().getHostName() + ":" + conn.getRemoteSocketAddress().getPort();
String openMessage = "Client " + conn.hashCode() + " at " + add + " opened a connection";
MatlabEvent matlab_event = new MatlabEvent( this, conn, openMessage );
Iterator<MatlabListener> listeners = _listeners.iterator();
while (listeners.hasNext() ) {
( (MatlabListener) listeners.next() ).Open( matlab_event );
for (MatlabListener _listener : _listeners) {
(_listener).Open(matlab_event);
}
}

// Method handler when a text message has been received from the client
@Override
public void onMessage( WebSocket conn, String message ) {
MatlabEvent matlab_event = new MatlabEvent( this, conn, message );
Iterator<MatlabListener> listeners = _listeners.iterator();
while (listeners.hasNext() ) {
( (MatlabListener) listeners.next() ).TextMessage( matlab_event );
for (MatlabListener _listener : _listeners) {
(_listener).TextMessage(matlab_event);
}
}

// Method handler when a binary message has been received from the client
@Override
public void onMessage( WebSocket conn, ByteBuffer blob ) {
MatlabEvent matlab_event = new MatlabEvent( this, conn, blob );
Iterator<MatlabListener> listeners = _listeners.iterator();
while (listeners.hasNext() ) {
( (MatlabListener) listeners.next() ).BinaryMessage( matlab_event );
for (MatlabListener _listener : _listeners) {
(_listener).BinaryMessage(matlab_event);
}
}

// Method handler when an error has occurred
@Override
public void onError( WebSocket conn, Exception ex ) {
MatlabEvent matlab_event = new MatlabEvent( this, conn, ex.getMessage() );
Iterator<MatlabListener> listeners = _listeners.iterator();
while (listeners.hasNext() ) {
( (MatlabListener) listeners.next() ).Error( matlab_event );
for (MatlabListener _listener : _listeners) {
(_listener).Error(matlab_event);
}
}

Expand All @@ -66,13 +61,12 @@ public void onClose( WebSocket conn, int code, String reason, boolean remote ) {
String add = conn.getRemoteSocketAddress().getHostName() + ":" + conn.getRemoteSocketAddress().getPort();
String closeMessage = remote ? "Client " + conn.hashCode() + " at "+ add + " closed the connection" : "Closed connection to client " + conn.hashCode() + " at " + add;
MatlabEvent matlab_event = new MatlabEvent( this, conn, closeMessage );
Iterator<MatlabListener> listeners = _listeners.iterator();
while (listeners.hasNext() ) {
( (MatlabListener) listeners.next() ).Close( matlab_event );
for (MatlabListener _listener : _listeners) {
(_listener).Close(matlab_event);
}
}

// Retreive a connection by hashcode
// Retrieve a connection by hashcode
public WebSocket getConnection( int hashCode ) {
Collection<WebSocket> conns = connections();
synchronized ( conns ) {
Expand Down Expand Up @@ -141,7 +135,7 @@ public void closeAll() {
}

// Methods for handling MATLAB as a listener, automatically managed
private List<MatlabListener> _listeners = new ArrayList<MatlabListener>();
private final List<MatlabListener> _listeners = new ArrayList<MatlabListener>();
public synchronized void addMatlabListener( MatlabListener lis ) {
_listeners.add( lis );
}
Expand Down
Loading

0 comments on commit a15dd15

Please sign in to comment.