草庐IT

c++ - Boost beast::websocket 回调函数

coder 2024-02-22 原文

我正在试验 Boost beast::websocket websocket_client_async.cpp例如,结合 websocket_server_async.cpp .

正如给定的那样,客户端 示例只是建立一个连接,向服务器发送一个字符串(它只是回显),打印回复,关闭并存在。

我正在尝试修改客户端以使 session 保持事件状态,以便我可以重复发送/接收字符串。因此,虽然示例代码的 on_handshake 函数会立即通过 ws_.async_write(...) 发送字符串,但我将其分离到它自己的 write(.. .) 函数。

这是我修改过的 session 类:

using tcp = boost::asio::ip::tcp;
namespace websocket = boost::beast::websocket;

void fail(boost::system::error_code ec, char const* what)
{
    std::cerr << what << ": " << ec.message() << "\n";
}

// Sends a WebSocket message and prints the response
class session : public std::enable_shared_from_this<session>
{
    tcp::resolver resolver_;
    websocket::stream<tcp::socket> ws_;
    std::atomic<bool> io_in_progress_;
    boost::beast::multi_buffer buffer_;
    std::string host_;

public:
    // Resolver and socket require an io_context
    explicit session(boost::asio::io_context& ioc) : resolver_(ioc), ws_(ioc) {
        io_in_progress_ = false;
    }

    bool io_in_progress() const {
        return io_in_progress_;
    }

    // +---------------------+
    // | The "open" sequence |
    // +---------------------+
    void open(char const* host, char const* port)
    {
        host_ = host;

        // Look up the domain name
        resolver_.async_resolve(host, port,
            std::bind( &session::on_resolve, shared_from_this(),
                std::placeholders::_1, std::placeholders::_2 )
        );
    }

    void on_resolve(boost::system::error_code ec, tcp::resolver::results_type results)
    {
        if (ec)
            return fail(ec, "resolve");

        boost::asio::async_connect(
            ws_.next_layer(), results.begin(), results.end(),
            std::bind( &session::on_connect, shared_from_this(),
                std::placeholders::_1 )
        );
    }

    void on_connect(boost::system::error_code ec)
    {
        if (ec)
            return fail(ec, "connect");

        ws_.async_handshake(host_, "/",
            std::bind( &session::on_handshake, shared_from_this(),
                std::placeholders::_1 )
        );
    }

    void on_handshake(boost::system::error_code ec)
    {
        if (ec)
            return fail(ec, "handshake");
        else {
            std::cout << "Successful handshake with server.\n";
        }
    }

    // +---------------------------+
    // | The "write/read" sequence |
    // +---------------------------+
    void write(const std::string &text)
    {
        io_in_progress_ = true;
        ws_.async_write(boost::asio::buffer(text),
            std::bind( &session::on_write, shared_from_this(),
                std::placeholders::_1, std::placeholders::_2 )
        );
    }

    void on_write(boost::system::error_code ec, std::size_t bytes_transferred)
    {
        boost::ignore_unused(bytes_transferred);
        if (ec)
            return fail(ec, "write");

        ws_.async_read(buffer_,
            std::bind( &session::on_read, shared_from_this(),
                std::placeholders::_1, std::placeholders::_2 )
        );
    }

    void on_read(boost::system::error_code ec, std::size_t bytes_transferred)
    {
        io_in_progress_ = false; // end of write/read sequence
        boost::ignore_unused(bytes_transferred);
        if (ec)
            return fail(ec, "read");

        std::cout << boost::beast::buffers(buffer_.data()) << std::endl;
    }

    // +----------------------+
    // | The "close" sequence |
    // +----------------------+
    void close()
    {
        io_in_progress_ = true;
        ws_.async_close(websocket::close_code::normal,
            std::bind( &session::on_close, shared_from_this(),
                std::placeholders::_1)
        );
    }

    void on_close(boost::system::error_code ec)
    {
        io_in_progress_ = false; // end of close sequence
        if (ec)
            return fail(ec, "close");

        std::cout << "Socket closed successfully.\n";
    }
};

问题是,虽然连接工作正常并且我可以发送一个字符串,但永远不会命中 on_read 回调(除非我做了下面描述的丑陋的 hack)。

我的 main 看起来像这样:

void wait_for_io(std::shared_ptr<session> psession, boost::asio::io_context &ioc)
{
    // Continually try to run the ioc until the callbacks are finally
    // triggered (as indicated by the session::io_in_progress_ flag)
    while (psession->io_in_progress()) {
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        ioc.run();
    }
}

int main(int argc, char** argv)
{
    // Check command line arguments.
    if (argc != 3) {
        std::cerr << "usage info goes here...\n";
        return EXIT_FAILURE;
    }
    const char *host = argv[1], *port = argv[2];

    boost::asio::io_context ioc;
    std::shared_ptr<session> p = std::make_shared<session>(ioc);
    p->open(host, port);
    ioc.run(); // This works. Connection is established and all callbacks are executed.

    p->write("Hello world"); // String is sent & received by server,
                             // even before calling ioc.run()
                             // However, session::on_read callback is never
                             // reached.

    ioc.run();               // This seems to be ignored and returns immediately, so
    wait_for_io(p, ioc);     // <-- so this hack is necessary

    p->close();              // session::on_close is never reached
    ioc.run();               // Again, this seems to be ignored and returns immediately, so
    wait_for_io(p, ioc);     // <-- this is necessary

    return EXIT_SUCCESS;
}

如果我这样做:

p->write("Hello world");
while(1) {
    std::this_thread::sleep_for(std::chrono::milliseconds(1));
}

我可以确认服务器发送和接收了字符串1session::on_read 回调到达.

p->close() 也会发生同样的事情。

但是,如果我添加奇怪的 wait_for_io() 函数,一切正常。我很肯定这是一个可怕的黑客攻击,但我无法弄清楚发生了什么。

1 注意:我可以确认消息确实到达了服务器,因为我修改了服务器示例以将任何接收到的字符串打印到控制台。这是我所做的唯一修改。回显到客户端的功能没有改变。

最佳答案

io_context::run 只会在没有待处理的工作时返回。如果您只是确保一直有一个对 websocket::stream::async_read 的挂起调用处于事件状态,那么 run 将永远不会返回,并且不需要 hack .此外,您将收到服务器发送的所有消息。

关于c++ - Boost beast::websocket 回调函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50030897/

有关c++ - Boost beast::websocket 回调函数的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  3. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  4. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  5. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  6. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  7. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  8. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  9. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  10. ruby-on-rails - 将字符串转换为 ruby​​-on-rails 中的函数 - 2

    我需要一个通过输入字符串进行计算的方法,像这样function="(a/b)*100"a=25b=50function.something>>50有什么方法吗? 最佳答案 您可以使用instance_eval:function="(a/b)*100"a=25.0b=50instance_evalfunction#=>50.0请注意,使用eval本质上是不安全的,尤其是当您使用外部输入时,因为它可能包含注入(inject)的恶意代码。另请注意,a设置为25.0而不是25,因为如果它是整数a/b将导致0(整数)。

随机推荐