WebSockets are the latest way to provide bi-directional data transfer for HTTP applications. They replace outdated workarounds like AJAX, repeated polling, Comet, etc. WebSockets are a special protocol atop HTTP (which in itself runs over TCP/IP), and can be wrapped in SSL for security (as in HTTPS). Being a Web Technology, it seems to have been developed by the JavaScript people exclusively for the JavaScript people – working with it outside a web browser or Node.js server can seem convoluted. But that’s the world they built, and we just we live in it.
Basic Perl support / documentation for WebSockets was difficult for me to find. The Mojolicious framework (specifically the UserAgent module) has native WebSockets support and can act as a client, but I was looking for info on using with WebSockets on a lower level / without Mojo or other frameworks. Hopefully, this post can shed some light on how you can use Perl to connect to a remote server using WebSockets, and get access to those sweet real-time services using our favorite language.
First off, if you can, you should just use AnyEvent::WebSocket::Client or Net::Async::WebSocket::Client (depending on your preference of async framework). This package has already done the hard work of combining the two packages you’d probably use anyway, Protocol::WebSocket::Client (for encoding / decoding WebSocket frames), and AnyEvent (for cross-platform non-blocking I/O and doing all the messy TCP socket stuff for you). Having already established my status as a Luddite a desire to know what’s really going on, let’s try to reinvent the wheel and write our own client.
A Client for the Echo Test Service
The goal of this project is to interoperate with the WebSocket Echo Server at ws://echo.websocket.org. The Echo Server simply listens to any messages sent to it, and returns the same message to the caller. This is enough to build a simple client that we can then customize for other services. There are two things we need to make this work:
- a plain Internet Socket, for managing the TCP connection to the remote server, and
- a Protocol handler, for encoding / decoding data in the WebSocket format.
The second part of this is already done for us by Protocol::WebSocket::Client: given a stream of bytes, it can identify WebSocket frames and parse them into data from the server, and it can take data from our program and encapsulate it for sending. This tripped me up at first, so pay attention: Protocol::WebSocket does NOT actually do anything with the TCP socket itself – meaning it does not send or receive any data on its own! The class is responsible for only these things: packing/unpacking data, generating a properly formatted handshake for initiating a WebSocket connection, and sending a “close” message to the server signalling intent to disconnect.
Given that Protocol::WebSocket::Client doesn’t do any TCP socket stuff itself, we have to manage all that. Fortunately, there’s the core module IO::Socket::INET which we can use. Protocol::WebSocket::Client also provides some hooks for points in the WebSocket flow, so that we can insert our own handlers at those points. Let’s get started with some code.
Continue reading