Skip to content

Commit bcae7f3

Browse files
committed
(ws) Add a new ws sub-command, echo_client. This command send a message to an echo server, and send back to a server whatever message it does receive. When connecting to a local ws echo_server, on my MacBook Pro 2015 I can send/receive around 30,000 messages per second. (cf #235)
1 parent d719c41 commit bcae7f3

File tree

9 files changed

+160
-4
lines changed

9 files changed

+160
-4
lines changed

docs/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# Changelog
22
All changes to this project will be documented in this file.
33

4+
## [10.1.4] - 2020-08-02
5+
6+
(ws) Add a new ws sub-command, echo_client. This command send a message to an echo server, and send back to a server whatever message it does receive. When connecting to a local ws echo_server, on my MacBook Pro 2015 I can send/receive around 30,000 messages per second. (cf #235)
7+
8+
## [10.1.3] - 2020-08-02
9+
10+
(ws) ws echo_server. Add a -q option to only enable warning and error log levels. This is useful for bench-marking so that we do not print a lot of things on the console. (cf #235)
11+
412
## [10.1.2] - 2020-07-31
513

614
(build) make using zlib optional, with the caveat that some http and websocket features are not available when zlib is absent

ixwebsocket/IXWebSocketVersion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@
66

77
#pragma once
88

9-
#define IX_WEBSOCKET_VERSION "10.1.2"
9+
#define IX_WEBSOCKET_VERSION "10.1.4"

makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ ws:
3434
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && ninja install)
3535

3636
ws_install:
37-
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. && make -j 4 install)
37+
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install)
38+
39+
ws_install_release:
40+
mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_BUILD_TYPE=MinSizeRel -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 .. -DUSE_TEST=0 && ninja install)
3841

3942
ws_openssl:
4043
mkdir -p build && (cd build ; cmake -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHON=1 -DUSE_TLS=1 -DUSE_WS=1 -DUSE_OPEN_SSL=1 .. ; make -j 4)

test/compatibility/python/websockets/echo_server.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
async def echo(websocket, path):
1111
while True:
1212
msg = await websocket.recv()
13-
print(f'Received {len(msg)} bytes')
13+
# print(f'Received {len(msg)} bytes')
1414
await websocket.send(msg)
1515

1616
host = os.getenv('BIND_HOST', 'localhost')

