diff --git a/CMakeLists.txt b/CMakeLists.txt index 981baa44e..b8d8b27d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -544,6 +544,7 @@ if(NOT NO_EXAMPLES) if(NOT NO_WEBSOCKET) add_subdirectory(examples/client) add_subdirectory(examples/client-benchmark) + add_subdirectory(examples/signaling-server-libdatachannel-ws) endif() if(NOT NO_MEDIA) add_subdirectory(examples/media-receiver) diff --git a/examples/signaling-server-libdatachannel-ws/CMakeLists.txt b/examples/signaling-server-libdatachannel-ws/CMakeLists.txt new file mode 100644 index 000000000..0149e52ee --- /dev/null +++ b/examples/signaling-server-libdatachannel-ws/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.15) +project(signaling-server-libdatachannel-ws + VERSION 0.1 + LANGUAGES CXX) + + set(MEDIA_UWP_RESOURCES + uwp/Logo.png + uwp/package.appxManifest + uwp/SmallLogo.png + uwp/SmallLogo44x44.png + uwp/SplashScreen.png + uwp/StoreLogo.png + uwp/Windows_TemporaryKey.pfx +) + +if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") + add_executable(signaling-server-libdatachannel-ws signaling-server.cpp ${MEDIA_UWP_RESOURCES}) +else() + add_executable(signaling-server-libdatachannel-ws signaling-server.cpp) +endif() + +set_target_properties(signaling-server-libdatachannel-ws PROPERTIES + CXX_STANDARD 17 + OUTPUT_NAME signaling-server) + +set_target_properties(signaling-server-libdatachannel-ws PROPERTIES + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER com.github.paullouisageneau.libdatachannel.examples.signaling-server-libdatachannel-ws) + +find_package(Threads REQUIRED) +target_link_libraries(signaling-server-libdatachannel-ws LibDataChannel::LibDataChannel Threads::Threads nlohmann_json::nlohmann_json) + +if(MSVC) + add_custom_command(TARGET signaling-server-libdatachannel-ws POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$/datachannel.dll" + $ + ) +endif() diff --git a/examples/signaling-server-libdatachannel-ws/signaling-server.cpp b/examples/signaling-server-libdatachannel-ws/signaling-server.cpp new file mode 100644 index 000000000..774953a0e --- /dev/null +++ b/examples/signaling-server-libdatachannel-ws/signaling-server.cpp @@ -0,0 +1,161 @@ +/** + * signaling server example for libdatachannel + * Copyright (c) 2024 Tim Schneider + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "rtc/rtc.hpp" + +#include +#include +#include +#include +#include + +#include +using nlohmann::json; + + +using namespace std::chrono_literals; +using std::shared_ptr; +using std::weak_ptr; +template weak_ptr make_weak_ptr(shared_ptr ptr) { return ptr; } + +std::string get_user(weak_ptr wws) { + const auto &ws = wws.lock(); + const auto path_ = ws->path().value(); + const auto user = path_.substr(path_.rfind('/') + 1); + return user; +} + +void signalHandler(int signum) { + std::cout << "Interrupt signal (" << signum << ") received.\n"; + // terminate program + exit(signum); +} + +int main(int argc, char *argv[]) { + rtc::WebSocketServerConfiguration config; + config.port = 8000; + config.enableTls = false; + config.certificatePemFile = std::nullopt; + config.keyPemFile = std::nullopt; + config.keyPemPass = std::nullopt; + config.bindAddress = std::nullopt; + config.connectionTimeout = std::nullopt; + + // Check command line arguments. + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--help") == 0) { + const size_t len = strlen(argv[0]); + char *path = (char *)malloc(len + 1); + strcpy(path, argv[0]); + path[len] = 0; + + char *app_name = NULL; + // app_name = last_path_segment(path, "\\/"); + fprintf(stderr, + "Usage: %s [-p ] [-a ] [--connection-timeout ] " + "[--enable-tls] [--certificatePemFile ] [--keyPemFile ] " + "[--keyPemPass ]\n" + "Example:\n" + " %s -p 8000 -a 127.0.0.1 \n", + app_name, app_name); + free(path); + return EXIT_FAILURE; + } + if (strcmp(argv[i], "-p") == 0) { + config.port = atoi(argv[++i]); + continue; + } + if (strcmp(argv[i], "-a") == 0) { + config.bindAddress = argv[++i]; + continue; + } + if (strcmp(argv[i], "--connection-timeout") == 0) { + config.connectionTimeout = std::chrono::milliseconds(atoi(argv[++i])); + continue; + } + if (strcmp(argv[i], "--enable-tls") == 0) { + config.enableTls = true; + continue; + } + if (strcmp(argv[i], "--certificatePemFile") == 0) { + config.certificatePemFile = argv[++i]; + continue; + } + if (strcmp(argv[i], "--keyPemFile") == 0) { + config.keyPemFile = argv[++i]; + continue; + } + if (strcmp(argv[i], "--keyPemPass") == 0) { + config.keyPemPass = argv[++i]; + continue; + } + } + + auto wss = std::make_shared(config); + std::unordered_map> clients_map; + + wss->onClient([&clients_map](std::shared_ptr ws) { + std::promise wsPromise; + auto wsFuture = wsPromise.get_future(); + std::cout << "WebSocket client (remote-address: " << ws->remoteAddress().value() << ")" + << std::endl; + + ws->onOpen([&clients_map, &wsPromise, wws = make_weak_ptr(ws)]() { + const auto user = get_user(wws); + std::cout << "WebSocket connected (user: " << user << ")" << std::endl; + clients_map.insert_or_assign(user, wws.lock()); + wsPromise.set_value(); + }); + ws->onError([&clients_map, &wsPromise, wws = make_weak_ptr(ws)](std::string s) { + wsPromise.set_exception(std::make_exception_ptr(std::runtime_error(s))); + const auto user = get_user(wws); + std::cout << "WebSocket error (user: " << user << ")" << std::endl; + clients_map.erase(user); + }); + ws->onClosed([&clients_map, &wsPromise, wws = make_weak_ptr(ws)]() { + const auto user = get_user(wws); + std::cout << "WebSocket closed (user: " << user << ")" << std::endl; + clients_map.erase(user); + }); + ws->onMessage([&clients_map, wws = make_weak_ptr(ws)](auto data) { + // data holds either std::string or rtc::binary + if (!std::holds_alternative(data)) + return; + + json message = json::parse(std::get(data)); + + auto it = message.find("id"); + if (it == message.end()) + return; + + auto id = it->get(); + + auto client_dst = clients_map.find(id); + if (client_dst == clients_map.end()) { + std::cout << "not found" << std::endl; + } else { + const auto user = get_user(wws); + + message["id"] = user; + auto &[id_dst, ws_dst] = *client_dst; + std::cout << user << "->" << id << ": " << message.dump() << std::endl; + ws_dst->send(message.dump()); + } + }); + std::cout << "Waiting for client to be connected..." << std::endl; + wsFuture.get(); + }); + + signal(SIGINT, signalHandler); + while (true) { + std::this_thread::sleep_for(1s); + } + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/examples/signaling-server-libdatachannel-ws/uwp/Logo.png b/examples/signaling-server-libdatachannel-ws/uwp/Logo.png new file mode 100644 index 000000000..65f91ac0f Binary files /dev/null and b/examples/signaling-server-libdatachannel-ws/uwp/Logo.png differ diff --git a/examples/signaling-server-libdatachannel-ws/uwp/SmallLogo.png b/examples/signaling-server-libdatachannel-ws/uwp/SmallLogo.png new file mode 100644 index 000000000..460c0222d Binary files /dev/null and b/examples/signaling-server-libdatachannel-ws/uwp/SmallLogo.png differ diff --git a/examples/signaling-server-libdatachannel-ws/uwp/SmallLogo44x44.png b/examples/signaling-server-libdatachannel-ws/uwp/SmallLogo44x44.png new file mode 100644 index 000000000..c2374581a Binary files /dev/null and b/examples/signaling-server-libdatachannel-ws/uwp/SmallLogo44x44.png differ diff --git a/examples/signaling-server-libdatachannel-ws/uwp/SplashScreen.png b/examples/signaling-server-libdatachannel-ws/uwp/SplashScreen.png new file mode 100644 index 000000000..834256580 Binary files /dev/null and b/examples/signaling-server-libdatachannel-ws/uwp/SplashScreen.png differ diff --git a/examples/signaling-server-libdatachannel-ws/uwp/StoreLogo.png b/examples/signaling-server-libdatachannel-ws/uwp/StoreLogo.png new file mode 100644 index 000000000..508c8a809 Binary files /dev/null and b/examples/signaling-server-libdatachannel-ws/uwp/StoreLogo.png differ diff --git a/examples/signaling-server-libdatachannel-ws/uwp/Windows_TemporaryKey.pfx b/examples/signaling-server-libdatachannel-ws/uwp/Windows_TemporaryKey.pfx new file mode 100644 index 000000000..1cad9993d Binary files /dev/null and b/examples/signaling-server-libdatachannel-ws/uwp/Windows_TemporaryKey.pfx differ diff --git a/examples/signaling-server-libdatachannel-ws/uwp/package.appxManifest b/examples/signaling-server-libdatachannel-ws/uwp/package.appxManifest new file mode 100644 index 000000000..2fd7736f1 --- /dev/null +++ b/examples/signaling-server-libdatachannel-ws/uwp/package.appxManifest @@ -0,0 +1,42 @@ + + + + + + + datachannel-client + CMake + StoreLogo.png + + + + + + + + + + + + + + +