Compare commits

...

9 Commits

53 changed files with 263 additions and 596 deletions

View File

@ -1,2 +1 @@
venv
build build

View File

@ -10,6 +10,7 @@ set (CMAKE_CXX_STANDARD 14)
set (CXX_STANDARD_REQUIRED ON) set (CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_EXTENSIONS OFF) set (CMAKE_CXX_EXTENSIONS OFF)
# -Wshorten-64-to-32 does not work with clang
if (NOT WIN32) if (NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
endif() endif()
@ -115,4 +116,3 @@ set( IXWEBSOCKET_INCLUDE_DIRS
target_include_directories( ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS} ) target_include_directories( ixwebsocket PUBLIC ${IXWEBSOCKET_INCLUDE_DIRS} )
add_subdirectory(ws) add_subdirectory(ws)
add_subdirectory(examples)

View File

@ -15,7 +15,7 @@ communication channels over a single TCP connection. *IXWebSocket* is a C++ libr
## Examples ## Examples
The examples folder countains a simple chat program, using a node.js broadcast server. The ws folder countains many interactive programs for chat and file transfers demonstrating client and server usage.
Here is what the client API looks like. Here is what the client API looks like.
@ -144,16 +144,6 @@ Large frames are broken up into smaller chunks or messages to avoid filling up t
* 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. * 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.
* The server code is using select to detect incoming data, and creates one OS thread per connection. This isn't as scalable as strategies using epoll or kqueue. * The server code is using select to detect incoming data, and creates one OS thread per connection. This isn't as scalable as strategies using epoll or kqueue.
## Examples
1. Bring up a terminal and jump to the examples folder.
2. Compile the example C++ code. `sh build.sh`
3. Install node.js from [here](https://nodejs.org/en/download/).
4. Type `npm install` to install the node.js dependencies. Then `node broadcast-server.js` to run the server.
5. Bring up a second terminal. `./cmd_websocket_chat bob`
6. Bring up a third terminal. `./cmd_websocket_chat bill`
7. Start typing things in any of those terminals. Hopefully you should see your message being received on the other end.
## C++ code organization ## C++ code organization
Here's a simplistic diagram which explains how the code is structured in term of class/modules. Here's a simplistic diagram which explains how the code is structured in term of class/modules.

View File

@ -1,7 +0,0 @@
add_subdirectory(broadcast_server)
add_subdirectory(ping_pong)
add_subdirectory(chat)
add_subdirectory(echo_server)
add_subdirectory(ws_connect)
# add_subdirectory(cobra_publisher)

View File

@ -1,9 +0,0 @@
CMakeCache.txt
package-lock.json
CMakeFiles
ixwebsocket_unittest
cmake_install.cmake
node_modules
ixwebsocket
Makefile
build

View File

@ -1,28 +0,0 @@
#
# Author: Benjamin Sergeant
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
#
cmake_minimum_required (VERSION 3.4.1)
project (broadcast_server)
# There's -Weverything too for clang
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshorten-64-to-32")
set (OPENSSL_PREFIX /usr/local/opt/openssl) # Homebrew openssl
set (CMAKE_CXX_STANDARD 14)
option(USE_TLS "Add TLS support" ON)
include_directories(broadcast_server .)
add_executable(broadcast_server
broadcast_server.cpp)
if (APPLE AND USE_TLS)
target_link_libraries(broadcast_server "-framework foundation" "-framework security")
endif()
target_link_libraries(broadcast_server ixwebsocket)
install(TARGETS broadcast_server DESTINATION bin)

View File

@ -1,75 +0,0 @@
/*
* broadcast_server.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#include <iostream>
#include <sstream>
#include <ixwebsocket/IXWebSocketServer.h>
int main(int argc, char** argv)
{
int port = 8080;
if (argc == 2)
{
std::stringstream ss;
ss << argv[1];
ss >> port;
}
ix::WebSocketServer server(port);
server.setOnConnectionCallback(
[&server](std::shared_ptr<ix::WebSocket> webSocket)
{
webSocket->setOnMessageCallback(
[webSocket, &server](ix::WebSocketMessageType messageType,
const std::string& str,
size_t wireSize,
const ix::WebSocketErrorInfo& error,
const ix::WebSocketOpenInfo& openInfo,
const ix::WebSocketCloseInfo& closeInfo)
{
if (messageType == ix::WebSocket_MessageType_Open)
{
std::cerr << "New connection" << std::endl;
std::cerr << "Uri: " << openInfo.uri << std::endl;
std::cerr << "Headers:" << std::endl;
for (auto it : openInfo.headers)
{
std::cerr << it.first << ": " << it.second << std::endl;
}
}
else if (messageType == ix::WebSocket_MessageType_Close)
{
std::cerr << "Closed connection" << std::endl;
}
else if (messageType == ix::WebSocket_MessageType_Message)
{
std::cerr << "Received " << wireSize << " bytes" << std::endl;
for (auto&& client : server.getClients())
{
if (client != webSocket)
{
client->send(str);
}
}
}
}
);
}
);
auto res = server.listen();
if (!res.first)
{
std::cerr << res.second << std::endl;
return 1;
}
server.start();
server.wait();
return 0;
}

View File

@ -1,3 +0,0 @@
build
venv
node_modules

View File

@ -1,21 +0,0 @@
#
# cmd_websocket_chat.cpp
# Author: Benjamin Sergeant
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
#
cmake_minimum_required (VERSION 3.4.1)
project (cmd_websocket_chat)
set (CMAKE_CXX_STANDARD 14)
option(USE_TLS "Add TLS support" ON)
add_executable(cmd_websocket_chat cmd_websocket_chat.cpp)
if (APPLE AND USE_TLS)
target_link_libraries(cmd_websocket_chat "-framework foundation" "-framework security")
endif()
target_link_libraries(cmd_websocket_chat ixwebsocket)
install(TARGETS cmd_websocket_chat DESTINATION bin)

View File

@ -1,39 +0,0 @@
# Building
1. cmake -G .
2. make
## Disable TLS
chat$ cmake -DUSE_TLS=OFF .
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/bsergeant/src/foss/ixwebsocket/examples/chat
chat$ make
Scanning dependencies of target ixwebsocket
[ 16%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocket.cpp.o
[ 33%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocket.cpp.o
[ 50%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketTransport.cpp.o
[ 66%] Linking CXX static library libixwebsocket.a
[ 66%] Built target ixwebsocket
[ 83%] Linking CXX executable cmd_websocket_chat
[100%] Built target cmd_websocket_chat
## Enable TLS (default)
```
chat$ cmake -DUSE_TLS=ON .
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/bsergeant/src/foss/ixwebsocket/examples/chat
(venv) chat$ make
Scanning dependencies of target ixwebsocket
[ 14%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocket.cpp.o
[ 28%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocket.cpp.o
[ 42%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXWebSocketTransport.cpp.o
[ 57%] Building CXX object ixwebsocket/CMakeFiles/ixwebsocket.dir/ixwebsocket/IXSocketAppleSSL.cpp.o
[ 71%] Linking CXX static library libixwebsocket.a
[ 71%] Built target ixwebsocket
[ 85%] Linking CXX executable cmd_websocket_chat
[100%] Built target cmd_websocket_chat
```

View File

@ -1,15 +0,0 @@
#!/bin/sh
#
# Author: Benjamin Sergeant
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
#
# 'manual' way of building. You can also use cmake.
g++ --std=c++11 \
../../ixwebsocket/IXSocket.cpp \
../../ixwebsocket/IXWebSocketTransport.cpp \
../../ixwebsocket/IXWebSocket.cpp \
-I ../.. \
cmd_websocket_chat.cpp \
-o cmd_websocket_chat

View File

@ -1,17 +0,0 @@
#!/bin/sh
#
# Author: Benjamin Sergeant
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
#
# 'manual' way of building. You can also use cmake.
clang++ --std=c++11 --stdlib=libc++ \
../../ixwebsocket/IXSocket.cpp \
../../ixwebsocket/IXWebSocketTransport.cpp \
../../ixwebsocket/IXSocketAppleSSL.cpp \
../../ixwebsocket/IXWebSocket.cpp \
cmd_websocket_chat.cpp \
-o cmd_websocket_chat \
-framework Security \
-framework Foundation

View File

@ -1,31 +0,0 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"ultron": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
},
"ws": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
"requires": {
"async-limiter": "1.0.0",
"safe-buffer": "5.1.2",
"ultron": "1.1.1"
}
}
}
}

View File

@ -1,6 +0,0 @@
{
"dependencies": {
"msgpack-js": "^0.3.0",
"ws": "^3.3.3"
}
}

View File

@ -1,28 +0,0 @@
#
# Author: Benjamin Sergeant
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
#
cmake_minimum_required (VERSION 3.4.1)
project (echo_server)
# There's -Weverything too for clang
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshorten-64-to-32")
set (OPENSSL_PREFIX /usr/local/opt/openssl) # Homebrew openssl
set (CMAKE_CXX_STANDARD 14)
option(USE_TLS "Add TLS support" ON)
include_directories(echo_server .)
add_executable(echo_server
echo_server.cpp)
if (APPLE AND USE_TLS)
target_link_libraries(echo_server "-framework foundation" "-framework security")
endif()
target_link_libraries(echo_server ixwebsocket)
install(TARGETS echo_server DESTINATION bin)

View File

@ -1,68 +0,0 @@
/*
* echo_server.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#include <iostream>
#include <sstream>
#include <ixwebsocket/IXWebSocketServer.h>
int main(int argc, char** argv)
{
int port = 8080;
if (argc == 2)
{
std::stringstream ss;
ss << argv[1];
ss >> port;
}
ix::WebSocketServer server(port);
server.setOnConnectionCallback(
[&server](std::shared_ptr<ix::WebSocket> webSocket)
{
webSocket->setOnMessageCallback(
[webSocket, &server](ix::WebSocketMessageType messageType,
const std::string& str,
size_t wireSize,
const ix::WebSocketErrorInfo& error,
const ix::WebSocketOpenInfo& openInfo,
const ix::WebSocketCloseInfo& closeInfo)
{
if (messageType == ix::WebSocket_MessageType_Open)
{
std::cerr << "New connection" << std::endl;
std::cerr << "Uri: " << openInfo.uri << std::endl;
std::cerr << "Headers:" << std::endl;
for (auto it : openInfo.headers)
{
std::cerr << it.first << ": " << it.second << std::endl;
}
}
else if (messageType == ix::WebSocket_MessageType_Close)
{
std::cerr << "Closed connection" << std::endl;
}
else if (messageType == ix::WebSocket_MessageType_Message)
{
webSocket->send(str);
}
}
);
}
);
auto res = server.listen();
if (!res.first)
{
std::cerr << res.second << std::endl;
return 1;
}
server.start();
server.wait();
return 0;
}

View File

@ -1,25 +0,0 @@
#
# Author: Benjamin Sergeant
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
#
cmake_minimum_required (VERSION 3.4.1)
project (ping_pong)
set (CMAKE_CXX_STANDARD 14)
option(USE_TLS "Add TLS support" ON)
add_executable(ping_pong ping_pong.cpp)
if (APPLE AND USE_TLS)
target_link_libraries(ping_pong "-framework foundation" "-framework security")
endif()
if (WIN32)
target_link_libraries(ping_pong wsock32 ws2_32)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()
target_link_libraries(ping_pong ixwebsocket)
install(TARGETS ping_pong DESTINATION bin)

View File

@ -1,15 +0,0 @@
#!/bin/sh
#
# Author: Benjamin Sergeant
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
#
# 'manual' way of building. You can also use cmake.
g++ --std=c++11 \
../../ixwebsocket/IXSocket.cpp \
../../ixwebsocket/IXWebSocketTransport.cpp \
../../ixwebsocket/IXWebSocket.cpp \
-I ../.. \
cmd_websocket_chat.cpp \
-o cmd_websocket_chat

View File

@ -1,17 +0,0 @@
#!/usr/bin/env python
import asyncio
import websockets
async def hello(uri):
async with websockets.connect(uri) as websocket:
await websocket.send("Hello world!")
response = await websocket.recv()
print(response)
pong_waiter = await websocket.ping('coucou')
ret = await pong_waiter # only if you want to wait for the pong
print(ret)
asyncio.get_event_loop().run_until_complete(
hello('ws://localhost:5678'))

View File

@ -1,21 +0,0 @@
#!/usr/bin/env python
import os
import asyncio
import websockets
async def echo(websocket, path):
async for message in websocket:
print(message)
await websocket.send(message)
if os.getenv('TEST_CLOSE'):
print('Closing')
# breakpoint()
await websocket.close(1001, 'close message')
# await websocket.close()
break
asyncio.get_event_loop().run_until_complete(
websockets.serve(echo, 'localhost', 5678))
asyncio.get_event_loop().run_forever()

View File

@ -1,9 +0,0 @@
#!/bin/sh
test -d build || {
mkdir -p build
cd build
cmake ..
}
(cd build ; make)
./build/ping_pong ws://localhost:5678

View File

@ -1,3 +0,0 @@
build
venv
node_modules

View File

@ -1,20 +0,0 @@
#
# Author: Benjamin Sergeant
# Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
#
cmake_minimum_required (VERSION 3.4.1)
project (ws_connect)
set (CMAKE_CXX_STANDARD 14)
option(USE_TLS "Add TLS support" ON)
add_executable(ws_connect ws_connect.cpp)
if (APPLE AND USE_TLS)
target_link_libraries(ws_connect "-framework foundation" "-framework security")
endif()
target_link_libraries(ws_connect ixwebsocket)
install(TARGETS ws_connect DESTINATION bin)

View File

@ -1,11 +0,0 @@
# Building
1. mkdir build
2. cd build
3. cmake ..
4. make
## Disable TLS
* Enable: `cmake -DUSE_TLS=OFF ..`
* Disable: `cmake -DUSE_TLS=ON ..`

View File

@ -1,25 +0,0 @@
#!/bin/sh
#
# Author: Benjamin Sergeant
# Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved.
#
# 'manual' way of building. You can also use cmake.
g++ --std=c++11 \
-DIXWEBSOCKET_USE_TLS \
-g \
../../ixwebsocket/IXEventFd.cpp \
../../ixwebsocket/IXSocket.cpp \
../../ixwebsocket/IXSetThreadName.cpp \
../../ixwebsocket/IXWebSocketTransport.cpp \
../../ixwebsocket/IXWebSocket.cpp \
../../ixwebsocket/IXDNSLookup.cpp \
../../ixwebsocket/IXSocketConnect.cpp \
../../ixwebsocket/IXSocketOpenSSL.cpp \
../../ixwebsocket/IXWebSocketPerMessageDeflate.cpp \
../../ixwebsocket/IXWebSocketPerMessageDeflateOptions.cpp \
-I ../.. \
ws_connect.cpp \
-o ws_connect \
-lcrypto -lssl -lz -lpthread

View File

@ -1,10 +1,10 @@
# #
# This makefile is just used to easily work with docker (linux build) # This makefile is just used to easily work with docker (linux build)
# #
all: run all: brew
brew: brew:
mkdir -p ws/build && (cd ws/build ; cmake .. ; make) mkdir -p build && (cd build ; cmake .. ; make -j install)
.PHONY: docker .PHONY: docker
docker: docker:

View File

@ -7,13 +7,14 @@ cmake_minimum_required (VERSION 3.4.1)
project (ws) project (ws)
# There's -Weverything too for clang # There's -Weverything too for clang
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic -Wshorten-64-to-32") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
set (CMAKE_CXX_STANDARD 14) set (CMAKE_CXX_STANDARD 14)
option(USE_TLS "Add TLS support" ON) option(USE_TLS "Add TLS support" ON)
include_directories(ws .) include_directories(ws .)
include_directories(ws ..)
include_directories(ws ../third_party) include_directories(ws ../third_party)
add_executable(ws add_executable(ws
@ -22,6 +23,11 @@ add_executable(ws
ixcrypto/IXHash.cpp ixcrypto/IXHash.cpp
ixcrypto/IXUuid.cpp ixcrypto/IXUuid.cpp
ws_ping_pong.cpp
ws_broadcast_server.cpp
ws_echo_server.cpp
ws_chat.cpp
ws_connect.cpp
ws_transfer.cpp ws_transfer.cpp
ws_send.cpp ws_send.cpp
ws_receive.cpp ws_receive.cpp

View File

@ -1,10 +1,10 @@
``` ```
# Start transfer server, which is just a broadcast server at this point
./ws transfer # running on port 8080.
# Start receiver first # Start receiver first
./ws receive ws://localhost:8080 ./ws receive ws://localhost:8080
# Sender # Then send a file. File will be received and written to disk by the receiver process
./ws send ws://localhost:8080 /file/to/path ./ws send ws://localhost:8080 /file/to/path
# Server
./ws transfer # running on port 8080.
``` ```

View File

@ -28,6 +28,11 @@ g++ --std=c++14 \
ixcrypto/IXBase64.cpp \ ixcrypto/IXBase64.cpp \
ixcrypto/IXHash.cpp \ ixcrypto/IXHash.cpp \
ixcrypto/IXUuid.cpp \ ixcrypto/IXUuid.cpp \
ws_ping_pong.cpp \
ws_broadcast_server.cpp \
ws_echo_server.cpp \
ws_chat.cpp \
ws_connect.cpp \
ws_transfer.cpp \ ws_transfer.cpp \
ws_send.cpp \ ws_send.cpp \
ws_receive.cpp \ ws_receive.cpp \

View File

@ -6,6 +6,7 @@
#pragma once #pragma once
#include <cstdint>
#include <vector> #include <vector>
namespace ix namespace ix

View File

@ -13,15 +13,27 @@
#include <iostream> #include <iostream>
#include <cli11/CLI11.hpp> #include <cli11/CLI11.hpp>
#include <ixwebsocket/IXSocket.h>
namespace ix namespace ix
{ {
int ws_ping_pong_main(const std::string& url);
int ws_echo_server_main(int port);
int ws_broadcast_server_main(int port);
int ws_chat_main(const std::string& url,
const std::string& user);
int ws_connect_main(const std::string& url);
int ws_receive_main(const std::string& url, int ws_receive_main(const std::string& url,
bool enablePerMessageDeflate); bool enablePerMessageDeflate);
extern int ws_transfer_main(int port); int ws_transfer_main(int port);
extern int ws_send_main(const std::string& url, int ws_send_main(const std::string& url,
const std::string& path); const std::string& path);
} }
@ -30,8 +42,9 @@ int main(int argc, char** argv)
CLI::App app{"ws is a websocket tool"}; CLI::App app{"ws is a websocket tool"};
app.require_subcommand(); app.require_subcommand();
std::string url; std::string url("ws://127.0.0.1:8080");
std::string path; std::string path;
std::string user;
int port = 8080; int port = 8080;
CLI::App* sendApp = app.add_subcommand("send", "Send a file"); CLI::App* sendApp = app.add_subcommand("send", "Send a file");
@ -44,8 +57,26 @@ int main(int argc, char** argv)
CLI::App* transferApp = app.add_subcommand("transfer", "Broadcasting server"); CLI::App* transferApp = app.add_subcommand("transfer", "Broadcasting server");
transferApp->add_option("--port", port, "Connection url"); transferApp->add_option("--port", port, "Connection url");
CLI::App* connectApp = app.add_subcommand("connect", "Connect to a remote server");
connectApp->add_option("url", url, "Connection url")->required();
CLI::App* chatApp = app.add_subcommand("chat", "Group chat");
chatApp->add_option("url", url, "Connection url")->required();
chatApp->add_option("user", user, "User name")->required();
CLI::App* echoServerApp = app.add_subcommand("echo_server", "Echo server");
echoServerApp->add_option("--port", port, "Connection url");
CLI::App* broadcastServerApp = app.add_subcommand("broadcast_server", "Broadcasting server");
broadcastServerApp->add_option("--port", port, "Connection url");
CLI::App* pingPongApp = app.add_subcommand("ping", "Ping pong");
pingPongApp->add_option("url", url, "Connection url")->required();
CLI11_PARSE(app, argc, argv); CLI11_PARSE(app, argc, argv);
ix::Socket::init();
if (app.got_subcommand("transfer")) if (app.got_subcommand("transfer"))
{ {
return ix::ws_transfer_main(port); return ix::ws_transfer_main(port);
@ -59,6 +90,26 @@ int main(int argc, char** argv)
bool enablePerMessageDeflate = false; bool enablePerMessageDeflate = false;
return ix::ws_receive_main(url, enablePerMessageDeflate); return ix::ws_receive_main(url, enablePerMessageDeflate);
} }
else if (app.got_subcommand("connect"))
{
return ix::ws_connect_main(url);
}
else if (app.got_subcommand("chat"))
{
return ix::ws_chat_main(url, user);
}
else if (app.got_subcommand("echo_server"))
{
return ix::ws_echo_server_main(port);
}
else if (app.got_subcommand("broadcast_server"))
{
return ix::ws_broadcast_server_main(port);
}
else if (app.got_subcommand("ping"))
{
return ix::ws_ping_pong_main(url);
}
else else
{ {
assert(false); assert(false);

View File

@ -0,0 +1,72 @@
/*
* ws_broadcast_server.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#include <iostream>
#include <sstream>
#include <ixwebsocket/IXWebSocketServer.h>
namespace ix
{
int ws_broadcast_server_main(int port)
{
std::cout << "Listening on port " << port << std::endl;
ix::WebSocketServer server(port);
server.setOnConnectionCallback(
[&server](std::shared_ptr<ix::WebSocket> webSocket)
{
webSocket->setOnMessageCallback(
[webSocket, &server](ix::WebSocketMessageType messageType,
const std::string& str,
size_t wireSize,
const ix::WebSocketErrorInfo& error,
const ix::WebSocketOpenInfo& openInfo,
const ix::WebSocketCloseInfo& closeInfo)
{
if (messageType == ix::WebSocket_MessageType_Open)
{
std::cerr << "New connection" << std::endl;
std::cerr << "Uri: " << openInfo.uri << std::endl;
std::cerr << "Headers:" << std::endl;
for (auto it : openInfo.headers)
{
std::cerr << it.first << ": " << it.second << std::endl;
}
}
else if (messageType == ix::WebSocket_MessageType_Close)
{
std::cerr << "Closed connection" << std::endl;
}
else if (messageType == ix::WebSocket_MessageType_Message)
{
std::cerr << "Received " << wireSize << " bytes" << std::endl;
for (auto&& client : server.getClients())
{
if (client != webSocket)
{
client->send(str);
}
}
}
}
);
}
);
auto res = server.listen();
if (!res.first)
{
std::cerr << res.second << std::endl;
return 1;
}
server.start();
server.wait();
return 0;
}
}

View File

@ -1,14 +1,13 @@
/* /*
* cmd_websocket_chat.cpp * ws_chat.cpp
* Author: Benjamin Sergeant * Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. * Copyright (c) 2017-2019 Machine Zone, Inc. All rights reserved.
*/ */
// //
// Simple chat program that talks to the node.js server at // Simple chat program that talks to the node.js server at
// websocket_chat_server/broacast-server.js // websocket_chat_server/broacast-server.js
// //
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <queue> #include <queue>
@ -20,19 +19,13 @@
// for convenience // for convenience
using json = nlohmann::json; using json = nlohmann::json;
using namespace ix; namespace ix
namespace
{ {
void log(const std::string& msg)
{
std::cout << msg << std::endl;
}
class WebSocketChat class WebSocketChat
{ {
public: public:
WebSocketChat(const std::string& user); WebSocketChat(const std::string& url,
const std::string& user);
void subscribe(const std::string& channel); void subscribe(const std::string& channel);
void start(); void start();
@ -46,19 +39,27 @@ namespace
std::pair<std::string, std::string> decodeMessage(const std::string& str); std::pair<std::string, std::string> decodeMessage(const std::string& str);
private: private:
std::string _url;
std::string _user; std::string _user;
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
std::queue<std::string> _receivedQueue; std::queue<std::string> _receivedQueue;
void log(const std::string& msg);
}; };
WebSocketChat::WebSocketChat(const std::string& user) : WebSocketChat::WebSocketChat(const std::string& url,
const std::string& user) :
_url(url),
_user(user) _user(user)
{ {
; ;
} }
void WebSocketChat::log(const std::string& msg)
{
std::cout << msg << std::endl;
}
size_t WebSocketChat::getReceivedMessagesCount() const size_t WebSocketChat::getReceivedMessagesCount() const
{ {
return _receivedQueue.size(); return _receivedQueue.size();
@ -76,11 +77,10 @@ namespace
void WebSocketChat::start() void WebSocketChat::start()
{ {
std::string url("ws://localhost:8080/"); _webSocket.setUrl(_url);
_webSocket.setUrl(url);
std::stringstream ss; std::stringstream ss;
log(std::string("Connecting to url: ") + url); log(std::string("Connecting to url: ") + _url);
_webSocket.setOnMessageCallback( _webSocket.setOnMessageCallback(
[this](ix::WebSocketMessageType messageType, [this](ix::WebSocketMessageType messageType,
@ -164,10 +164,11 @@ namespace
_webSocket.send(encodeMessage(text)); _webSocket.send(encodeMessage(text));
} }
void interactiveMain(const std::string& user) int ws_chat_main(const std::string& url,
const std::string& user)
{ {
std::cout << "Type Ctrl-D to exit prompt..." << std::endl; std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
WebSocketChat webSocketChat(user); WebSocketChat webSocketChat(url, user);
webSocketChat.start(); webSocketChat.start();
while (true) while (true)
@ -188,16 +189,3 @@ namespace
webSocketChat.stop(); webSocketChat.stop();
} }
} }
int main(int argc, char** argv)
{
std::string user("user");
if (argc == 2)
{
user = argv[1];
}
Socket::init();
interactiveMain(user);
return 0;
}

View File

@ -9,15 +9,8 @@
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXSocket.h> #include <ixwebsocket/IXSocket.h>
using namespace ix; namespace ix
namespace
{ {
void log(const std::string& msg)
{
std::cout << msg << std::endl;
}
class WebSocketConnect class WebSocketConnect
{ {
public: public:
@ -32,6 +25,8 @@ namespace
private: private:
std::string _url; std::string _url;
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
void log(const std::string& msg);
}; };
WebSocketConnect::WebSocketConnect(const std::string& url) : WebSocketConnect::WebSocketConnect(const std::string& url) :
@ -40,6 +35,11 @@ namespace
; ;
} }
void WebSocketConnect::log(const std::string& msg)
{
std::cout << msg << std::endl;
}
void WebSocketConnect::stop() void WebSocketConnect::stop()
{ {
_webSocket.stop(); _webSocket.stop();
@ -148,18 +148,12 @@ namespace
std::cout << std::endl; std::cout << std::endl;
webSocketChat.stop(); webSocketChat.stop();
} }
}
int main(int argc, char** argv) int ws_connect_main(const std::string& url)
{
if (argc != 2)
{ {
std::cerr << "Usage: ws_connect <url>" << std::endl;
return 1;
}
std::string url = argv[1];
Socket::init(); Socket::init();
interactiveMain(url); interactiveMain(url);
return 0; return 0;
}
} }

68
ws/ws_echo_server.cpp Normal file
View File

@ -0,0 +1,68 @@
/*
* ws_broadcast_server.cpp
* Author: Benjamin Sergeant
* Copyright (c) 2018 Machine Zone, Inc. All rights reserved.
*/
#include <iostream>
#include <sstream>
#include <ixwebsocket/IXWebSocketServer.h>
namespace ix
{
int ws_echo_server_main(int port)
{
std::cout << "Listening on port " << port << std::endl;
ix::WebSocketServer server(port);
server.setOnConnectionCallback(
[&server](std::shared_ptr<ix::WebSocket> webSocket)
{
webSocket->setOnMessageCallback(
[webSocket, &server](ix::WebSocketMessageType messageType,
const std::string& str,
size_t wireSize,
const ix::WebSocketErrorInfo& error,
const ix::WebSocketOpenInfo& openInfo,
const ix::WebSocketCloseInfo& closeInfo)
{
if (messageType == ix::WebSocket_MessageType_Open)
{
std::cerr << "New connection" << std::endl;
std::cerr << "Uri: " << openInfo.uri << std::endl;
std::cerr << "Headers:" << std::endl;
for (auto it : openInfo.headers)
{
std::cerr << it.first << ": " << it.second << std::endl;
}
}
else if (messageType == ix::WebSocket_MessageType_Close)
{
std::cerr << "Closed connection" << std::endl;
}
else if (messageType == ix::WebSocket_MessageType_Message)
{
std::cerr << "Received "
<< wireSize << " bytes"
<< std::endl;
webSocket->send(str);
}
}
);
}
);
auto res = server.listen();
if (!res.first)
{
std::cerr << res.second << std::endl;
return 1;
}
server.start();
server.wait();
return 0;
}
}

View File

@ -1,7 +1,7 @@
/* /*
* ping_pong.cpp * ws_ping_pong.cpp
* Author: Benjamin Sergeant * Author: Benjamin Sergeant
* Copyright (c) 2017-2018 Machine Zone, Inc. All rights reserved. * Copyright (c) 2018-2019 Machine Zone, Inc. All rights reserved.
*/ */
#include <iostream> #include <iostream>
@ -9,15 +9,8 @@
#include <ixwebsocket/IXWebSocket.h> #include <ixwebsocket/IXWebSocket.h>
#include <ixwebsocket/IXSocket.h> #include <ixwebsocket/IXSocket.h>
using namespace ix; namespace ix
namespace
{ {
void log(const std::string& msg)
{
std::cout << msg << std::endl;
}
class WebSocketPingPong class WebSocketPingPong
{ {
public: public:
@ -33,6 +26,8 @@ namespace
private: private:
std::string _url; std::string _url;
ix::WebSocket _webSocket; ix::WebSocket _webSocket;
void log(const std::string& msg);
}; };
WebSocketPingPong::WebSocketPingPong(const std::string& url) : WebSocketPingPong::WebSocketPingPong(const std::string& url) :
@ -41,6 +36,11 @@ namespace
; ;
} }
void WebSocketPingPong::log(const std::string& msg)
{
std::cout << msg << std::endl;
}
void WebSocketPingPong::stop() void WebSocketPingPong::stop()
{ {
_webSocket.stop(); _webSocket.stop();
@ -124,7 +124,7 @@ namespace
_webSocket.send(text); _webSocket.send(text);
} }
void interactiveMain(const std::string& url) int ws_ping_pong_main(const std::string& url)
{ {
std::cout << "Type Ctrl-D to exit prompt..." << std::endl; std::cout << "Type Ctrl-D to exit prompt..." << std::endl;
WebSocketPingPong webSocketPingPong(url); WebSocketPingPong webSocketPingPong(url);
@ -155,17 +155,3 @@ namespace
webSocketPingPong.stop(); webSocketPingPong.stop();
} }
} }
int main(int argc, char** argv)
{
if (argc != 2)
{
std::cerr << "Usage: ping_pong <url>" << std::endl;
return 1;
}
std::string url = argv[1];
Socket::init();
interactiveMain(url);
return 0;
}