在 httplib REST 服务器中发送 boost/asio IP 命令不起作用

问题描述 投票:0回答:1

我正在尝试使用 REST 服务器发送 boost/asio IP 命令,但看起来好像什么也没发生。为了获得更好的上下文,我有一种方法可以在看门狗定时器超时时发送 IP 命令;如果我在 REST 监听方法之前调用该函数,命令将被发送并且一切正常,但是一旦 REST 服务器启动,我就无法再使用 boost/asio。我对 boost/asio 不是很有经验,所以我很困惑。

这是我的代码基本结构的示例:

#include <iostream>
#include <boost/asio.hpp>

#include "../include/httplib.h"
#include "../include/json.hpp"

using boost::asio::ip::tcp;

static boost::asio::io_context io_context;
static tcp::socket testSocket(io_context);
boost::system::error_code boost_error;

std::string testIP = "192.168.9.114";
std::string testPort = "20000";

int serverPort__ = 8077;
static httplib::Server server_;


namespace RestServer {
    std::string what(const std::exception_ptr &eptr = std::current_exception()) {

        if (!eptr) { throw std::bad_exception(); }

        try { std::rethrow_exception(eptr); }
        catch (const std::exception &e) { return e.what()   ; }
        catch (const std::string    &e) { return e          ; }
        catch (const char           *e) { return e          ; }
        catch (...)                     { return "who knows"; }
    }

    int main() {
        std::cout << "Starting REST server at port " << serverPort__ << std::endl;

        if (!server_.is_valid()) {
            std::cout << "Rest server has an error..." << std::endl;
            return -1;
        }

        server_.Get("/stop", [&](const httplib::Request &req, httplib::Response &res) {
            std::cout << "Rest server is stopping..." << std::endl;
            (void)req;
            (void)res;
            server_.stop();
        });

        server_.set_error_handler([](const httplib::Request &req, httplib::Response &res) {
            const char *fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
            char buf[BUFSIZ];
            if (res.body.length() > 4) {
                snprintf(buf, sizeof(buf), "<p>Error Status: <span style='color:red;'>%d</span></p><p>Message: %s</p>", res.status, res.body.c_str());
            }
            else {
                snprintf(buf, sizeof(buf), fmt, res.status);
            }
            res.set_content(buf, "text/html");
        });

        server_.set_exception_handler([](const httplib::Request &req, httplib::Response &res, std::exception_ptr ep) {
            std::cout << "REST Exception!  " << what(ep) << std::endl;
        });

        server_.Get("/TEST", [](const httplib::Request &req, httplib::Response &res) {
            nlohmann::json jsonRes;
            
            std::string cmdStr = req.get_param_value("cmd");
            std::string  opStr = req.get_param_value("op");

            uint8_t operation = 0;
            uint8_t command = 0;

            try {
                operation = std::stoi(opStr);
                command = std::stoi(cmdStr);
            } 
            catch (std::exception &_) {
                res.body = "Bad operation/command/addr/numBytes";
                res.status = 400;
                return;
            }

            switch (operation) {
                case 0: {
                    switch (command) {
                        case 1: {
                            // example of boost/asio method done via rest
                            sendTestIPCommand(0x02, 0x01);
                        }
                    }
                }
                case 1: {
                    switch (command) {
                        case 1: {
                            // other read commands
                        }
                    }
                }
            }
            res.set_content(jsonRes.dump(), "application/json");
        });

        server_.listen("0.0.0.0", serverPort__);

        std::cout << "Quitting REST server at port" << serverPort__ << std::endl;

        return 0;
    }
}

void startTestSocket() {
    try {
        testSocket = tcp::socket(io_context);
        tcp::resolver signalLightResolver(io_context);
        tcp::resolver::results_type signalLightEndpoints = signalLightResolver.resolve(tcp::resolver::query(tcp::v4(), testIP, testPort));
        boost::asio::connect(testSocket, signalLightEndpoints);
    }
    catch (std::exception &e) {
        std::cerr << "Socket Exception: " << e.what() << std::endl;
    }
}

void sendTestIPCommand(uint8_t color, uint8_t mode) {
    unsigned char c_pIdataW[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

    c_pIdataW[0] = 'W';
    c_pIdataW[color] = mode;

    auto start_time = std::chrono::steady_clock::now();

    try {
        while (true) {
            boost::asio::write(testSocket, boost::asio::buffer(c_pIdataW), boost_error);
            auto now = std::chrono::steady_clock::now();
            if (std::chrono::duration_cast<std::chrono::seconds>(now - start_time).count() > 3) {
                std::cout << "socket start break" << std::endl;
                break;
            }
        }
    }
    catch (std::exception &e) {
        std::cerr << "Socket Exception: " << e.what() << std::endl;
    }

    if (!boost_error) {
        std::cout << "starting signal light.." << std::endl;
    }
    else {
        std::cerr << "a socket error has occurred" << std::endl;
    }
}

int main() {
    startTestSocket();
    return RestServer::main();
}

如果您有任何关于原因或可能的解决方案的外部信息,我们将不胜感激!

我尝试过多线程:为其余服务器创建了一个唯一的线程,希望允许 REST 对 boost/asio 函数是非阻塞的。为 io_context 创建了一个独特的线程,希望允许 boost/asio 函数非阻塞地休息。我在这两种情况下都尝试过互斥。我已经尝试过 boost/asio 的异步写入方法,但不太确定我对此的期望,因为这是 GPT-4o 推荐的最后努力。

c++ boost-asio httplib
1个回答
0
投票

在连接完成之前,您的

startTestSocket
永远不会返回。如果
testIP
/
testPort
组合没有响应,这可能需要很长时间。

当我将

Socket Exception
更改为 127.0.0.1 时,您可以看到它如何更改以打印
testIP
(我的计算机知道它不会响应,因此它不会无限期地阻止):

似乎不清楚

startTestSocket
实际上应该做什么,所以也许你可以不使用它?如果您需要两者同时运行,请制作它们

  • 在单独的线程上运行
  • 使用相同的 io_context 异步运行
© www.soinside.com 2019 - 2024. All rights reserved.