-
Notifications
You must be signed in to change notification settings - Fork 121
Seasocks quick tutorial
Seasocks is a tiny embeddable C++ server with WebSockets support.
It'll only likely work on Linux, and requires a somewhat modern C++ compiler with good C++14 support, so something like GCC 5 or a recent clang is needed.
SeaSocks builds using CMake, which can be installed with your favourite package manager pretty simply. Once you've got it installed:
$ mkdir build
$ cd build
$ cmake ..
$ make
And then run one of test apps (which need to run from the root of the project, not the build directory).
$ cd ..
$ build/src/app/c/ws_test
Visit http://localhost:9090/ and see a somewhat uninspiring but very minimal web socket test application. You can control the number by clicking, and if you open several browsers you'll see the number ticks up in each when you click on it.
Everything in Seasocks is in the seasocks
namespace. Unless there's a clash with your own classes, it's convenient to put a using namespace seasocks;
at the top of your C++ files. The code below assumes you've done this. Additionally, we'll assume using namespace std;
to make things shorter.
SeaSocks needs to log stuff from time to time. To control where the logging goes, you'll need an implementation of the Logger
class (from <seasocks/Logger.h>
). If stdout
is good enough for you, just make a PrintfLogger
(from <seasocks/PrintfLogger.h>
).
Create a Server
and give it the logger, then call serve("path", port);
and you're done! serve
blocks forever, or until another thread calls server.terminate();
. For our simple example we'll let Ctrl-C be our killer. Our example then is:
void example() {
auto logger = make_shared<PrintfLogger>();
Server server(logger);
server.serve("web", 9090);
}
To add web sockets support, we need to create a subclass of WebSocket::Handler (from <seasocks/WebSocket.h>
). There are three methods to override: onConnect
, onData
and onDisconnect
. onData
is optional, if you don't care for replies from your clients. When you get called for onConnect
, you're handed a WebSocket
which you can stash away in a list of "connected clients" (until onDisconnect
is called, anyway). This WebSocket
has a method to let you send data to your clients.
A quick side-note on threading: SeaSocks is single-threaded - which makes it quite easy to reason about things. You can be certain your list of clients is up-to-date until onDisconnect
is called. However, this has a drawback: if you need to send messages to clients you have to do it from the SeaSocks thread. This may be inconvenient if you are reacting to asynchronous results coming on other threads, so SeaSocks has a way to execute methods on its own thread: the Server
class has an execute
method which arranges for the supplied Server::Runnable
has its run()
method called on the server thread as soon as possible.
Anyway, armed with your own WebSocket::Handler
, you can register it as a named handler before calling serve
by passing it to server.addWebSocketHandler("/endpoint", handler);
. Then, JavaScript connecting to /endpoint
will talk to your Handler
. Here's a really simple "chat" server:
struct ChatHandler : WebSocket::Handler {
set<WebSocket *> connections;
void onConnect(WebSocket *socket) override
{ connections.insert(socket); }
void onData(WebSocket *, const char *data) override
{ for (auto c : connections) c->send(data); }
void onDisconnect(WebSocket *socket) override
{ connections.erase(socket); }
};
void chat() {
Server server(make_shared<PrintfLogger>());
server.addWebSocketHandler("/chat", make_shared<ChatHandler>());
server.serve("web", 9090);
}
There's a bunch of handy JSON formatters in <server/util/Json.h>
of the form makeArray(...)
or similar.
WebSockets are SeaSocks's main motivation, but static content's possible too. You can add a "page handler" with Server::addPageHandler
, and that handler can serve up its own content (or authenticate, or whatever). There's some helper classes in server/util
to make this a bit easier, have a look at RootPageHandler
and its CrackedUriPageHandler
(which conveniently break URIs up into manageable bits).
As of this this commit, seasocks supports per-message deflate as described in RFC 7692. See the notes in that commit message for some current limitations.
In order to use this feature, it must be enabled. After creating a seasocks::Server
object, call the setPerMessageDeflateEnabled
method:
server.setPerMessageDeflateEnabled(true);