> 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.