WebSocket
This chapter covers
- The concept of a real-time web
- The WebSocket protocol
- Building a WebSocket-based chat room server with Netty
So for now we’ll accept the following non-authoritative description from Wikipedia as adequate:
The real-time web is a network web using technologies and practices that enable users to receive information as soon as it is published by its authors, rather than requiring that they or their software check a source periodically for updates.
Introducing WebSocket
The WebSocket protocol was designed from the ground up to provide a practical solution to the problem of bidirectional data transmission on the web, allowing client and server to transmit messages at any time and, consequently, requiring them to handle message receipt asynchronously.
Our example WebSocket application
Figure 12.1 illustrates the application logic:
1 A client sends a message.
2 The message is broadcast to all other connected clients.
Adding WebSocket support
A mechanism known as the upgrade handshake is used to switch from standard HTTP or HTTPS protocol to WebSocket.
Figure 12.2 illustrates the server logic, which, as always in Netty, will be implemented by a set of ChannelHandlers
.
Handling HTTP requests
The following list shows the code for HttpRequestHandler
, which extends SimpleChannelInboundHandler
for FullHttpRequest
messages. Notice how the implementation of channelRead0()
forwards any requests for the URI /ws
.
This represents the first part of the chat server, which manages pure HTTP requests and responses. Next we’ll handle the WebSocket frames, which transmit the actual chat messages.
WEBSOCKET FRAMES
WebSockets transmit data in frames, each of which represents a part of a message. A complete message may consist of many frames.
Handling WebSocket frames
The WebSocket RFC, published by the IETF, defines six frames; Netty provides a POJO implementation for each of them. The following table lists the frame types and describes their use.
Our chat application will use the following frame types:
CloseWebSocketFrame
PingWebSocketFrame
PongWebSocketFrame
TextWebSocketFrame
TextWebSocketFrame
is the only one we actually need to handle. In conformity with the WebSocket RFC, Netty provides a WebSocketServerProtocolHandler
to manage the others.
The following listing shows our ChannelInboundHandler
for TextWebSocketFrames
, which will also track all the active WebSocket connections in its ChannelGroup.
Initializing the ChannelPipeline
The following listing shows the code for the resulting ChatServerInitializer
.
The call to initChannel()
sets up the ChannelPipeline
of the newly registered Channel
by installing all the required ChannelHandlers
. These are summarized in the following table, along with their individual responsibilities.
The state of the pipeline before the upgrade is illustrated in figure 12.3.
Figure 12.4 shows the ChannelPipeline
after these operations have completed.
Bootstrapping
The final piece of the picture is the code that bootstraps the server and installs the ChatServerInitializer
. This will be handled by the ChatServer
class.
Testing the application
We’ll use the following Maven command to build and start the server:1
mvn -PChatServer clean package exec:exec
To use a different port, you can either edit the value in the file or override it with a System property:1
mvn -PChatServer -Dport=1111 clean package exec:exec
Figure 12.5 shows the UI in the Chrome browser.
The figure shows two connected clients. The first is connected using the interface at the top. The second client is connected via the Chrome browser’s command line at the bottom. You’ll notice that there are messages sent from both clients, and each message is displayed to both.
What about encryption?
The following listing shows how this can be done by extending our ChatServerInitializer
to create a SecureChatServerInitializer
.
The final step is to adapt the ChatServer
to use the SecureChatServerInitializer
so as to install the SslHandler
in the pipeline. This gives us the SecureChatServer
shown here.