C++ uWebSockets объединяет цикл обработки событий в одном потоке
Я использую uWebSockets в своем проекте C++, где у меня есть свой собственный цикл событий. Это цикл while, с переменной задержкой между каждым выполнением. Это выглядит примерно так:
while (true) {
std::this_thread::sleep_for (variableTime);
// Execute logic
}
Ранее я использовал другой поток для выполнения логики, но я хочу интегрировать цикл uWebSockets с моим циклом. Что-то вроде этого:
#include <iostream>
#include <uWS/uWS.h>
using namespace std;
int main () {
uWS::Hub h;
h.onMessage([](uWS::WebSocket<uWS::SERVER> *ws, char *message, size_t length, uWS::OpCode opCode) {
ws->send(message, length, opCode);
});
if (h.listen(3000)) {
h.run();
}
while (true) {
std::this_thread::sleep_for (variableTime);
h.getMessages(); // <-- doesn't call `onMessage` until this is executed
// Execute logic
}
}
Как бы я поступил так?
2 ответа
Сегодня у меня был тот же вопрос. После некоторого поиска исходного кода, я думаю, что нашел ответ.
Кажется, вы ищете недавно добавленную ( https://github.com/uNetworking/uWebSockets/pull/762) Node:: Poll (Hub наследует Node) функцию, которая не блокируется для использования в основном цикле программ, Я думаю, что это должно работать именно так, как вы имели в виду getMessages.
До v0.14.8 ответ выше работал, но с тех пор функциональность исчезла, был проведен серьезный рефакторинг uWebSockets.
Во всяком случае, сейчас у меня v20.14.0, и я хотел эту функциональность, и я прибег к хаку, но он работает достаточно хорошо для меня. Это предполагает, что uWebSockets использует LIBUV:
// 1) create the app, but don't call app.run()
// auto app = uWS::SSLApp({ ... }).listen(3000, [](auto* listen_socket) {});
// 2) extract uv_loop from the Loop
// check us_loop_t from https://github.com/uNetworking/uSockets/blob/master/src/internal/eventing/libuv.h
struct dummy_us_loop_t
{
alignas(LIBUS_EXT_ALIGNMENT) char data[104];
uv_loop_t* uv_loop;
int is_default;
void* uv_pre;
void* uv_check;
};
auto uv_loop = ((dummy_us_loop_t*)uWS::Loop::get())->uv_loop;
// 3) optionally add uv_loop hooks, for ex and idle callback:
if (true)
{
struct MyStruct
{
int x = 0;
int y = 0;
} myStruct = { .x = 5, .y = 9 };
uv_idle_t idler = { .data = &myStruct };
uv_idle_init(uv_loop, &idler);
uv_idle_start(&idler, [](uv_idle_t* handle) {
static int counter;
const auto& obj = (MyStruct*)handle->data;
std::cout << "idle: " << counter << " " << obj->x << " " << obj->y << std::endl;
if (++counter > 100000) uv_idle_stop(handle);
});
}
// 4) now you can just run the uv_loop step by step, instead of app.run()
while (true)
{
// your custom code here
std::cout << ".";
uv_run(uv_loop, UV_RUN_NOWAIT);
}
Другой способ — использовать обработчики pre и post, но они будут запускаться только при возникновении события, поэтому это не подходит для чего-то в реальном времени, но хорошо для частых проверок:
uWS::Loop::get()->addPostHandler((void*)200, [](uWS::Loop* loop) { std::cout << "post"; });
uWS::Loop::get()->addPreHandler((void*)200, [](uWS::Loop* loop) { std::cout << "pre"; });