Netty has had WebSocket support for quite a while now, and it is easy to get up an running quickly with the existing examples. The example provided with Netty, consists of a handler which takes care of the HTTP Upgrade handshake, and also manages the WebSocket protocol. If you need to service http requests, for example serving static files, in addition to WebSocket calls then this approach works great.
But if all you want to do is create a custom application protocol on top of WebSocket and only service WebSocket requests, then the above solution is a fair amount of code that you'll have to maintain yourself. So, we set out to try to simplify this and this post describes what we came up with.
Handling a TextSocketFrame
Below is an example of implementing a handler that will receive a TextWebSocketFrame :
public class CustomTextFrameHandler extends ChannelInboundMessageHandlerAdapter<TextWebSocketFrame> { | |
@Override | |
public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception { | |
String request = frame.getText(); | |
ctx.channel().write(new TextWebSocketFrame(request.toUpperCase())); | |
} | |
} |
The text content is retrieved by calling getText, and can then be parsed as required by the application protocol. This might involve parsing the String as JSON or whatever the protocol in question dictates.
Bootstrapping Netty
Now, lets take a look at what is needed to bootstrap Netty:
public static void main(String[] args) throws Exception { | |
final ServerBootstrap sb = new ServerBootstrap(); | |
try { | |
sb.group(new NioEventLoopGroup(), new NioEventLoopGroup()) | |
.channel(NioServerSocketChannel.class) | |
.localAddress(new InetSocketAddress(port)) | |
.childHandler(new ChannelInitializer<SocketChannel>() { | |
@Override | |
public void initChannel(final SocketChannel ch) throws Exception { | |
ch.pipeline().addLast( | |
new HttpRequestDecoder(), | |
new HttpChunkAggregator(65536), | |
new HttpResponseEncoder(), | |
new WebSocketServerProtocolHandler("/websocket"), | |
new CustomTextFrameHandler()); | |
} | |
}); | |
final Channel ch = sb.bind().sync().channel(); | |
System.out.println("Web socket server started at port " + port); | |
ch.closeFuture().sync(); | |
} finally { | |
sb.shutdown(); | |
} | |
} |
As you can see the CustomTextFrameHandler from the previous code example is the last handler added, and right before it, we have a new handler named WebSocketServerProtocolHandler.
An example has been included in Netty which uses the above code for the server side, and a simple html page as the WebSocket client.
Netty 3.x Support
This feature has also been back-ported to Netty 3.x. The CustomTextFrameHandler class would then look something like this:
public class CustomTextFrameHandler extends SimpleChannelHandler { | |
@Override | |
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { | |
if (e.getMessage() instanceof TextWebSocketFrame) { | |
TextWebSocketFrame frame = (TextWebSocketFrame) e.getMessage(); | |
ctx.getChannel().write(new TextWebSocketFrame(frame.getText().toUpperCase())); | |
} | |
} | |
} |
An example has been included in Netty which uses the above code for the server side, and a simple html page as the WebSocket client.