> Docs > WebSocket Server
A WebSocketServer needs to be attached to an HttpServer. The WebSocketServer must be created and configured before the HttpServer starts.
HttpServer httpServer = new HttpServer( req->HttpResponse.file(200, "/tmp/ws.test.html") ); httpServer.conf().trafficDump( System.out::print ); WebSocketHandler wsHandler = new MyWsHandler(); WebSocketServer wsServer = new WebSocketServer( httpServer, wsHandler ); wsServer.conf().trafficDump( System.err::print ); httpServer.start();
See WebSocketServerConf for config options.
You can use this simple client for testing:
/tmp/ws.test.html
<html>
<head>
<script type="text/javascript">
window.onload = function()
{
ws = new WebSocket("ws://localhost:8080");
ws.onmessage = function(e){ log(e.data ); };
ws.onclose = function(e){ log("CLOSE"); };
ws.onerror = function(e){ log("ERROR"); };
};
function log(s){ document.getElementById("log").innerHTML += s+"\n"; }
</script>
</head>
<body>
<h1>WebSocket Test</h1>
<h2>Send:</h2>
<input onkeydown="if(event.keyCode==13){ws.send(value);value='';}">
<h2>Received:</h2>
<pre id="log"></pre>
</body>
</html>
Each WebSocketServer contains a WebSocketHandler that handles WebSocket handshakes. The WebSocketHandler interface contains a single abstract method
Async<WebSocketResponse> handle(WebSocketRequest request)
For each handshake request, the handler generates either a Reject or an Accept response. For example,
public class MyWsHandler implements WebSocketHandler
{
@Override
public Async<WebSocketResponse> handle(WebSocketRequest request)
{
if( isBanned(request.ip()) )
return WebSocketResponse.reject(403, "permission denied");
else
return WebSocketResponse.accept( this::handleChannel );
}
Async<Void> handleChannel(WebSocketChannel channel)
{
...
}
}
Note that in the default configuration, same-origin is enforced by the server before handle() is called.
If the handshake is accepted, the Accept response specifies a channelHandler. In the example above, the channel handler is this::handleChannel. A new WebSocketChannel is created after handshake and fed to the channel handler.
A WebSocketChannel contains methods to read and write WebSocketMessage.
Async<WebSocketMessage> readMessage();
Async<Long> writeMessage(WebSocketMessage message);
For example, a simple echo server:
Async<Void> handleChannel(WebSocketChannel channel)
{
return AsyncIterator
.forEach_( channel::readMessage, channel::writeMessage )
.finally_( channel::close );
}
Note that if the client closes the channel gracefully, readMessage() action completes with WebSocketClose which is a subtype of End.
You may want to use the more convenient methods like readText(max) and writeText(chars)
Async<Void> handleChannel(WebSocketChannel channel)
{
AsyncIterator<String> inputs = ()->channel.readText(1000);
return inputs
.map( this::process )
.forEach_( channel::writeText )
.finally_( channel::close );
}
String process(String input)
{
switch(input)
{
case "time" : return Instant.now().toString();
case "user" : return System.getProperty("user.name");
default : return "pardon?";
}
}
An example of a simple chat server:
Async<Void> handleChannel(WebSocketChannel channel)
{
allChannels.put(channel, "");
return AsyncIterator
.forEach_( ()->channel.readText(1000), this::broadcast )
.finally_( ()->allChannels.remove(channel) )
.finally_( channel::close );
}
final ConcurrentHashMap<WebSocketChannel,String> allChannels = new ConcurrentHashMap<>();
Async<Void> broadcast(String msg)
{
for(WebSocketChannel channel : allChannels.keySet())
channel.writeText(msg); // don't wait for write completion
return Async.VOID;
}
A WebSocketMessage is a subtype of ByteSource. You can read an incoming message in streaming-style:
Async<WebSocketMessage> asyncMsg = channel.readMessage();
asyncMsg.then( msg->
AsyncIterator.forEach( msg::read, System.out::println )
);
To create an outgoing message, see static methods in WebSocketMessage. For example, to send a file
ByteSource src = new FileByteSource("/tmp/abc.txt");
WebSocketMessage msg = WebSocketMessage.text( src );
channel.writeMessage( msg );
Use HotWebSocketHandler to hot-reload the server app when source code changes.
Usually, a WebSocket app works together with an HTTP app; they share the same code and the same classloader. Therefore we want the two to share the same HotReloader.
HotReloader reloader = new HotReloader().onJavaFiles(SRC_DIR); HotHttpHandler httpHandler = new HotHttpHandler(reloader, MyHttpHandler.class); HotWebSocketHandler wsHandler = new HotWebSocketHandler(reloader, MyWsHandler.class);
After reloading, new WebSocket channels will be handled by the new handler instance; previous channels are still being handled by the old handler instance until they are closed. A client needs to reconnect (usually by refreshing browser window) to see the effect of reloading.