How can I gracefully stop a uWebSockets server? - c++

How could I exit from the run() call in the official example? For example after receiving a signal.
uWS::SSLApp({
/* There are tons of SSL options */
.cert_file_name = "cert.pem",
.key_file_name = "key.pem"
}).onGet("/", [](auto *res, auto *req) {
/* Respond with the web app on default route */
res->writeStatus("200 OK")
->writeHeader("Content-Type", "text/html; charset=utf-8")
->end(indexHtmlBuffer);
}).onWebSocket<UserData>("/ws/chat", [&](auto *ws, auto *req) {
/* Subscribe to topic /chat */
ws->subscribe("chat");
}).onMessage([&](auto *ws, auto message, auto opCode) {
/* Parse incoming message according to some protocol & publish it */
if (seemsReasonable(message)) {
ws->publish("chat", message);
} else {
ws->close();
}
}).onClose([&](auto *ws, int code, auto message) {
/* Remove websocket from this topic */
ws->unsubscribe("chat");
}).listen("localhost", 3000, 0).run();

In a documentation, there is written following:
Many users ask how they should stop the event loop. That's not how it is done, you never stop it, you let it fall through. By closing all sockets, stopping the listen socket, removing any timers, etc, the loop will automatically cause App.run to return gracefully, with no memory leaks.
Because the App itself is under RAII control, once the blocking .run call returns and the App goes out of scope, all memory will gracefully be deleted.
So it means you have to release every source inside of the function. So e.g. this:
void testThread() {
std::this_thread::sleep_for(15s);
us_listen_socket_close(0, listen_socket);
}
int main()
{
std::thread thread(testThread);
uWS::App app;
/* Very simple WebSocket broadcasting echo server */
app.ws<PerSocketData>("/*", {
/* Settings */
.compression = uWS::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024 * 1024,
.idleTimeout = 10,
.maxBackpressure = 1 * 1024 * 1204,
/* Handlers */
.open = [](auto* ws, auto* req) {
/* Let's make every connection subscribe to the "broadcast" topic */
ws->subscribe("broadcast");
},
.message = [](auto* ws, std::string_view message, uWS::OpCode opCode) {
},
.drain = [](auto* ws) {
/* Check getBufferedAmount here */
},
.ping = [](auto* ws) {
},
.pong = [](auto* ws) {
},
.close = [](auto* ws, int code, std::string_view message) {
std::cout << "Client disconnect!" << std::endl;
/* We automatically unsubscribe from any topic here */
}
}).listen(9001, [](auto* token) {
listen_socket = token;
if (token) {
std::cout << "Listening on port " << 9001 << std::endl;
}
});
app.run();
std::cout << "Shutdown!" << std::endl;
And after calling testThread, the server should exit (if no client is connected, otherwise, you should also disconnect connected clients (sockets)) and continue after run() line. After disconnect the client, my output is following:
Listening on port 9001
Client disconnect!
Client disconnect!
Client disconnect!
Client disconnect!
Client disconnect!
Client disconnect!
Shutdown!

Related

I want to run a websocket server with mongoose how do I solve the error?

https://github.com/cesanta/mongoose/blob/master/examples/websocket-server/main.c
#include "mongoose.h"
static const char *s_listen_on = "ws://localhost:80020";
static const char *s_web_root = ".";
// This RESTful server implements the following endpoints:
// /websocket - upgrade to Websocket, and implement websocket echo server
// /api/rest - respond with JSON string {"result": 123}
// any other URI serves static files from s_web_root
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_OPEN) {
// c->is_hexdumping = 1;
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_http_match_uri(hm, "/websocket")) {
// Upgrade to websocket. From now on, a connection is a full-duplex
// Websocket connection, which will receive MG_EV_WS_MSG events.
mg_ws_upgrade(c, hm, NULL);
} else if (mg_http_match_uri(hm, "/rest")) {
// Serve REST response
mg_http_reply(c, 200, "", "{\"result\": %d}\n", 123);
} else {
// Serve static files
struct mg_http_serve_opts opts = {.root_dir = s_web_root};
mg_http_serve_dir(c, ev_data, &opts);
}
} else if (ev == MG_EV_WS_MSG) {
// Got websocket frame. Received data is wm->data. Echo it back!
struct mg_ws_message *wm = (struct mg_ws_message *) ev_data;
mg_ws_send(c, wm->data.ptr, wm->data.len, WEBSOCKET_OP_TEXT);
}
(void) fn_data;
}
int main(void) {
struct mg_mgr mgr; // Event manager
mg_mgr_init(&mgr); // Initialise event manager
printf("Starting WS listener on %s/websocket\n", s_listen_on);
mg_http_listen(&mgr, s_listen_on, fn, NULL); // Create HTTP listener
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_mgr_free(&mgr);
return 0;
}
I want to create a websocket server, but I am getting an error while running this project.
I tried on different ports but the result did not change.
Error is:
mongoose.c:2774:mg_listen Failed: ws://localhost:80020, errno 0
Failed

