2015-10-03 19 views
9

Boost asio kullanarak UDP paketi almaya çalışıyorum. Kodum this blocking UDP client example from the asio documentation'un temel alınmıştır.Bu program neden beklenen UDP paketlerini almıyor?

3 saniyelik aralıklarla iletilen bir C6655 TI DSP'den BOOTP benzeri bir UDP paketi almaya çalışıyorum. Programımın dinlediği aynı arabirimi izleyen Wireshark'ım var ve gelen paketleri görebiliyorum (Wireshark'tan aktarılan tam paket veri için aşağıya bakın). Paketler, DSP'den gelen gerçekten değil, ben tcpdump ile bir tane yakaladım ve ben packeth ile bir Raspberry Pi'den simüle ediyorum. Ancak, programım paketleri almıyor. 4 saniyelik bir zamanaşımı vardır (DSP her 3 saniyede bir yayınladığı için). Zaman aşımına çarparsa, bu etki için bir mesaj yazar, aksi halde alınan bayt sayısını yazdırması gerekir. Programın tam (compilable) kaynak kodu (yaklaşık 100 satır) takip eder.

Komut, 192.168.5.122 67 4000 parametreleriyle çağrılıyor, yani 192.168.5.122:67 üzerinde 4000 milisaniye bir zamanaşımı ile dinleniyor.

Düzenleme: Aşağıdaki kodun yanı sıra, bunu bir noktadan başka bir arama sonucu tarafından önerildiği gibi ve 0.0.0.0 IP adresi olarak denedim.

boost::asio::socket_base::broadcast option(true); 
socket_.set_option(option); 

düzgün Berkeley yuvalarını kullanarak yazılı bu paketi, alabilecek bir programa sahip yapın:

Ben de boşuna aşağıdaki eklendi. INADDR_ANY'ye bağlanmanın ötesinde, görebileceğim özel bir şey yapmıyor. İşte

// 
// blocking_udp_client.cpp 
// ~~~~~~~~~~~~~~~~~~~~~~~ 
// 
#include <boost/asio/deadline_timer.hpp> 
#include <boost/asio/io_service.hpp> 
#include <boost/asio/ip/udp.hpp> 
#include <boost/bind.hpp> 
#include <boost/date_time/posix_time/posix_time_types.hpp> 
#include <iostream> 

using boost::asio::deadline_timer; 
using boost::asio::ip::udp; 

class listener 
{ 
public: 
    listener(const udp::endpoint& listen_endpoint) 
     : socket_(io_service_, listen_endpoint) 
     , deadline_(io_service_) 
    { 
     deadline_.expires_at(boost::posix_time::pos_infin); 
     check_deadline(); 
    } 

    std::size_t receive(const boost::asio::mutable_buffer& buffer, boost::posix_time::time_duration timeout, boost::system::error_code& ec) 
    { 
     deadline_.expires_from_now(timeout); 
     ec = boost::asio::error::would_block; 
     std::size_t length = 0; 
     socket_.async_receive(boost::asio::buffer(buffer), boost::bind(&listener::handle_receive, _1, _2, &ec, &length)); 

     // TODO: The following do/while is hinky. Does run_one() need to happen before the comparison? 
     do io_service_.run_one(); 
     while (ec == boost::asio::error::would_block); 

     return length; 
    } 

private: 
    void check_deadline() 
    { 
     if (deadline_.expires_at() <= deadline_timer::traits_type::now()) 
     { 
      // cancel() won't work on XP. Something about using close() instead... Look it up. I'm doing this on Win10. 
      socket_.cancel(); 
      deadline_.expires_at(boost::posix_time::pos_infin); 
     } 
     deadline_.async_wait(boost::bind(&listener::check_deadline, this)); 
    } 

    static void handle_receive(const boost::system::error_code& ec, std::size_t length, boost::system::error_code* out_ec, std::size_t* out_length) 
    { 
     *out_ec = ec; 
     *out_length = length; 
    } 

private: 
    boost::asio::io_service io_service_; 
    udp::socket socket_; 
    deadline_timer deadline_; 
}; 

int main(int argc, char* argv[]) 
{ 
    try 
    { 
     if (argc != 4) 
     { 
      std::cerr << "Usage: blocking_udp_timeout <listen_addr> <listen_port> <timeout_ms>\n"; 
      return 1; 
     } 

     udp::endpoint listen_endpoint(boost::asio::ip::address::from_string("0.0.0.0"), atoi(argv[2])); 
     std::cout << "Endpoint: " << listen_endpoint << std::endl; 

     auto timeout = atoi(argv[3]); 
     std::cout << "Timeout : " << timeout << std::endl; 

     listener c(listen_endpoint); 

     for (;;) 
     { 
      char data[1024]; 
      boost::system::error_code ec; 
      auto n = c.receive(boost::asio::buffer(data), boost::posix_time::milliseconds{timeout}, ec); 

      if (ec) 
      { 
       std::cout << "Receive error: " << ec.message() << "\n"; 
      } 
      else 
      { 
       std::cout << "Received " << n << " bytes." << std::endl; 
      } 
     } 
    } 
    catch (std::exception& e) 
    { 
     std::cerr << "Exception: " << e.what() << "\n"; 
    } 

    return 0; 
} 

aldığım çalışıyorum paket geçerli:

İşte tam programıdır.

0000 ff ff ff ff ff ff c4 ed ba aa 28 35 08 00 45 00 ..........(5..E. 
0010 01 48 00 01 00 00 10 11 a9 a5 00 00 00 00 00 00 .H.............. 
0020 00 00 00 44 00 43 01 34 00 00 01 01 06 00 12 34 ...D.C.4.......4 
0030 56 78 00 01 00 00 00 00 00 00 00 00 00 00 00 00 Vx.............. 
0040 00 00 00 00 00 00 c4 ed ba aa 28 35 00 00 00 00 ..........(5.... 
0050 00 00 00 00 00 00 74 69 2d 62 6f 6f 74 2d 74 61 ......ti-boot-ta 
0060 62 6c 65 2d 73 76 72 00 00 00 00 00 00 00 00 00 ble-svr......... 
0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
0080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
0090 00 00 00 00 00 00 74 69 2d 62 6f 6f 74 2d 74 61 ......ti-boot-ta 
00a0 62 6c 65 2d 30 30 30 37 00 00 00 00 00 00 00 00 ble-0007........ 
00b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
00c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
00d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
00e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
00f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
0100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
0110 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
0120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
0130 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
0140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
0150 00 00 00 00 00 00        ...... 

ben bu paketi alabilecek bir Berkeley yuva uygulama (Ben hata işleme ve diğer çeşitli kod kaldırdık.) Var:

{ 
    struct sockaddr_in servaddr; 
    socklen_t len; 
    char mesg[RECV_BUFFER_LENGTH]; 

    sockfd = socket(AF_INET, SOCK_DGRAM, 0); 
    bzero(&servaddr, sizeof(servaddr)); 
    servaddr.sin_family = AF_INET; 
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    servaddr.sin_port = htons(67); 
    bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); 
    n = recvfrom(sockfd, mesg, RECV_BUFFER_LENGTH, 0, NULL, &len); 
} 
+0

BOOTP normalde, özel bayraklar gerektiren bir yayın gerektirir. –

+0

@BenVoigt Bunu "boost :: asio" ile nasıl gerçekleştireceğinizi açıklayabilir misiniz? – Steve

+0

Bulduğum diğer bazı arama sonuçlarının önerdiği gibi, '0' (garip görünüyor) bağlantı noktasını dinlemeyi denedim, ancak boşuna. – Steve

cevap

3

socket_.cancel() sonra neler düşünün Bu Ethernet çerçevesi içermektedir ancak socket_.async_receive() numaralı aramadan önce. Herhangi bir veri ulaşırsa, sokete atanan "alma işleyicisi" yoktur. Zamanlayıcının süresi dolduğunda, 3 kez çağrılması run_once() olur (her async_wait(), expires_at() ve diğerleriden bazıları zaten atanan işleyicilerin iptal edilmesine neden olur ve zaten atanan işleyicilerin operation_aborted hata koduyla çalışma kuyruğuna gönderilmesine neden olur).

Zamanaşımınızın ayarlandığından bahsetmediniz. Kodunuzu temel aldığınız örnek (Boost belgelerinden biri), zaman aşımını 10 saniyeye ayarlar, ancak bunu yapılandırılabilir hale getirirsiniz. Zaman aşımı çok sıkıysa, bu bir dönüşe neden olur (bir önceki -> önceki işleyiciyi geri çağır -> son -> vs.) ve paketi alırken muhtemelen socket_.cancel()'u arayabilir. Eğer durum buysa, bir yayın ile test etmek için şimdiye kadar gitmek zorunda kalmazsınız. Bunu noktadan noktaya bir bağlantıyla da görebilirdiniz.

Düzenle: uzun bir zaman aşımı (4000ms) ve tam kod kullanırken, gönderilen bir yayını alabildim.BSD netcat bozuk olduğu için "geleneksel" netcat yüklemeliydim. Ama hat aşağıda çalıştı:

echo "hello world" | nc.traditional -b -u 192.168.XXX.255 1500 

XXX.255 kelimenin tam anlamıyla "XXX.255" değildir. Bu benim yerel yayın adresim. nc.bsd'deki -b seçeneği bozuldu (yukarıdaki seçeneklerle nc.bsd'yi neden güttüğünüzü görebilirsiniz).

unix stackexchange nc.traditional, nc.bsd'nin UDP yayınlarını neden yapamadıklarını (-b seçeneği hiçbir şey yapmaz) başkalarına nasıl iyi bir örnek verdiğine iyi bir örnektir.

PackEth yayınları veya p-t-p trafiğini gönderemediğinden, yayın trafiğini gönderip gönderemediğinizin bir ölçütü olarak kullanamayacağım. Kuşkusuz, onunla çok fazla tecrübem yok, bu yüzden kırılmış mı yoksa doğru yapılandırılmamış mı bilmiyorum.

+0

Sıkı veya anlık zaman aşımı durumunda bile bu tür bir döngü mümkün olduğunu düşünmüyorum. Veri geldiğinde ve bekleyen bir okuma işlemi yoksa, datagram hala çekirdek içinde sıraya girer. Veri mevcutsa, "async_receive()" işlemi başlatılırken, tamamlama işleyicisinin başarı ile gönderilmesine neden olan bir okuma gerçekleşir, bu noktada okuma işlemi artık iptal edilemez. –

+0

@Tanner Sansbury, haklısınız. Bir dizi senaryoyu denedim ve hepsinin yayın adresi ile ilgili bir noktaya değindim ve başarısız olduğunu düşünüyorum. Yayın seçeneğini bağlamadan önce ve sonra ayarlamayı denedim. Hiçbir şey işe yaramıyor gibi görünüyor. Bunu Linux Mint'te, boost 49, btw ile yapıyorum. Bunu söylemeyi düşünürdüm çünkü herkesin kabul ettiği tek şey, yayın yönlendirmesinin Linux ve Windows üzerinde farklı çalışmasıdır. Oh, ve async_receive_from() 'a değiştirdim. Yardım etmedi. Bir PackETH sorunu olsa da. Nc -u çalışırken p-t-p gönderildiğinde gelen paketleri göremiyorum. –

İlgili konular