2015-10-03 8 views
9

Ich versuche, ein UDP-Paket mit Boost Asio zu erhalten. Mein Code basiert auf this blocking UDP client example from the asio documentation.Warum empfängt dieses Programm nicht die erwarteten UDP-Pakete?

Ich versuche, ein BOOTP-ähnliches UDP-Paket von einem C6655 TI DSP zu empfangen, die in 3-Sekunden-Intervallen übertragen werden. Ich habe Wireshark, der die gleiche Schnittstelle beobachtet, die mein Programm überwacht, und es kann die eingehenden Pakete sehen (siehe unten für die genauen Paketdaten, die von Wireshark exportiert wurden). Die Pakete sind nicht wirklich kommen aus dem DSP, habe ich eins mit tcpdump gefangen und ich simuliere es von einem Raspberry Pi mit packeth.

Allerdings empfängt mein Programm die Pakete nicht. Es hat einen Timeout von 4 Sekunden (da der DSP alle 3 Sekunden sendet). Wenn es das Zeitlimit erreicht, gibt es eine entsprechende Nachricht aus, andernfalls soll es die Anzahl der empfangenen Bytes drucken. Der vollständige (kompilierbare) Quellcode des Programms folgt (etwa 100 Zeilen).

Der Befehl wird mit den Parametern 192.168.5.122 67 4000 aufgerufen, was bedeutet, dass 192.168.5.122:67 mit einer Zeitüberschreitung von 4.000 Millisekunden überwacht wird.

Edit: Zusätzlich zu dem Code unten, habe ich auch versucht, dies als mein Endpunkt: udp::endpoint listen_endpoint(boost::asio::ip::address_v4::any(), atoi(argv[2])); sowie die IP-Adresse 0.0.0.0 wie von einem Suchergebnis irgendwo vorgeschlagen.

Ich habe auch die folgenden ohne Erfolg:

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

ich ein Programm, das dieses Paket in der Lage, mit Berkeley Sockeln zu erhalten, geschrieben. Es macht nichts besonderes, was ich sehen kann, außer Bindung an INADDR_ANY. Hier

ist das vollständige Programm:

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

Hier ist das Paket, das ich erhalten werde versuchen. Dazu gehört auch die Ethernet-Frame:

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        ...... 

Ich habe eine Socket-Implementierung Berkeley, die dieses Paket empfangen kann (I Fehlerbehandlung und andere misc entfernt Code.):

{ 
    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 ist normalerweise ein Broadcast, der spezielle Flags benötigt, um –

+0

@BenVoigt zu empfangen Kannst du erklären, wie man das mit 'boost :: asio' macht? – Steve

+0

Ich habe versucht, auf Port '0' zu hören (was seltsam scheint), wie es von einigen anderen Suchergebnissen vorgeschlagen wurde, aber ohne Erfolg. – Steve

Antwort

3

Überlegen Sie, was passiert, nachdem socket_.cancel() , aber vor dem nächsten Anruf an socket_.async_receive(). Wenn Daten eintreffen, ist dem Socket kein "receive handler" zugewiesen. Wenn der Zeitgeber abgelaufen ist, wird run_once() 3 Mal aufgerufen (weil alle async_wait(), expires_at() und einige der anderen Stornierungen von bereits zugewiesenen Handlern verursachen und bewirken, dass die bereits zugewiesenen Handler in die Ausführungswarteschlange mit dem Fehlercode operation_aborted versetzt werden).

Sie haben nicht erwähnt, auf was Ihr Timeout eingestellt ist. Das Beispiel, auf dem Sie Ihren Code basieren (der aus der Boost-Dokumentation), legt das Zeitlimit auf 10 Sekunden fest, Sie konfigurieren es jedoch. Wenn das Zeitlimit zu eng ist, würde dies zu einem Durchdrehen führen (post-> cancel previous-> call previous handler-> post-> usw.) und möglicherweise socket_.cancel() aufrufen, während das Paket empfangen wird. Wenn das der Fall ist, müssten Sie nicht so weit gehen, um es mit einer Sendung zu testen. Sie könnten es auch mit einer Punkt-zu-Punkt-Verbindung sehen.

Bearbeiten: während der Verwendung eines langen Timeout (4000 ms) und Ihren genauen Code konnte ich eine gesendete Sendung empfangen.Ich musste "traditionelles" netcat installieren, weil BSD netcat defekt ist. Aber die Linie arbeitete unter:

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

XXX.255 ist nicht wörtlich "XXX.255". Es ist meine lokale Broadcast-Adresse. Option -b in nc.bsd ist kaputt (Sie können sehen, warum, wenn Sie nc.bsd mit obigen Optionen strace).

unix stackexchange nc.traditional hat ein gutes Beispiel dafür, wie andere herausgefunden haben, warum nc.bsd UDP-Broadcasts nicht tun kann (Option -b tut nichts).

PackEth war nicht in der Lage, Broadcasts oder p-t-p-Verkehr zu senden, also würde ich es nicht als Maß dafür verwenden, ob Sie Broadcast-Verkehr senden können. Zugegebenermaßen habe ich nicht viel Erfahrung damit, also weiß ich nicht, ob es kaputt ist oder ob ich es nicht richtig konfiguriert habe.

+0

Ich glaube nicht, dass diese Art von Schleifen möglich ist, selbst bei einem engen oder sofortigen Timeout. Wenn Daten eintreffen und keine ausstehende Leseoperation vorliegt, wird das Datagramm immer noch in den Kernel eingereiht. Wenn Daten verfügbar sind, wird innerhalb der initiierenden Operation 'async_receive()' ein Lesevorgang ausgeführt, wodurch bewirkt wird, dass der Beendigungshandler mit Erfolg gesendet wird, wobei zu diesem Zeitpunkt die Leseoperation nicht mehr abbrechbar ist. –

+0

@ Tanner Sansbury, Sie haben Recht. Ich habe eine Reihe von Szenarien ausprobiert und alle arbeiten Punkt für Punkt und scheitern mit der Broadcast-Adresse. Ich habe versucht, die Broadcast-Option vor und nach der Bindung einzustellen. Nichts scheint zur Arbeit zu kommen. Ich mache das auf Linux Mint mit Boost 49, BTW. Dachte, ich würde das erwähnen, weil das Einzige, dem alle zustimmen, ist, dass Broadcast-Routing unter Linux und Windows anders funktioniert. Oh, und ich habe es auf async_receive_from() umgestellt. Ich würde nicht helfen. Obwohl es sich um ein PackETH-Problem handeln könnte. Ich sehe Pakete nicht ankommen, wenn p-t-p gesendet wird, während nc-u funktioniert. –