2010-12-06 58 views
10

J'ai un client qui envoie des données via UDP-broadcast. (Pour dire 127.0.0.255:12345)Laissez deux serveurs UDP écouter sur le même port?

Maintenant, je veux avoir plusieurs serveurs d'écoute de ces données. Pour le faire sur une machine locale, ils doivent partager le port 12345 pour l'écoute. Ma question est, si cela est possible, s'il y a des inconvénients et s'il pourrait y avoir des problèmes avec cette approche.

Il existe une alternative qui entraîne malheureusement beaucoup de frais généraux:
Implémentez une sorte de processus d'enregistrement. Au démarrage, chaque serveur indique au client son port. Le client envoie alors les messages à chaque port (il faut envoyer les données plusieurs fois, il faut mettre en place une sorte de handshaking ...)
Connaissez-vous une meilleure alternative? Si cela compte:
J'utilise C++ avec Boost :: Asio. Le logiciel devrait être portable (principalement Linux et Windows).

+0

J'ai essayé d'utiliser la méthode suivante de boost :: asio :: :: udp socket 'Set_Option (udp :: socket :: reuse_address (true));' sans succès ... Il est Presque pas de documentation à ce sujet, quelqu'un a un indice à ce sujet? – MOnsDaR

+1

Voir aussi http://stackoverflow.com/questions/14388706/ –

Répondre

5

Cette réponse est référencée à la réponse de cdhowie, qui a lié un document qui indique que SO_REUSEPORT aurait l'effet que j'essaie d'atteindre.

J'ai étudié comment et si cette option est implémentée et principalement centrée sur Boost :: Asio et Linux. Boost :: Asio ne définit cette option que si le système d'exploitation est égal à BSD ou Mac OS X.

Le code pour cela est contenu dans le fichier boost/asio/detail/reactive_socket_service.hpp (Boost Version 1.40, dans les versions plus récentes, le code a été déplacé dans d'autres fichiers).
Je me suis demandé pourquoi Asio ne définit pas cette option pour les plates-formes comme Linux et Windows.

Il y a plusieurs références discuter que ce n'est pas implémenté sous Linux: https://web.archive.org/web/20120315052906/http://kerneltrap.org/mailarchive/linux-netdev/2008/8/7/2851754
http://kerneltrap.org/mailarchive/linux-kernel/2010/6/23/4586155

Il y a aussi un patch qui devrait ajouter cette fonctionnalité au noyau: https://web-beta.archive.org/web/20110807043058/http://kerneltrap.org/mailarchive/linux-netdev/2010/4/19/6274993

Je ne savoir si cette option existe pour Windows, mais en définissant portable comme un attribut pour un logiciel fonctionnant sous Linux, cela signifie que SO_REUSEPORT est spécifique au système d'exploitation et qu'il n'y a pas de solution portable pour ma question. Dans l'une des discussions que j'ai liées, il est recommandé à UDP d'implémenter un maître-écouteur qui fournit ensuite les données entrantes à plusieurs esclaves-auditeurs.

Je vais marquer cette réponse comme acceptée (bien que je me sente mal en acceptant ma propre réponse), car elle indique pourquoi l'approche de l'utilisation de SO_REUSEPORT échouera en essayant de l'utiliser avec un logiciel portable.

+0

Bien sûr, depuis lors, Linux a eu la fonctionnalité ajoutée (depuis le noyau 3.9). Voir des informations intéressantes ici: http://lwn.net/Articles/542629/ - cependant, le 'SO_REUSEPORT' signifie que plusieurs serveurs peuvent accepter des datagrammes, pas qu'ils reçoivent tous le même datagramme. –

22

Vous devrez lier la socket dans les deux processus avec l'option SO_REUSEPORT. Si vous ne spécifiez pas cette option dans le premier processus, la liaison dans la seconde échouera. De même, si vous spécifiez cette option dans la première, mais pas dans la seconde, la liaison dans la seconde échouera. Cette option spécifie effectivement à la fois une requête ("Je veux lier à ce port même si elle est déjà liée par un autre processus") et une permission ("d'autres processus peuvent également lier ce port").

Voir la section 4.12 de this document pour plus d'informations.

+0

J'ai marqué cette réponse comme "correcte" car elle montre qu'il n'est pas possible d'écouter à 1 port avec serveur serveral en même temps. L'URL liée supporte cette réponse. Si une autre réponse est affichée qui montre que c'est possible ou qui a une explication plus détaillée, je marquerai la nouvelle réponse comme acceptée à la place de ceci. – MOnsDaR

+1

@MOnsDaR: Que voulez-vous dire "pas possible?" De ce document: * "L'indicateur SO_REUSEPORT permet à plusieurs processus de se lier à la même adresse à condition que tous utilisent l'option SO_REUSEPORT." * – cdhowie

+2

Il ne montre rien de ce genre. Il montre que c'est * possible * et il montre * comment. * – EJP

3

Plusieurs sources expliquent que vous devez utiliser SO_REUSEADDR sous Windows. Mais aucune mention qu'il est possible de recevoir le message UDP avec et sans liant le socket. Le code ci-dessous lie le socket à un listen_endpoint local, ce qui est essentiel, car sans cela vous pouvez et recevrez toujours vos messages UDP, mais par défaut, vous aurez la propriété exclusive du port. Toutefois, si vous définissez reuse_address (true) sur le socket (ou sur l'accepteur lors de l'utilisation de TCP) et que vous liez le socket par la suite, cela permettra à plusieurs applications ou plusieurs instances de votre propre application de le faire à nouveau. tout le monde recevra tous les messages.

// Create the socket so that multiple may be bound to the same address. 
boost::asio::ip::udp::endpoint listen_endpoint(
    listen_address, multicast_port); 

// == important part == 
socket_.open(listen_endpoint.protocol()); 
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true)); 
socket_.bind(listen_endpoint); 
// == important part == 

boost::array<char, 2000> recvBuffer; 
socket_.async_receive_from(boost::asio::buffer(recvBuffer), m_remote_endpoint, 
     boost::bind(&SocketReader::ReceiveUDPMessage, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)