ws/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ add_executable(ws
5151
ws_ping_pong.cpp
5252
ws_broadcast_server.cpp
5353
ws_echo_server.cpp
54+
ws_echo_client.cpp
5455
ws_chat.cpp
5556
ws_connect.cpp
5657
ws_transfer.cpp

ws/ws.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ int main(int argc, char** argv)
125125
std::string logfile;
126126
std::string scriptPath;
127127
std::string republishChannel;
128+
std::string sendMsg("hello world");
128129
ix::SocketTLSOptions tlsOptions;
129130
ix::CobraConfig cobraConfig;
130131
ix::CobraBotConfig cobraBotConfig;
@@ -243,6 +244,18 @@ int main(int argc, char** argv)
243244
connectApp->add_option("--subprotocol", subprotocol, "Subprotocol");
244245
addTLSOptions(connectApp);
245246

247+
CLI::App* echoClientApp =
248+
app.add_subcommand("echo_client", "Echo messages sent by a remote server");
249+
echoClientApp->fallthrough();
250+
echoClientApp->add_option("url", url, "Connection url")->required();
251+
echoClientApp->add_flag("-x", disablePerMessageDeflate, "Disable per message deflate");
252+
echoClientApp->add_flag("-b", binaryMode, "Send in binary mode");
253+
echoClientApp->add_option(
254+
"--ping_interval", pingIntervalSecs, "Interval between sending pings");
255+
echoClientApp->add_option("--subprotocol", subprotocol, "Subprotocol");
256+
echoClientApp->add_option("--send_msg", sendMsg, "Send message");
257+
addTLSOptions(echoClientApp);
258+
246259
CLI::App* chatApp = app.add_subcommand("chat", "Group chat");
247260
chatApp->fallthrough();
248261
chatApp->add_option("url", url, "Connection url")->required();
@@ -504,6 +517,16 @@ int main(int argc, char** argv)
504517
subprotocol,
505518
pingIntervalSecs);
506519
}
520+
else if (app.got_subcommand("echo_client"))
521+
{
522+
ret = ix::ws_echo_client(url,
523+
disablePerMessageDeflate,
524+
binaryMode,
525+
tlsOptions,
526+
subprotocol,
527+
pingIntervalSecs,
528+
sendMsg);
529+
}
507530
else if (app.got_subcommand("echo_server"))
508531
{
509532
ret = ix::ws_echo_server_main(

ws/ws.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ namespace ix
5454
const std::string& subprotocol,
5555
int pingIntervalSecs);
5656

57+
int ws_echo_client(const std::string& url,
58+
bool disablePerMessageDeflate,
59+
bool binaryMode,
60+
const ix::SocketTLSOptions& tlsOptions,
61+
const std::string& subprotocol,
62+
int pingIntervalSecs,
63+
const std::string& sendMsg);
64+
5765
int ws_receive_main(const std::string& url,
5866
bool enablePerMessageDeflate,
5967
int delayMs,

ws/ws_connect.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ namespace ix
160160
std::stringstream ss;
161161
if (msg->type == ix::WebSocketMessageType::Open)
162162
{
163-
log("ws_connect: connected");
163+
spdlog::info("ws_connect: connected");
164164
spdlog::info("Uri: {}", msg->openInfo.uri);
165165
spdlog::info("Headers:");
166166
for (auto it : msg->openInfo.headers)

ws/ws_echo_client.cpp

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* ws_echo_client.cpp
3+
* Author: Benjamin Sergeant
4+
* Copyright (c) 2020 Machine Zone, Inc. All rights reserved.
5+
*/
6+
7+
#include <iostream>
8+
#include <ixcore/utils/IXCoreLogger.h>
9+
#include <ixwebsocket/IXNetSystem.h>
10+
#include <ixwebsocket/IXSetThreadName.h>
11+
#include <ixwebsocket/IXWebSocket.h>
12+
#include <spdlog/spdlog.h>
13+
#include <sstream>
14+
#include <thread>
15+
16+
namespace ix
17+
{
18+
int ws_echo_client(const std::string& url,
19+
bool disablePerMessageDeflate,
20+
bool binaryMode,
21+
const ix::SocketTLSOptions& tlsOptions,
22+
const std::string& subprotocol,
23+
int pingIntervalSecs,
24+
const std::string& sendMsg)
25+
{
26+
// Our websocket object
27+
ix::WebSocket webSocket;
28+
29+
webSocket.setUrl(url);
30+
webSocket.setTLSOptions(tlsOptions);
31+
webSocket.setPingInterval(pingIntervalSecs);
32+
33+
if (disablePerMessageDeflate)
34+
{
35+
webSocket.disablePerMessageDeflate();
36+
}
37+
38+
if (!subprotocol.empty())
39+
{
40+
webSocket.addSubProtocol(subprotocol);
41+
}
42+
43+
std::atomic<uint64_t> receivedCount(0);
44+
uint64_t receivedCountTotal(0);
45+
uint64_t receivedCountPerSecs(0);
46+
47+
// Setup a callback to be fired (in a background thread, watch out for race conditions !)
48+
// when a message or an event (open, close, error) is received
49+
webSocket.setOnMessageCallback(
50+
[&webSocket, &receivedCount, &sendMsg, binaryMode](const ix::WebSocketMessagePtr& msg) {
51+
if (msg->type == ix::WebSocketMessageType::Message)
52+
{
53+
webSocket.send(msg->str, msg->binary);
54+
receivedCount++;
55+
}
56+
else if (msg->type == ix::WebSocketMessageType::Open)
57+
{
58+
spdlog::info("ws_echo_client: connected");
59+
spdlog::info("Uri: {}", msg->openInfo.uri);
60+
spdlog::info("Headers:");
61+
for (auto it : msg->openInfo.headers)
62+
{
63+
spdlog::info("{}: {}", it.first, it.second);
64+
}
65+
66+
webSocket.send(sendMsg, binaryMode);
67+
}
68+
});
69+
70+
auto timer = [&receivedCount, &receivedCountTotal, &receivedCountPerSecs] {
71+
setThreadName("Timer");
72+
while (true)
73+
{
74+
//
75+
// We cannot write to sentCount and receivedCount
76+
// as those are used externally, so we need to introduce
77+
// our own counters
78+
//
79+
std::stringstream ss;
80+
ss << "messages received: " << receivedCountPerSecs << " per second "
81+
<< receivedCountTotal << " total";
82+
83+
CoreLogger::info(ss.str());
84+
85+
receivedCountPerSecs = receivedCount - receivedCountTotal;
86+
receivedCountTotal += receivedCountPerSecs;
87+
88+
auto duration = std::chrono::seconds(1);
89+
std::this_thread::sleep_for(duration);
90+
}
91+
};
92+
93+
std::thread t1(timer);
94+
95+
// Now that our callback is setup, we can start our background thread and receive messages
96+
std::cout << "Connecting to " << url << "..." << std::endl;
97+
webSocket.start();
98+
99+
// Send a message to the server (default to TEXT mode)
100+
webSocket.send("hello world");
101+
102+
while (true)
103+
{
104+
std::string text;
105+
std::cout << "> " << std::flush;
106+
std::getline(std::cin, text);
107+
108+
webSocket.send(text);
109+
}
110+
111+
return 0;
112+
}
113+
} // namespace ix

0 commit comments

Comments
 (0)