简体   繁体   English

boost asio udp socket async_receive_from不会调用处理程序

[英]boost asio udp socket async_receive_from does not call the handler

I want to create an autonomous thread devoted only to receive data from an UDP socket using boost libraries (asio). 我想创建一个自治线程,专门用于使用boost库(asio)从UDP套接字接收数据。 This thread should be an infinite loop triggered by some data received from the UDP socket. 该线程应该是由UDP套接字接收的一些数据触发的无限循环。 In my application I need to use an asynchronous receive operation. 在我的应用程序中,我需要使用异步接收操作。

If I use the synchronous function receive_from everything works as expected. 如果我使用同步函数receive_from,一切都按预期工作。

However if I use async_receive_from the handler is never called. 但是,如果我使用async_receive_from,则永远不会调用处理程序。 Since I use a semaphore to detect that some data have been received, the program locks and the loop is never triggered. 由于我使用信号量来检测是否已收到某些数据,因此程序会锁定并且永远不会触发循环。

I have verified (with a network analyzer) that the sender device properly sends the data on the UDP socket. 我已经验证(使用网络分析器)发送方设备正确地在UDP套接字上发送数据。

I have isolated the problem in the following code. 我已在以下代码中隔离了该问题。

#include <boost\array.hpp>
#include <boost\asio.hpp>
#include <boost\thread.hpp>
#include <boost\interprocess\sync\interprocess_semaphore.hpp>

#include <iostream>

typedef boost::interprocess::interprocess_semaphore Semaphore;

using namespace boost::asio::ip;

class ReceiveUDP
{
public:

    boost::thread*  m_pThread;

    boost::asio::io_service         m_io_service;
    udp::endpoint                   m_local_endpoint;
    udp::endpoint                   m_sender_endpoint;

    udp::socket                     m_socket;

    size_t      m_read_bytes;
    Semaphore   m_receive_semaphore;

    ReceiveUDP() :
        m_socket(m_io_service),
        m_local_endpoint(boost::asio::ip::address::from_string("192.168.0.254"), 11),
        m_sender_endpoint(boost::asio::ip::address::from_string("192.168.0.11"), 5550),
        m_receive_semaphore(0)
    {
        Start();
    }

    void Start()
    {
        m_pThread = new boost::thread(&ReceiveUDP::_ThreadFunction, this);
    }

    void _HandleReceiveFrom(
        const boost::system::error_code& error,
        size_t                                  received_bytes)
    {
        m_receive_semaphore.post();

        m_read_bytes = received_bytes;
    }

    void _ThreadFunction()
    {
        try
        {
            boost::array<char, 100> recv_buf;

            m_socket.open(udp::v4());
            m_socket.bind(m_local_endpoint);
            m_io_service.run();

            while (1)
            {
#if 1 // THIS WORKS

                m_read_bytes = m_socket.receive_from(
                    boost::asio::buffer(recv_buf), m_sender_endpoint);

#else // THIS DOESN'T WORK

                m_socket.async_receive_from(
                    boost::asio::buffer(recv_buf),
                    m_sender_endpoint,
                    boost::bind(&ReceiveUDP::_HandleReceiveFrom, this,
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));

                /* The program locks on this wait since _HandleReceiveFrom
                is never called. */
                m_receive_semaphore.wait();

#endif

                std::cout.write(recv_buf.data(), m_read_bytes);
            }

            m_socket.close();
        }
        catch (std::exception& e)
        {
            std::cerr << e.what() << std::endl;
        }
    }
};

void main()
{
    ReceiveUDP  receive_thread;

    receive_thread.m_pThread->join();
}

A timed_wait on the semaphore is to be preferred, however for debug purposes I have used a blocking wait as in the code above. 信号量上的timed_wait是首选,但是出于调试目的,我使用了阻塞等待,如上面的代码所示。

Did I miss something? 我错过了什么? Where is my mistake? 我的错误在哪里?

Your call to io_service.run() is exiting because there is no work for the io_service to do. 您对io_service.run()调用正在退出,因为io_service没有工作要做。 The code then enters the while loop and calls m_socket.async_receive_from . 然后代码进入while循环并调用m_socket.async_receive_from At this point the io_service is not running ergo it never reads the data and calls your handler. 此时io_service没有运行,它从不读取数据并调用您的处理程序。

you need to schedule the work to do before calling io_service run: 你需要在调用io_service run之前安排工作:

ie: 即:

// Configure io service
ReceiveUDP  receiver;

m_socket.open(udp::v4());
m_socket.bind(m_local_endpoint);
m_socket.async_receive_from(
    boost::asio::buffer(recv_buf),
    m_sender_endpoint,
    boost::bind(&ReceiveUDP::_HandleReceiveFrom, receiver,
    boost::asio::placeholders::error,
    boost::asio::placeholders::bytes_transferred));

The handler function will do the following: 处理函数将执行以下操作:

// start the io service
void HandleReceiveFrom(
    const boost::system::error_code& error,
    size_t received_bytes)
{
    m_receive_semaphore.post();

    // schedule the next asynchronous read
    m_socket.async_receive_from(
        boost::asio::buffer(recv_buf),
        m_sender_endpoint,
        boost::bind(&ReceiveUDP::_HandleReceiveFrom, receiver,
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred));

    m_read_bytes = received_bytes;
}

Your thread then simply waits for the semaphore: 然后你的线程只是等待信号量:

while (1)
{
    m_receive_semaphore.wait();
    std::cout.write(recv_buf.data(), m_read_bytes);
}

Notes: 笔记:

  1. Do you really need this additional thread? 你真的需要这个额外的线程吗? The handler is completely asynchronous, and boost::asio can be used to manage a thread pool (see: think-async ) 处理程序是完全异步的,boost :: asio可用于管理线程池(请参阅: think-async
  2. Please do not use underscores followed by a capitol letter for variable / function names. 请不要使用下划线,然后使用国会大厦字母表示变量/功能名称。 They are reserved. 它们是保留的。

m_io_service.run() returns immediately, so noone dispatches completion handlers. m_io_service.run()立即返回,因此没有人调度完成处理程序。 Note that io_service::run is a kind of "message loop" of an asio-based application, and it should run as long as you want asio functionality to be available (this's a bit simplified description, but it's good enough for your case). 请注意, io_service::run是基于asio的应用程序的一种“消息循环”,只要您希望asio功能可用,它就应该运行(这是一个简单的描述,但它对您的情况来说已经足够了) 。

Besides, you should not invoke async.operation in a loop. 此外,您不应该在循环中调用async.operation。 Instead, issue subsequent async.operation in the completion handler of the previous one -- to ensure that 2 async.reads would not run simultaniously. 相反,在前一个完成处理程序中发出后续的async.operation - 以确保2个async.reads不会同时运行。

See asio examples to see the typical asio application design. 请参阅asio示例以查看典型的asio应用程序设计。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM