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"; });
Другие вопросы по тегам