Skip to content

Commit a388817

Browse files
author
Peter Thorson
committed
Initial work on utility_server tutorial
1 parent d080299 commit a388817

File tree

1 file changed

+181
-0
lines changed

1 file changed

+181
-0
lines changed
+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
Utility Server Example Application Tutorial
2+
===========================================
3+
4+
Introduction
5+
------------
6+
7+
This tutorial provides a step by step discussion of building a basic WebSocket++ server. The final product of this tutorial is the utility_server example application from the example section. This server demonstrates the following features:
8+
9+
- Use Asio Transport for networking
10+
- Accept multiple WebSocket connections at once
11+
- Read incoming messages and perform a few basic actions (echo, broadcast, telemetry, server commands) based on the path
12+
- Use validate handler to reject connections to invalid paths
13+
- Serve basic HTTP responses with the http handler
14+
- Gracefully exit the server
15+
- Encrypt connections with TLS
16+
17+
This tutorial is current as of the 0.6.x version of the library.
18+
19+
Chapter 1: Initial Setup & Basics
20+
---------------------------------
21+
22+
### Step 1
23+
24+
_Add WebSocket++ includes and set up a a server endpoint type._
25+
26+
WebSocket++ includes two major object types. The endpoint and the connection. The
27+
endpoint creates and launches new connections and maintains default settings for
28+
those connections. Endpoints also manage any shared network resources.
29+
30+
The connection stores information specific to each WebSocket session.
31+
32+
> **Note:** Once a connection is launched, there is no link between the endpoint and the connection. All default settings are copied into the new connection by the endpoint. Changing default settings on an endpoint will only affect future connections.
33+
Connections do not maintain a link back to their associated endpoint. Endpoints do not maintain a list of outstanding connections. If your application needs to iterate over all connections it will need to maintain a list of them itself.
34+
35+
WebSocket++ endpoints are built by combining an endpoint role with an endpoint config. There are two different types of endpoint roles, one each for the client and server roles in a WebSocket session. This is a server tutorial so we will use the server role `websocketpp::server` which is provided by the `<websocketpp/server.hpp>` header.
36+
37+
> #### Terminology: Endpoint Config
38+
> WebSocket++ endpoints have a group of settings that may be configured at compile time via the `config` template parameter. A config is a struct that contains types and static constants that are used to produce an endpoint with specific properties. Depending on which config is being used the endpoint will have different methods available and may have additional third party dependencies.
39+
40+
The endpoint role takes a template parameter called `config` that is used to configure the behavior of endpoint at compile time. For this example we are going to use a default config provided by the library called `asio`, provided by `<websocketpp/config/asio_no_tls.hpp>`. This is a server config that uses the Asio library to provide network transport and does not support TLS based security. Later on we will discuss how to introduce TLS based security into a WebSocket++ application, more about the other stock configs, and how to build your own custom configs.
41+
42+
Combine a config with an endpoint role to produce a fully configured endpoint. This type will be used frequently so I would recommend a typedef here.
43+
44+
`typedef websocketpp::server<websocketpp::config::asio> server`
45+
46+
#### `utility_server` constructor
47+
48+
This endpoint type will be the base of the utility_server object that will keep track of the state of the server. Within the `utility_server` constructor several things happen:
49+
50+
First, we adjust the endpoint logging behavior to include all error logging channels and all access logging channels except the frame payload, which is particularly noisy and generally useful only for debugging. [TODO: link to more information about logging]
51+
52+
~~~{.cpp}
53+
m_endpoint.set_error_channels(websocketpp::log::elevel::all);
54+
m_endpoint.set_access_channels(websocketpp::log::alevel::all ^ websocketpp::log::alevel::frame_payload);
55+
~~~
56+
57+
Next, we initialize the transport system underlying the endpoint. This method is specific to the Asio transport not WebSocket++ core. It will not be necessary or present in endpoints that use a non-asio config.
58+
59+
> **Note:** This example uses an internal Asio `io_service` that is managed by the endpoint itself. This is a simple arrangement suitable for programs where WebSocket++ is the only code using Asio. If you have an existing program that already manages an `io_service` object or want to build a new program where WebSocket++ handlers share an io_service with other handlers you can pass the `io_service` you want WebSocket++ to register its handlers on to the `init_asio()` method and it will use it instead of generating and managing its own. [TODO: FAQ link instead?]
60+
61+
~~~{.cpp}
62+
m_endpoint.init_asio();
63+
~~~
64+
65+
#### `utility_server::run` method
66+
67+
In addition to the constructor, we also add a run method that sets up the listening socket, begins accepting connections, starts the Asio io_service event loop.
68+
69+
~~~{.cpp}
70+
// Listen on port 9002
71+
m_endpoint.listen(9002);
72+
73+
// Queues a connection accept operation
74+
m_endpoint.start_accept();
75+
76+
// Start the Asio io_service run loop
77+
m_endpoint.run();
78+
~~~
79+
80+
The final line, `m_endpoint.run();`, will block until the endpoint is instructed to stop listening for new connections. While running it will listen for and process new connections as well as accept and process new data and control messages for existing connections. WebSocket++ uses Asio in an asyncronous mode where multiple connections can be similtaneously serviced efficiently within a single thread.
81+
82+
#### Build
83+
Adding WebSocket++ has added a few dependencies to our program that must be addressed in the build system. Firstly, the WebSocket++ library headers need must be in the include search path of your build system. How exactly this is done depends on where you have the WebSocket++ headers installed what build system you are using.
84+
85+
For the rest of this tutorial we are going to assume a C++11 build environment. WebSocket++ will work with pre-C++11 systems if your build system has access to a recent version of the Boost library headers.
86+
87+
Finally, to use the Asio transport config we need to bring in the Asio library. There are two options here. If you have access to a C++11 build environment the standalone version from http://think-async.com is a good option. This header only library does not bring in any special dependencies and ensures you have the latest version of Asio. If you do not have a C++11 build environment or already have brought in the Boost libraries you can also use the version of Asio bundled with Boost.
88+
89+
To use standalone Asio, make sure the Asio headers are in your include path and define ASIO_STANDALONE. To use Boost Asio, make sure the Boost headers are in your include path and that you are linking to the boost_system library.
90+
91+
`c++ -std=c++11 step1.cpp` (Asio Standalone)
92+
OR
93+
`c++ -std=c++11 step1.cpp -lboost_system` (Boost Asio)
94+
95+
#### Code so far
96+
```cpp
97+
// The ASIO_STANDALONE define is necessary to use the standalone version of Asio.
98+
// Remove if you are using Boost Asio.
99+
#define ASIO_STANDALONE
100+
101+
#include <websocketpp/config/asio_no_tls.hpp>
102+
#include <websocketpp/server.hpp>
103+
104+
#include <functional>
105+
106+
typedef websocketpp::server<websocketpp::config::asio> server;
107+
108+
class utility_server {
109+
public:
110+
utility_server() {
111+
// Set logging settings
112+
m_endpoint.set_error_channels(websocketpp::log::elevel::all);
113+
m_endpoint.set_access_channels(websocketpp::log::alevel::all ^ websocketpp::log::alevel::frame_payload);
114+
115+
// Initialize Asio
116+
m_endpoint.init_asio();
117+
}
118+
119+
void run() {
120+
// Listen on port 9002
121+
m_endpoint.listen(9002);
122+
123+
// Queues a connection accept operation
124+
m_endpoint.start_accept();
125+
126+
// Start the Asio io_service run loop
127+
m_endpoint.run();
128+
}
129+
private:
130+
server m_endpoint;
131+
};
132+
133+
int main() {
134+
utility_server s;
135+
s.run();
136+
return 0;
137+
}
138+
```
139+
140+
### Step 2
141+
142+
_Set up a message handler to echo all replies back to the original user_
143+
144+
#### Setting a message handler
145+
146+
> ###### Terminology: Registering handlers
147+
> WebSocket++ provides a number of execution points where you can register to have a handler run. Which of these points are available to your endpoint will depend on its config. TLS handlers will not exist on non-TLS endpoints for example. A complete list of handlers can be found at http://www.zaphoyd.com/websocketpp/manual/reference/handler-list.
148+
>
149+
> Handlers can be registered at the endpoint level and at the connection level. Endpoint handlers are copied into new connections as they are created. Changing an endpoint handler will affect only future connections. Handlers registered at the connection level will be bound to that specific connection only.
150+
>
151+
> The signature of handler binding methods is the same for endpoints and connections. The format is: `set_*_handler(...)`. Where * is the name of the handler. For example, `set_open_handler(...)` will set the handler to be called when a new connection is open. `set_fail_handler(...)` will set the handler to be called when a connection fails to connect.
152+
>
153+
> All handlers take one argument, a callable type that can be converted to a `std::function` with the correct count and type of arguments. You can pass free functions, functors, and Lambdas with matching argument lists as handlers. In addition, you can use `std::bind` (or `boost::bind`) to register functions with non-matching argument lists. This is useful for passing additional parameters not present in the handler signature or member functions that need to carry a 'this' pointer.
154+
>
155+
> The function signature of each handler can be looked up in the list above in the manual. In general, all handlers include the `connection_hdl` identifying which connection this even is associated with as the first parameter. Some handlers (such as the message handler) include additional parameters. Most handlers have a void return value but some (`validate`, `ping`, `tls_init`) do not. The specific meanings of the return values are documented in the handler list linked above.
156+
157+
158+
### Step 3
159+
160+
_error handling_
161+
162+
163+
### Step 4
164+
165+
_Set up open and close handlers and a connection data structure_
166+
167+
### Step 5
168+
169+
_Change the message handler for connections based on URI and add a validate handler to reject invalid URIs_
170+
171+
### Step 6
172+
173+
_Add some Admin commands (report total clients, cleanly shut down server)_
174+
175+
### Step 7
176+
177+
_Add some Broadcast commands_
178+
179+
### Step 8
180+
181+
_Add TLS_

0 commit comments

Comments
 (0)