Using co-routines in boost::beast websocket implementation - support parallel message handling

I'm using secure websocket boost::beast implementation and I'd like to be able to receive new message while current one is being handled. So I've chosen to try it using co-routines (method with yield)
this is my websocket object :
using Websocket = boost::beast::websocket::stream<
boost::beast::ssl_stream<boost::beast::tcp_stream>>;
std::optional<Websocket> ws_;
And this is how I call my websocket listener code
ws_.reset();
boost::asio::spawn(ioc_,
[=](const boost::asio::yield_context &yield) {
this->startAndServeWs(yield);
});
}
And this is how my websocket handler method works : notice this it's divided into 2 parts.
First, the initialization part.
Second, the websocket mainloop in which it ready to read new message.
So my Question is whether this code below is suitable to fetch new messages from server while handling the current message (in either sendPostFetchItems or sendPostDownloadNewVersion which can take a while since they trigger http post request and wait for server response). And if it doesn't, so can I assume that the new message will be queued, waiting for the next iteration when current message handle will be completed ?
My second question is about the catch statement, and if this is the proper way to retry the connection
void Comm::startAndServeWs(const boost::asio::yield_context &yield) {
try {
// webSocet init according to ssl context and io_context.
ws_.emplace(ioc_, ctx_);
boost::beast::get_lowest_layer(ws_.value())
.expires_after(std::chrono::seconds(30));
boost::asio::ip::tcp::resolver resolver(io_context_);
auto const results =
resolver.async_resolve(ip_.value(), port_.value(), yield);
auto ep = boost::beast::get_lowest_layer(ws_.value())
.async_connect(results, yield);
// Set SNI Hostname (many hosts need this to handshake successfully)
if (!SSL_set_tlsext_host_name(ws_.value().next_layer().native_handle(),
ip_.value().c_str())) {
throw("Failed to set SNI Hostname");
}
// Update the host_ string. This will provide the value of the
// Host HTTP header during the WebSocket handshake.
// Se e https://tools.ietf.org/html/rfc7230#section-5.4
auto address = ip_.value() + std::to_string(ep.port());
// Perform the SSL handshake
ws_.value().next_layer().handshake(boost::asio::ssl::stream_base::client);
// Turn off the timeout on the tcp_stream, because
// the websocket stream has its own timeout system.
boost::beast::get_lowest_layer(ws_.value()).expires_never();
// Set suggested timeout settings for the websocket
ws_.value().set_option(
boost::beast::websocket::stream_base::timeout::suggested(
boost::beast::role_type::client));
// Set a decorator to change the User-Agent of the handshake
ws_.value().set_option(boost::beast::websocket::stream_base::decorator(
[](boost::beast::websocket::request_type &req) {
req.set(boost::beast::http::field::user_agent, kWebsocketIdentifier);
}));
Log("trying to establish websocket in address {}", address);
ws_.value().async_handshake(address, "/ws", yield);
//////////////////// here's the websocket main loop.
for (;;) {
boost::beast::flat_buffer buffer;
// Read a message into our buffer
---> ws_.value().async_read(buffer, yield);
Log("websocket response buffer = {}",boost::beast::make_printable(buffer.data()));
try {
nlohmann::json response = nlohmann::json::parse(s);
if (response["command"] == "fetchItems") {
sendPostFetchItems();
} else if (response["command"] == "getLogs") {
sendPostDownloadNewVersion();
}...
}
} catch (std::exception &e) {
Log("websocket reconnect failed. reason = {}", e.what());
ws_.reset();
timer_websocket_.expires_at(timer_websocket_.expiry() +
boost::asio::chrono::seconds(10));
timer_websocket_.async_wait(
[this]([[maybe_unused]] const boost::system::error_code &error) {
boost::asio::spawn(io_context_,
[this](const boost::asio::yield_context &yield) {
this->startAndServeWs(yield);
});
});
}
}

REQ socket drop message silently if two message coming together very fast

This is a reproducible message silently dropped issue with a very small changes to the lbbroker.cpp.
https://github.com/booksbyus/zguide/blob/master/examples/C%2B%2B/lbbroker.cpp
In the lbbroker.cpp file, a worker will be pop out from work_queue when handling a request from client thread. If we modify the line 163 from std::string worker_addr = worker_queue.front(); to static std::string worker_addr = worker_queue.front();, as the following picture shows. All request will be forwarded to one worker, then we can see some messages will be dropped by the REQ socket the worker. Is it normal? REQ socket cannot receive other message when the worker logic is running ?
The client thread:
static void * client_thread(void *arg) {
zmq::context_t context(1);
zmq::socket_t client(context, ZMQ_REQ);
#if (defined (WIN32))
s_set_id(client, (intptr_t)arg);
client.connect("tcp://localhost:5672"); // frontend
#else
s_set_id(client); // Set a printable identity
client.connect("ipc://frontend.ipc");
#endif
// Send request, get reply
s_send(client, "HELLO");
std::string reply = s_recv(client);
std::cout << "Client: " << reply << std::endl;
return (NULL);
}
The worker thread:
// Worker using REQ socket to do LRU routing
//
static void *
worker_thread(void *arg) {
zmq::context_t context(1);
zmq::socket_t worker(context, ZMQ_REQ);
#if (defined (WIN32))
s_set_id(worker, (intptr_t)arg);
worker.connect("tcp://localhost:5673"); // backend
#else
s_set_id(worker);
worker.connect("ipc://backend.ipc");
#endif
// Tell backend we're ready for work
s_send(worker, "READY");
while (1) {
// Read and save all frames until we get an empty frame
// In this example there is only 1 but it could be more
std::string address = s_recv(worker);
{
std::string empty = s_recv(worker);
assert(empty.size() == 0);
}
// Get request, send reply
std::string request = s_recv(worker);
std::cout << "Worker: " << request << std::endl;
s_sendmore(worker, address);
s_sendmore(worker, "");
s_send(worker, "OK");
}
return (NULL);
}
The main function(start client and worker threads):
Note: std::string worker_addr = worker_queue.front(); is changed to static std::string worker_addr = worker_queue.front(); to let all requests from different clients send to one work thread, then some message will be dropped by the REQ socket in work thread.
int main(int argc, char *argv[])
{
// Prepare our context and sockets
zmq::context_t context(1);
zmq::socket_t frontend(context, ZMQ_ROUTER);
zmq::socket_t backend(context, ZMQ_ROUTER);
#if (defined (WIN32))
frontend.bind("tcp://*:5672"); // frontend
backend.bind("tcp://*:5673"); // backend
#else
frontend.bind("ipc://frontend.ipc");
backend.bind("ipc://backend.ipc");
#endif
int client_nbr;
for (client_nbr = 0; client_nbr < 10; client_nbr++) {
pthread_t client;
pthread_create(&client, NULL, client_thread, (void *)(intptr_t)client_nbr);
}
int worker_nbr;
for (worker_nbr = 0; worker_nbr < 3; worker_nbr++) {
pthread_t worker;
pthread_create(&worker, NULL, worker_thread, (void *)(intptr_t)worker_nbr);
}
// Logic of LRU loop
// - Poll backend always, frontend only if 1+ worker ready
// - If worker replies, queue worker as ready and forward reply
// to client if necessary
// - If client requests, pop next worker and send request to it
//
// A very simple queue structure with known max size
std::queue<std::string> worker_queue;
while (1) {
// Initialize poll set
zmq::pollitem_t items[] = {
// Always poll for worker activity on backend
{ backend, 0, ZMQ_POLLIN, 0 },
// Poll front-end only if we have available workers
{ frontend, 0, ZMQ_POLLIN, 0 }
};
if (worker_queue.size())
zmq::poll(&items[0], 2, -1);
else
zmq::poll(&items[0], 1, -1);
// Handle worker activity on backend
if (items[0].revents & ZMQ_POLLIN) {
// Queue worker address for LRU routing
worker_queue.push(s_recv(backend));
{
// Second frame is empty
std::string empty = s_recv(backend);
assert(empty.size() == 0);
}
// Third frame is READY or else a client reply address
std::string client_addr = s_recv(backend);
// If client reply, send rest back to frontend
if (client_addr.compare("READY") != 0) {
{
std::string empty = s_recv(backend);
assert(empty.size() == 0);
}
std::string reply = s_recv(backend);
s_sendmore(frontend, client_addr);
s_sendmore(frontend, "");
s_send(frontend, reply);
if (--client_nbr == 0)
break;
}
}
if (items[1].revents & ZMQ_POLLIN) {
// Now get next client request, route to LRU worker
// Client request is [address][empty][request]
std::string client_addr = s_recv(frontend);
{
std::string empty = s_recv(frontend);
assert(empty.size() == 0);
}
std::string request = s_recv(frontend);
static std::string worker_addr = worker_queue.front();
worker_queue.pop();
s_sendmore(backend, worker_addr);
s_sendmore(backend, "");
s_sendmore(backend, client_addr);
s_sendmore(backend, "");
s_send(backend, request);
}
}
return 0;
}

How to implement a WebSocket server serving only 1 client at a time

I'm using WebSocket++ library to implement a WebSocket server.
Due to the characteristic of my server, I would like it to serve only 1 client at a time. That is, once a client has been connected to my server, I would like it to stop listening from other potential clients until the already connected client disconnects.
Currently my server looks like:
typedef websocketpp::server<websocketpp::config::asio> server;
static void onReceiveData(server *s, websocketpp::connection_hdl hdl, server::message_ptr msg)
{
std::string payload(msg->get_payload());
// process inside payload
websocketpp::lib::error_code ec;
s->send(hdl, payload, websocketpp::frame::opcode::BINARY, ec);
}
int main(void)
{
server myServer;
myServer.init_asio();
myServer.set_message_handler(websocketpp::lib::bind(&onReceiveData, &myServer, websocketpp::lib::placeholders::_1, websocketpp::lib::placeholders::_2));
myServer.listen(9002);
myServer.start_accept();
myServer.run();
}
How should I pause and resume listening, or is there any parameter for limiting number of concurrent clients?
Use validate to decide when to accept or refuse a connection:
static bool accept_new_connections = true;
bool validate(server *, websocketpp::connection_hdl) {
return accept_new_connections;
}
void on_open(server *, websocketpp::connection_hdl hdl) {
accept_new_connections = false;
}
void on_close(server *, websocketpp::connection_hdl hdl) {
accept_new_connections = true;
}
Obviously to make above work you will need to set additional handlers for open, close and validate:
myServer.set_open_handler(bind(&on_open, &echo_server, ::_1));
myServer.set_close_handler(bind(&on_close, &echo_server, ::_1));
myServer.set_validate_handler(bind(&validate, &echo_server, ::_1));

NS-3 Socket to connect to external program

I am trying for a way to write a socket class to connect my NS-3 simulation to an outside program. So what I want to do is to create packets in NS-3 and send them through this socket to an outside tool, do some simple manipulations on the packet in that tool, and then send it back to NS-3. I don't think the built in NS-3 socket can be used for this purpose.
Has anyone come across something like this before or has any suggestions?
Your help is very much appreciated!
I'm using a TCP socket to connect an external Python TCP Socket using NS-3, here is the code:
/*
* Create a NS-3 Application that opens a TCP Socket and
* waits for incoming connections
*
*/
#include "ns3/icmpv4.h"
#include "ns3/assert.h"
#include "ns3/log.h"
#include "ns3/ipv4-address.h"
#include "ns3/socket.h"
#include "ns3/integer.h"
#include "ns3/boolean.h"
#include "ns3/inet-socket-address.h"
#include "ns3/packet.h"
#include "ns3/trace-source-accessor.h"
#include "ns3/config.h"
#include "ns3/tos-device.h"
#include "ns3/names.h"
#include "ns3/string.h"
#include "ns3/object.h"
namespace ns3 {
IOProxyServer::IOProxyServer ()
{
m_socket = 0;
}
TypeId IOProxyServer::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::IoProxyServer")
.SetParent<Application> ()
.AddConstructor<IOProxyServer> ()
.AddAttribute("RemotePortNumber",
"Remote port listening for connections",
IntegerValue(9999),
MakeIntegerAccessor(&IOProxyServer::m_remotePortNumber),
MakeIntegerChecker<int64_t>())
.AddAttribute("RemoteIp",
"Remote IP listening for connections",
StringValue("127.0.0.1"),
MakeStringAccessor(&IOProxyServer::m_remoteIp),
MakeStringChecker())
.AddAttribute("LocalPortNumber",
"Local port for incoming connections",
IntegerValue(3333),
MakeIntegerAccessor(&IOProxyServer::m_localPortNumber),
MakeIntegerChecker<int64_t>())
.AddAttribute("LocalIp",
"Local IP for incoming connections",
StringValue("127.0.0.1"),
MakeStringAccessor(&IOProxyServer::m_localIp),
MakeStringChecker());
return tid;
}
void IOProxyServer::StartApplication (void)
{
NS_LOG_FUNCTION (this);
m_socket = Socket::CreateSocket (GetNode (), TypeId::LookupByName ("ns3::TcpSocketFactory"));
NS_ASSERT_MSG (m_socket != 0, "An error has happened when trying to create the socket");
InetSocketAddress src = InetSocketAddress (Ipv4Address::GetAny(), m_localPortNumber );
InetSocketAddress dest = InetSocketAddress(Ipv4Address(m_remoteIp.c_str()), m_remotePortNumber);
int status;
status = m_socket->Bind (src);
NS_ASSERT_MSG (status != -1, "An error has happened when trying to bind to local end point");
status = m_socket->Connect(dest);
NS_ASSERT_MSG (status != -1, "An error has happened when trying to connect to remote end point");
// Configures the callbacks for the different events related with the connection
//m_socket->SetConnectCallback
m_socket->SetAcceptCallback (
MakeNullCallback<bool, Ptr<Socket>, const Address &> (),
MakeCallback (&IOProxyServer::HandleAccept, this));
m_socket->SetRecvCallback (
MakeCallback (&IOProxyServer::HandleRead, this));
m_socket->SetDataSentCallback (
MakeCallback (&IOProxyServer::HandleSend,this));
//m_socket->SetSendCallback
m_socket->SetCloseCallbacks (
MakeCallback (&IOProxyServer::HandlePeerClose, this),
MakeCallback (&IOProxyServer::HandlePeerError, this));
// If we need to configure a reception only socket or a sending only socket
// we need to call one of the following methods:
// m_socket->ShutdownSend();
// m_socket->ShutdownRecv();
}
void IOProxyServer::StopApplication (void)
{
NS_LOG_FUNCTION (this);
m_socket->Close();
}
void IOProxyServer::HandlePeerClose (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
}
void IOProxyServer::HandlePeerError (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
}
void IOProxyServer::HandleSend (Ptr<Socket> socket, uint32_t dataSent)
{
NS_LOG_FUNCTION (this << socket);
}
void IOProxyServer::HandleAccept (Ptr<Socket> s, const Address& from)
{
NS_LOG_FUNCTION (this << s << from);
s->SetRecvCallback (MakeCallback (&IOProxyServer::HandleRead, this));
}
void IOProxyServer::HandleRead (Ptr<Socket> socket)
{
NS_LOG_FUNCTION (this << socket);
Ptr<Packet> packet;
while ((packet = socket->RecvFrom (from)))
{
if (packet->GetSize () == 0)
{ //EOF
break;
}
if (InetSocketAddress::IsMatchingType (from))
{
//Do whatever you need with the incoming info
}
}
}
void IOProxyServer::SendData()
{
//Do whatever you need for creating your packet and send it using the socket
//Ptr<Packet> packet = Create<Packet>(pointer, sizeof(pointer));
//m_socket->Send(packet, 0, from);
}
IOProxyServer::~IOProxyServer ()
{
}
void IOProxyServer::DoDispose (void)
{
NS_LOG_FUNCTION (this);
m_socket = 0;
Application::DoDispose ();
}
} // namespace ns3