Skip to content

Commit 9b8d6ce

Browse files
author
Benjamin Sergeant
committed
First import
1 parent 65bd7d5 commit 9b8d6ce

17 files changed

+21386
-1
lines changed

LICENSE.txt

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are
5+
met:
6+
7+
1. Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
10+
2. Redistributions in binary form must reproduce the above copyright
11+
notice, this list of conditions and the following disclaimer in the
12+
documentation and/or other materials provided with the
13+
distribution.
14+
15+
3. Neither the name of the copyright holder nor the names of its
16+
contributors may be used to endorse or promote products derived
17+
from this software without specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.md

+163-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,163 @@
1-
# IXWebSocket
1+
# General
2+
3+
## Introduction
4+
5+
[*WebSocket*](https://en.wikipedia.org/wiki/WebSocket) is a computer communications protocol, providing full-duplex
6+
communication channels over a single TCP connection. This library provides a C++ library for Websocket communication. The code is derived from [easywsclient](https://github.com/dhbaird/easywsclient).
7+
8+
## Examples
9+
10+
The examples folder countains a simple chat program, using a node.js broadcast server.
11+
12+
Here is what the API looks like.
13+
14+
```
15+
ix::WebSocket webSocket;
16+
17+
std::string url("ws://localhost:8080/");
18+
webSocket.configure(url);
19+
20+
// Setup a callback to be fired when a message or an event (open, close, error) is received
21+
webSocket.setOnMessageCallback(
22+
[](ix::WebSocketMessageType messageType, const std::string& str, ix::WebSocketErrorInfo error)
23+
{
24+
if (messageType == ix::WebSocket_MessageType_Message)
25+
{
26+
std::cout << str << std::endl;
27+
}
28+
});
29+
30+
// Now that our callback is setup, we can start our background thread and receive messages
31+
webSocket.start();
32+
33+
// Send a message to the server
34+
webSocket.send("hello world");
35+
36+
// ... finally ...
37+
38+
// Stop the connection
39+
webSocket:stop()
40+
```
41+
42+
## Implementation details
43+
44+
### TLS/SSL
45+
46+
Connections can be optionally secured and encrypted with TLS/SSL when using a wss:// endpoint, or using normal un-encrypted socket with ws:// endpoints. AppleSSL is used on iOS and OpenSSL is used on Android.
47+
48+
### Polling and background thread work
49+
50+
No manual polling to fetch data is required. Data is sent and received instantly by using a background thread for receiving data and the select [system](http://man7.org/linux/man-pages/man2/select.2.html) call to be notified by the OS of incoming data. No timeout is used for select so that the background thread is only woken up when data is available, to optimize battery life. This is also the recommended way of using select according to the select tutorial, section [select law](https://linux.die.net/man/2/select_tut). Read and Writes to the socket are non blocking. Data is sent right away and not enqueued by writing directly to the socket, which is [possible](https://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid) since system socket implementations allow concurrent read/writes. However concurrent writes need to be protected with mutex.
51+
52+
### Automatic reconnection
53+
54+
If the remote end (server) breaks the connection, the code will try to perpetually reconnect, by using an exponential backoff strategy, capped at one retry every 10 seconds.
55+
56+
## Limitations
57+
58+
* There is no per message compression support. That could be useful for retrieving large messages, but could also be implemented at the application level. However that would conflict with auto-serialiasation.
59+
* There is no text support for sending data, only the binary protocol is supported. Sending json or text over the binary protocol works well.
60+
* Automatic reconnection works at the TCP socket level, and will detect remote end disconnects. However, if the device/computer network become unreachable (by turning off wifi), it is quite hard to reliably and timely detect it at the socket level using `recv` and `send` error codes. [Here](https://stackoverflow.com/questions/14782143/linux-socket-how-to-detect-disconnected-network-in-a-client-program) is a good discussion on the subject. This behavior is consistent with other runtimes such as node.js. One way to detect a disconnected device with low level C code is to do a name resolution with DNS but this can be expensive. Mobile devices have good and reliable API to do that.
61+
62+
## Examples
63+
64+
1. Bring up a terminal and jump to the examples folder.
65+
2. Compile the example C++ code. `sh build.sh`
66+
3. Install node.js from [here](https://nodejs.org/en/download/).
67+
4. Type `npm install` to install the node.js dependencies. Then `node broadcast-server.js` to run the server.
68+
5. Bring up a second terminal. `env USER=bob ./cmd_websocket_chat`
69+
6. Bring up a third terminal. `env USER=bill ./cmd_websocket_chat`
70+
7. Start typing things in any of those terminals. Hopefully you should see your message being received on the other end.
71+
72+
## C++ code organization
73+
74+
Here's a simplistic diagram which explains how the code is structured in term of class/modules.
75+
76+
```
77+
+-----------------------+
78+
| | Start the receiving Background thread. Auto reconnection. Simple websocket Ping.
79+
| IXWebSocket | Interface used by C++ test clients. No IX dependencies.
80+
| |
81+
+-----------------------+
82+
| |
83+
| IXWebSocketTransport | Low level websocket code, framing, managing raw socket. Adapted from easywsclient.
84+
| |
85+
+-----------------------+
86+
| |
87+
| IXWebSocket | ws:// Unencrypted Socket handler
88+
| IXWebSocketAppleSSL | wss:// TLS encrypted Socket AppleSSL handler. Used on iOS and macOS
89+
| IXWebSocketOpenSSL | wss:// TLS encrypted Socket OpenSSL handler. Used on Android and Linux
90+
| | Can be used on macOS too.
91+
+-----------------------+
92+
```
93+
94+
## Advanced usage
95+
96+
### Sending messages
97+
98+
`websocket:send("foo")` will send a message.
99+
100+
If the connection was closed and sending failed, the return value will be set to false.
101+
102+
### ReadyState
103+
104+
`getReadyState()` returns the state of the connection. There are 4 possible states.
105+
106+
1. WebSocket_ReadyState_Connecting - The connection is not yet open.
107+
2. WebSocket_ReadyState_Open - The connection is open and ready to communicate.
108+
3. WebSocket_ReadyState_Closing - The connection is in the process of closing.
109+
4. WebSocket_MessageType_Close - The connection is closed or couldn't be opened.
110+
111+
### Open and Close notifications
112+
113+
The onMessage event will be fired when the connection is opened or closed. This is similar to the [Javascript browser API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket), which has `open` and `close` events notification that can be registered with the browser `addEventListener`.
114+
115+
```
116+
webSocket.setOnMessageCallback(
117+
[this](ix::WebSocketMessageType messageType, const std::string& str, ix::WebSocketErrorInfo error)
118+
{
119+
if (messageType == ix::WebSocket_MessageType_Open)
120+
{
121+
puts("send greetings");
122+
}
123+
else if (messageType == ix::WebSocket_MessageType_Close)
124+
{
125+
puts("disconnected");
126+
}
127+
}
128+
);
129+
```
130+
131+
### Error notification
132+
133+
A message will be fired when there is an error with the connection. The message type will be `ix::WebSocket_MessageType_Error`. Multiple fields will be available on the event to describe the error.
134+
135+
```
136+
webSocket.setOnMessageCallback(
137+
[this](ix::WebSocketMessageType messageType, const std::string& str, ix::WebSocketErrorInfo error)
138+
{
139+
if (messageType == ix::WebSocket_MessageType_Error)
140+
{
141+
ss << "cmd_websocket_chat: Error ! " << error.reason << std::endl;
142+
ss << "#retries: " << event.retries << std::endl;
143+
ss << "Wait time(ms): " << event.waitTime << std::endl;
144+
ss << "HTTP Status: " << event.httpStatus << std::endl;
145+
}
146+
}
147+
);
148+
```
149+
150+
### start, stop
151+
152+
1. `websocket.start()` connect to the remote server and starts the message receiving background thread.
153+
2. `websocket.stop()` disconnect from the remote server and closes the background thread.
154+
155+
### Configuring the remote url
156+
157+
The url can be set and queried after a websocket object has been created. You will have to call `stop` and `start` if you want to disconnect and connect to that new url.
158+
159+
```
160+
std::string url = 'wss://example.com'
161+
websocket.configure(url);
162+
assert(websocket:getUrl() == url)
163+
```

examples/broadcast-server.js

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* cmd_websocket_chat.cpp
3+
* Author: Benjamin Sergeant
4+
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
5+
*/
6+
const WebSocket = require('ws');
7+
8+
const wss = new WebSocket.Server({ port: 8080 });
9+
10+
// Broadcast to all.
11+
wss.broadcast = function broadcast(data) {
12+
wss.clients.forEach(function each(client) {
13+
if (client.readyState === WebSocket.OPEN) {
14+
client.send(data);
15+
}
16+
});
17+
};
18+
19+
wss.on('connection', function connection(ws) {
20+
ws.on('message', function incoming(data) {
21+
// Broadcast to everyone else.
22+
wss.clients.forEach(function each(client) {
23+
if (client !== ws && client.readyState === WebSocket.OPEN) {
24+
client.send(data);
25+
}
26+
});
27+
});
28+
});

examples/build.sh

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/sh
2+
#
3+
# Author: Benjamin Sergeant
4+
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
5+
#
6+
7+
clang++ --std=c++11 --stdlib=libc++ \
8+
../ixwebsocket/IXSocket.cpp \
9+
../ixwebsocket/IXWebSocketTransport.cpp \
10+
../ixwebsocket/IXSocketAppleSSL.cpp \
11+
../ixwebsocket/IXWebSocket.cpp \
12+
cmd_websocket_chat.cpp \
13+
-o cmd_websocket_chat \
14+
-framework Security \
15+
-framework Foundation

0 commit comments

Comments
 (0)