The WebSocket protocol defines a full-duplex single socket connection over which messages can be sent between a client and a server. It simplifies much of the complexity around bi-directional web communication and connection management. WebSocket represents the next evolutionary step in Web communication compared to Comet and Ajax. And of course, Zinc HTTP Components supports Web sockets as you will discover throughout this chapter.
HTTP, one of the main technologies of the internet, defines a communication protocol between a client and a server where the initiative of the communication lies with the client and each interaction consists of a client request and a server response. When correctly implemented and used, HTTP is enormously scaleable and very flexible.
With the arrival of advanced Web applications mimicking regular desktop applications with rich user interfaces, as well as mobile Web applications, it became clear that HTTP was not suitable or not a great fit for two use cases:
Previously, Comet and Ajax were used as (partial) solutions to these use cases. The WebSocket protocol defines a reliable communication channel between two equal parties, typically, but not necessarily, a Web client and a Web server, over which asynchronous messages can be send with very little overhead. Messages can be any String or ByteArray. Overhead is just a couple of bytes. There is no such thing as a direct reply or a synchronous confirmation.
Using WebSockets, a server can notify a client instantly of interesting events, and clients can quickly send small notifications to a server, possibly multiplexing many virtual communications channels over a single network socket.
As a protocol, WebSocket starts with an initial setup handshake that is based on HTTP. The initiative for setting up a WebSocket lies with the client, who is sending a so called connection upgrade request. The upgrade request contains a couple of special HTTP headers. The server begins as a regular HTTP server accepting the connection upgrade request. When the request conforms to the specification, a 101 Switching Protocols response is sent. This response also contains a couple of special HTTP headers. From that point on, the HTTP conversation over the network socket stops and the WebSocket protocol begins.
WebSocket messages consist of one or more frames with minimal encoding. Behind the scenes, a number of control frames are used to properly close the WebSocket and to manage keeping alive the connection using ping and pong frames.
The code implementing Zinc WebSockets resides in a single package called 'Zinc-WebSocket-Core' in the Zinc HTTP Components repository.
There is also an accompanying 'Zinc-WebSocket-Tests' package containing the unit tests.
ConfigurationOfZincHTTPComponents has a
'WebSocket' group that you can load separately.
Here is the loading code snippet:
An endpoint for a WebSocket is specified using an URL:
Two new schemes are defined,
ws:// for regular WebSockets and
wss:// for the secure (TLS/SSL) variant.
Zinc WebSockets supports the usage of client side WebSockets of both the regular and secure variants.
Note that the secure variant requires loading the Zodiac TLS/SSL Pharo project.
The Zinc WebSockets API is really simple: you use
readMessage and finally
close on an open socket.
Here is a client-side example talking to a public echo service:
readMessage is blocking. It always returns a complete
ByteArray, possible assembled out of multiple frames. Inside
readMessage control frames will be handled automagically. Reading and sending are completely separate and independent.
For sending very large messages, there are
sendByteFrames: that take a collection of
ByteArrays to be sent as
different frames of the same message. At the other end, these will be joined together and seen as a single message.
To use secure Web sockets, just use the proper URL scheme
wss:// as in the following example:
Of course, your image has to contain Zodiac and your VM needs access to the proper plugin. That should not be a problem with the latest Pharo releases.
Since the WebSocket protocol starts off as HTTP, it is logical that a ZnServer with a special delegate is the starting point.
ZnWebSocketDelegate implements the standard
handleRequest: to check if the incoming request is a valid WebSocket connection upgrade request. If so, the matching 101 switching protocols response is constructed and sent. From that moment on, the network socket stream is handed over to a new, server side
ZnWebSocketDelegate has two properties. An optional prefix implements a specific path, like
The required handler is any object implementing
value: with the new Web socket as argument.
Let's implement the echo service that we connected to as a client in the previous section. In essence, we should go in a loop, reading a message and sending it back. Here is the code:
We start a default server on port 1701 and replace its delegate with
an instance of
ZnWebSocketDelegate. This instance will pass each correct web socket
request on to its handler. In this example, a block is used as handler. The handler is given a new connected
ZnWebSocket instance. For the echo service, we
go into a repeat loop, reading a message and sending it back.
Finally, you can stop the server using:
The code above works but will eventually encounter two
readMessage call blocks on the socket stream waiting for input until its timeout expires, which will be signaled with a ConnectionTimedOut exception.
In most applications, you should just keep on reading, essentially ignoring the timeout for an infinite wait on incoming messages.
This behavior is implemented in the
ZnWebSocket>>runWith: convenience method: it enters a loop reading messages and passing them to a block, continuing on timeouts. This simplifies our example:
That leaves us with the problem of
ConnectionClosed. This exception can occur at the lowest level when the underlying network connection closes unexpectedly,
or at the WebSocket protocol level when the other end sends a close frame. In either case we have to deal with it as a server. In our trivial echo example, we can catch and ignore any ConnectionClosed exception or log it as follows:
Although using a block as handler is convenient, for non-trivial examples a regular object implementing
value: will probably be better.
You can find such an implementation in
The current process (thread) as spawned by the server can be used freely by the handler code, for as long as the web socket connection lasts. The responsibility for closing the connection lies with the handler, although a close from the other side will be handled correctly.
To test the service, you can use a client-side web socket, like we did in the previous section.
This is what the unit test
The following setup will serve this code:
Now, you can try the following URLs in your Web browser:
The first one will connect to
ws://echo.websocket.org as a reference, the second one will connect to our implementation at
Another simple example is available in
ZnWebSocketStatusHandler where a couple of Smalltalk image statistics are emitted every second for an efficient live view in your browser.
In this scenario, the server accepts each incoming web socket connection and starts streaming to it, not interested in any incoming messages.
Here is the core loop:
Here is code to setup all examples:
Visit http://localhost:1701/ws-status-client to see statistics (uptime, memory...) of your running Pharo lively refreshed in a Web page.
What happened is that your Web browser contacted the Zinc server at
Another available example is
It implements the core logic of a chatroom: clients can send messages to the server which distributes them to all connected clients.
In this case, the handler has to manage a collection of all connected client Web sockets.
Here is the core loop:
Distributing a message basically means iterating over each client:
Here is code to start this Web chat app:
Visit http://localhost:1701/ws-chat-client to access your Web chat application. You can open multiple tabs on this same URL to simulate multiple users on the Chat. You can also send chat messages directly from Pharo, much like a moderator:
All code resides in the
The wire level protocol, the encoding and decoding of frames is in
The key methods are
readFrom: as well as the instance creation protocol. Together with the testing protocol and
printOn: these should give enough information to understand the implementation.
ZnWebSocket implements the protocol above frames, either from a server or a client perspective. The key methods are
readFrame, sending is quite simple.
Client-side setup can be found on the class side of
Server-side handling of the setup is implemented in the class
Two kinds of exceptions,
ZnWebSocketClosed and a shared
ZnWebSocketUtils class round out the core code.
There is a live demo available at http://websocket.stfx.eu with the basic Zinc-WebSocket demos: echo, status & chatroom.
A starting point to learn how these demos were set up is
installExamplesInServer: in the
Setting up a production demo is complicated by the fact that most proxies and load balancers, most notably the market leader Apache, do not (yet) deal correctly with the WebSocket protocol. It is thus easier to organize things such that your client directly talks to your Smalltalk image.
WebSockets integrate smoothly with Zinc HTTP Components to form another part of the Pharo Web stack. It provides support for building modern single page Web applications in Pharo. The implementation of Zinc WebSockets as an add-on to Zinc HTTP Components was made possible in part through financial backing by Andy Burnett of Knowinnovation Inc. and ESUG.