2012-11-27 37 views
5

मैं बूस्ट लाइब्रेरीज़ (एएसआईओ) का उपयोग करके केवल यूडीपी सॉकेट से डेटा प्राप्त करने के लिए समर्पित एक स्वायत्त धागा बनाना चाहता हूं। यह थ्रेड यूडीपी सॉकेट से प्राप्त कुछ डेटा द्वारा ट्रिगर किया गया एक अनंत लूप होना चाहिए। मेरे आवेदन में मुझे एक एसिंक्रोनस प्राप्त ऑपरेशन का उपयोग करने की आवश्यकता है।बूस्ट asio udp सॉकेट async_receive_from हैंडलर को कॉल नहीं करता

यदि मैं सिंक्रोनस फ़ंक्शन का उपयोग करता हूं तो सब कुछ अपेक्षित काम करता है।

हालांकि अगर मैं हैंडलर से async_receive_from का उपयोग कभी नहीं करता हूं। चूंकि मैं यह पता लगाने के लिए एक सेमफोर का उपयोग करता हूं कि कुछ डेटा प्राप्त हुए हैं, प्रोग्राम लॉक और लूप कभी ट्रिगर नहीं होता है।

मैंने सत्यापित किया है (एक नेटवर्क विश्लेषक के साथ) कि प्रेषक डिवाइस यूडीपी सॉकेट पर डेटा भेजता है।

मैंने निम्नलिखित कोड में समस्या को अलग कर दिया है।

#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(); 
} 

सेमाफोर पर एक timed_wait लेकिन डिबग प्रयोजनों के लिए मैं ऊपर कोड में के रूप में एक अवरुद्ध इंतजार का इस्तेमाल किया है, पसंद किया जा रहा है।

क्या मुझे कुछ याद आया? मेरी गलती कहां है?

उत्तर

7

आपकी कॉल io_service.run() से बाहर निकल रही है क्योंकि io_service करने के लिए कोई काम नहीं है। कोड while लूप में प्रवेश करता है और m_socket.async_receive_from पर कॉल करता है। इस बिंदु पर io_service एर्गो नहीं चल रहा है, यह डेटा को कभी नहीं पढ़ता है और आपके हैंडलर को कॉल करता है।

आप काम का समय निर्धारित करने io_service रन कॉल करने से पहले क्या करने की जरूरत:

अर्थात्:

// 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)); 

हैंडलर समारोह निम्न करेगा:

// 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; 
} 

आपका धागा तो बस इंतजार करता है सेमफोर के लिए:

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

नोट्स:

  1. क्या आपको वास्तव में इस अतिरिक्त धागे की आवश्यकता है? हैंडलर पूरी तरह से असीमित है, और बूस्ट :: एएसओ का उपयोग थ्रेड पूल को प्रबंधित करने के लिए किया जा सकता है (देखें: think-async)
  2. कृपया वेरिएबल/फ़ंक्शन नामों के लिए कैपिटल अक्षर के बाद अंडरस्कोर का उपयोग न करें। वे आरक्षित हैं।
+0

बहुत बहुत धन्यवाद! मैंने आपके सुझावों के अनुसार कोड को संशोधित किया है और सब कुछ ठीक काम करता है। मैंने थ्रेड निर्माण से पहले आईओ सेवा को कॉन्फ़िगर किया है। कॉल io_service.run करने के लिए() सिर्फ धागा निर्माण के बाद है: \t शून्य प्रारंभ() \t { \t \t m_socket.open (यूडीपी :: v4()); \t \t m_socket.bind (m_local_endpoint); \t \t StartRead(); \t \t m_pThread = नया बढ़ावा :: थ्रेड (और ReceiveUDP :: _ थ्रेड फ़ंक्शन, यह); \t \t m_io_service.run(); \t} जहां StartRead() async_receive_from पर कॉल है। फिर से धन्यवाद। – arms

+0

इसके लिए धन्यवाद, मैं पागल हो रहा था। – Alex

0

m_io_service.run() तुरंत लौटता है, इसलिए कोई भी पूरा करने वाले हैंडलर प्रेषित नहीं करता है। ध्यान दें कि io_service::run एक एएसओ-आधारित एप्लिकेशन का एक "संदेश लूप" है, और जब तक आप एएसओ कार्यक्षमता उपलब्ध होना चाहते हैं, तब तक इसे चलाना चाहिए (यह थोड़ा सा सरल वर्णन है, लेकिन यह आपके मामले के लिए पर्याप्त है)।

इसके अलावा, आपको लूप में async.operation का आह्वान नहीं करना चाहिए। इसके बजाए, पिछले एक के पूरा होने वाले हैंडलर में बाद में async.operation - यह सुनिश्चित करने के लिए कि 2 async.reads एक साथ नहीं चलेंगे।

विशिष्ट एएसओ एप्लिकेशन डिज़ाइन को देखने के लिए एएसओ उदाहरण देखें।