【问题标题】:Udp server from boost not working on multithreading, but work only on the main thread来自 boost 的 udp 服务器不能在多线程上工作,但只能在主线程上工作
【发布时间】:2019-10-25 07:44:46
【问题描述】:

我有一个带有 boost::asio 的异步 udp 服务器 但问题是: 如果我在线程上启动它,服务器将无法工作 但是如果我在主线程上启动它(与服务阻塞)它正在工作......

我尝试用叉子来做,但不能用 eiser

class Server {
private:
    boost::asio::io_service _IO_service;
    boost::shared_ptr<boost::asio::ip::udp::socket> _My_socket;
    boost::asio::ip::udp::endpoint _His_endpoint;
    boost::array<char, 1000> _My_Buffer;

private:

    void Handle_send(const boost::system::error_code& error, size_t size, std::string msg) {
    //do stuff
   };

    void start_send(std::string msg) {
        _My_socket->async_send_to(boost::asio::buffer(msg), _His_endpoint,
            boost::bind(&Server::Handle_send, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred, msg));
    };

    void Handle_receive(const boost::system::error_code& error, size_t size) {
    //do stuff
    };

    void start_receive(void) {
        _My_socket->async_receive_from(
            boost::asio::buffer(_My_Buffer), _His_endpoint,
            boost::bind(&Server::Handle_receive, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred));
    }

public:
    Server(int port):
    _IO_service(),
    _My_socket(boost::make_shared<boost::asio::ip::udp::socket>(_IO_service, \
        boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port)))
    {
        start_receive();
    };

    void Launch() {
        _IO_service.run();
    };
};

目标是在后台调用 Server::Launch。

【问题讨论】:

  • 您应该显示代码如何在 main 中调用 Launch

标签: c++ multithreading udp boost-asio


【解决方案1】:

首先,您在start_send 中有未定义的行为。

async_send_to 立即返回,所以msg 作为局部变量在start_send 返回时被销毁。当您调用async_send_to 时,您必须确保在异步操作完成之前msg 没有被破坏。描述了什么in documentation -

虽然缓冲区对象可以根据需要被复制,但其所有权 底层内存块由调用者保留,必须 保证它们在调用处理程序之前一直有效

可以通过多种方式解决,最简单的就是使用字符串作为数据成员(作为发送数据的缓冲区):

class Server {
    //..
    std::string _M_toSendBuffer;
    // 
    void start_send(std::string msg) {
        _M_toSend = msg; // store msg into buffer for sending
        _My_socket->async_send_to(boost::asio::buffer(_M_toSend), _His_endpoint,
            boost::bind(&Server::Handle_send, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred, 
                _M_toSend));
    };

另一种解决方案是将msg包装成智能指针以延长其生命周期:

void Handle_send(const boost::system::error_code& error, size_t size, 
        boost::shared_ptr<std::string> msg) {
   //do stuff
};

void start_send(std::string msg) {
    boost::shared_ptr<std::string> msg2 = boost::make_shared<std::string>(msg); // [1]
    _My_socket->async_send_to(boost::asio::buffer(*msg2), _His_endpoint,
        boost::bind(&Server::Handle_send, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred, 
            msg2)); // [2]
};

在 [1] 行中,我们创建 shared_ptr,其中包含 msg 内容,然后在 [2] 行中,shared_ptr 的引用计数器在调用 bind 时增加,因此字符串的生存期延长并被销毁在调用处理程序之后。


关于基于线程的非工作版本。您没有显示调用Launch 的代码,但也许您只是不加入这个线程?

Server s(3456);
boost::thread th(&Server::Launch,&s);
th.join(); // are you calling this line?

或者您的代码在 start_send 中可能不适用于 UB。

【讨论】:

  • 首先,感谢您的回答。我更正了发件人问题。但是对于线程,我使用的是 th.detech();或者 if (fork() == 0) but never th.join()... with th.join() 它似乎可以工作,但它会阻塞,但至少它工作得很好。使用 .join() 我需要在另一个线程中执行主程序并同时调用 join() 吗?
  • 所以再往前走一点,问题来自服务器中的 strdup,如果客户端连接会导致线程崩溃......并且在服务器分离后,它会连接到自己... strdup 使线程崩溃,因此客户端无法连接...
  • 你说th.join()是阻塞的,但这取决于它被调用的地方。您必须在 main 函数中有一些等待条件,以防止 main 返回。好的,您可以致电 detach,但随后 main 会立即结束。在 main 中返回 0 之前,您必须阻止您的程序,例如通过 join 或使用无限循环并在内部休眠,或者其他任何但它必须阻止的方法。关于第二条评论,我不明白strdup 的问题。
猜你喜欢
  • 1970-01-01
  • 2019-06-20
  • 1970-01-01
  • 1970-01-01
  • 2015-04-09
  • 1970-01-01
  • 2016-09-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多