Boost :: asio UDP Broadcast avec port éphémère

J’ai des problèmes avec les transactions de diffusion udp sous boost :: asio, liées à l’extrait de code suivant. Puisque j’essaie de diffuser dans cette instance, deviceIP = “255.255.255.255”. devicePort est un port de gestion spécifié pour mon périphérique. Je veux utiliser un port local éphémère, donc je préférerais autant que possible ne pas avoir à socket.bind () après la connexion, et le code prend en charge la monodiffusion en définissant localPort = 0.

boost::asio::ip::address_v4 targetIP = boost::asio::ip::address_v4::from_ssortingng(deviceIP); m_targetEndPoint = boost::asio::ip::udp::endpoint(targetIP, devicePort); m_ioServicePtr = boost::shared_ptr(new boost::asio::io_service); m_socketPtr = boost::shared_ptr(new boost::asio::ip::udp::socket(*m_ioServicePtr)); m_socketPtr->open(m_targetEndPoint.protocol()); m_socketPtr->set_option(boost::asio::socket_base::broadcast(true)); // If no local port is specified, default parameter is 0 // If local port is specified, bind to that port. if(localPort != 0) { boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), localPort); m_socketPtr->bind(localEndpoint); } if(m_forceConnect) m_socketPtr->connect(m_targetEndPoint); this->AsyncReceive(); // Register Asynch Recieve callback and buffer m_socketThread = boost::shared_ptr(new boost::thread(boost::bind(&MyNetworkBase::RunSocketThread, this))); // Start thread running io_service process 

Peu importe ce que je fais en termes de parameters suivants, la transmission fonctionne correctement et je peux utiliser Wireshark pour voir les paquets de réponse provenant du périphérique comme prévu. Ces paquets de réponse sont également des diffusions, car le périphérique peut se trouver sur un sous-réseau différent de celui qui le recherche.

Les problèmes sont extrêmement étranges à mon avis, mais sont les suivants:

  1. Si je spécifie le port local et m_forceConnect = false, tout fonctionne correctement et mon rappel est déclenché de manière appropriée.
  2. Si je mets m_forceConnect = true dans le constructeur, mais que je passe un port local à 0, la transmission fonctionne correctement, mais mon rappel de réception ne se déclenche jamais. Je suppose que c’est parce que la cible (m_targetEndpoint) est 255.255.255.255 et que le périphérique a une véritable adresse IP, le paquet de réponse est filtré.
  3. ( ce que je veux réellement ) Si m_forceConnect = false (et les données sont transmises en utilisant un appel send_to), et le port local = 0, prenant donc un port éphémère, mon rappel RX se déclenche immédiatement avec un code d’erreur 10022 Argument non valide “erreur de socket.

Quelqu’un peut-il suggérer pourquoi je ne peux pas utiliser la connexion de cette manière (pas explicitement lié et pas explicitement connecté)? Je ne veux évidemment pas utiliser socket.connect () dans ce cas, car je veux répondre à tout ce que je reçois. Je ne veux pas non plus utiliser un port prédéfini, car je souhaite que l’utilisateur puisse créer plusieurs copies de cet object sans conflit de port.

Comme certains l’ont peut-être remarqué, l’objective général est d’utiliser la même classe de base d’interface réseau pour gérer à la fois les cas de monodiffusion et de diffusion. Évidemment, pour la version unicast, je peux parfaitement synchroniser m_socket-> connect () car je connais l’adresse IP du périphérique, et je reçois les réponses car elles proviennent de l’adresse IP connectée, donc j’ai défini m_forceConnect = true, .

Comme toutes mes transmissions utilisent send_to, j’ai aussi essayé de socket.connect (endpoint (ip :: addressv4 :: any (), devicePort), mais j’obtiens une exception ‘L’adresse demandée n’est pas valide dans son contexte’ lorsque j’essaie il.

J’ai essayé un piratage assez sérieux:

 boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), m_socketPtr->local_endpoint().port()); m_socketPtr->bind(localEndpoint); 

où j’extrais le numéro de port éphémère initial et essaye de le lier, mais curieusement qui lève une exception d’argument non valide lorsque j’essaie de lier.

OK, j’ai trouvé une solution à ce problème. Sous Linux, ce n’est pas nécessaire, mais sous Windows, j’ai découvert que si vous n’êtes ni lié ni connecté, vous devez avoir transmis quelque chose avant d’appeler asynch_recieve_from (), l’appel étant inclus dans la méthode this-> asynch_receive () .

Ma solution, faites une transmission factice d’une chaîne vide immédiatement avant de lancer l’appel asynch_receive sous Windows, afin que le code modifié devienne:

 m_socketPtr->set_option(boost::asio::socket_base::broadcast(true)); // If no local port is specified, default parameter is 0 // If local port is specified, bind to that port. if(localPort != 0) { boost::asio::ip::udp::endpoint localEndpoint(boost::asio::ip::address_v4::any(), localPort); m_socketPtr->bind(localEndpoint); } if(m_forceConnect) m_socketPtr->connect(m_targetEndPoint); // A dummy TX is required for the socket to acquire the local port properly under windoze // Transmitting an empty ssortingng works fine for this, but the TX must take place BEFORE the first call to Asynch_receive_from(...) #ifdef WIN32 m_socketPtr->send_to(boost::asio::buffer("", 0), m_targetEndPoint); #endif this->AsyncReceive(); // Register Asynch Recieve callback and buffer m_socketThread = boost::shared_ptr(new boost::thread(boost::bind(&MyNetworkBase::RunSocketThread, this))); 

C’est un peu un bidouillage dans mon livre, mais c’est beaucoup mieux que de mettre en œuvre toutes les exigences pour reporter l’appel à la réception asynchrone jusqu’à la première transmission.