OVH Cloud OVH Cloud

Socket UDP en broadcast

83 réponses
Avatar
Thomas Nemeth
Bonjour !

Je cherche à émettre des données (une chaîne de 30 caractères) en
broadcast UDP. Si je sais me débrouiller pour les socket TCP (et encore
je n'ai pas tenté le broadcast), pour les sockets UDP j'ai du mal à voir
comment configurer la socket afin de pouvoir écrire à partir d'une
machine et lire à partir de plusieurs autres.

Jusque-là, pour mes serveurs, je faisais la recette habituelle :
- pour les serveurs :
socket()
setsockopt() pour REUSEADDR et LINGER
bind()
listen()

- pour les clients :
socket()
setsockopt() (idem)
bind()
connect()

Or pour les sockets UDP en broadcast, je ne vois pas trop comment
faire. Pour l'instant (ce qui ne marche pas) :
socket()
setsockopt() pour REUSEADDR et BROADCAST
connect()

Puis write() ou send() pour écrire et read() ou recv() pour lire.

Quelqu'un a-t-il une idée sur la méthode à appliquer ?
Merci d'avance.

Thomas.

10 réponses

Avatar
Laurent Wacrenier
Thomas Nemeth écrit:
Pas besoin de select() s'il y a des threads. Il suffit de bloquer sur
recvfrom() avec une socket de controle sur un autre port.


Si si : dans ce thread j'attend aussi des connexions sur RS :)


Une fois que tu as deux threads, tu peux en avoir trois.


Avatar
Thomas Nemeth
Laurent Wacrenier a tapoté :

Thomas Nemeth écrit:
- clients : reçoivent en permanence ces données.
socket()
setsockopt()
while (cond) recvfrom()


Il manque un bind() pour définir l'adresse et le port d'attente.


Hum... recvfrom() ne le fait pas ?


Mais si je veux pouvoir faire en sorte qu'un des clients envoie
une commande de changement de type de données au serveur, il faut
que le serveur puisse écouter... Or écouter sur du broadcast
alors qu'on y écrit soi-même peut être un poil gênant, non ? Le
serveur risque de recevoir ses propres messages et donc croire
qu'il reçoit une demande de connexion.


Mieux vaut utiliser un autre port pour le controle.


Effectivement, cependant ça m'oblige à ouvrir une nouvelle socket.
C'est une solution à envisager : je la garde sous le coude :)


Y aurait-il la possibilité pour le serveur de faire un truc dans
le genre :
socket()
setsockopt()
bind() // sur l'adresse locale
recv() // dans un thread


recvfrom()


Ok.


while (cont) sendto(broadcast) // dans un autre thread


Plutôt sendto() vers l'adresse récupérée par le recvfrom().


Pas possible : le cahier des charges dit que ça doit obligatoirement
être du broadcast. J'aurais effectivement préféré faire du
client/serveur au sens habituel (puisque j'ai déjà fait plusieurs
programmes sur le modèle -- via TCP) mais malheureusement ce n'est
pas possible.

Le recvfrom() dans le 1er thread sert uniquement à savoir quand
basculer d'un mode d'émission à un autre. Alors que le sendto() est
destiné à balancer les données en permanences sur le broadcast.


Thomas.


Avatar
Thomas Nemeth
Laurent Wacrenier a tapoté :

Thomas Nemeth écrit:
Pas besoin de select() s'il y a des threads. Il suffit de bloquer
sur recvfrom() avec une socket de controle sur un autre port.


Si si : dans ce thread j'attend aussi des connexions sur RS :)


Une fois que tu as deux threads, tu peux en avoir trois.


En fait ce ne sont pas vraiment des threads. J'en ai parler pour
me simplifier l'explication. La partie qui émet les données en
permanence est faite par interruption alors que celle qui attend
les connexions RS et UDP pour changer le mode d'émission est le
programme principal utilisant déjà select().

Cependant je ne vois pas trop où serait le pb de rajouter la socket
dans la liste des fdsets de réception pour select()...


Thomas.



Avatar
Laurent Wacrenier
Thomas Nemeth écrit:
Il manque un bind() pour définir l'adresse et le port d'attente.


Hum... recvfrom() ne le fait pas ?


Et pourquoi le paquet irait à ton démon et pas à un autre ?
L'adresse en argument de recvfrom() est celle à la source du paquet.

Effectivement, cependant ça m'oblige à ouvrir une nouvelle socket.
C'est une solution à envisager : je la garde sous le coude :)


Mieux vaut éviter les dialogues en broadcast.
Ça fini en dialogues de sourds et en inondations du réseau.


Avatar
Laurent Wacrenier
Thomas Nemeth écrit:
Cependant je ne vois pas trop où serait le pb de rajouter la socket
dans la liste des fdsets de réception pour select()...


Oui s'il n'y a pas de threads. Sinon c'est de la programation mixte
avec bugs venant de plusieurs cotés à la fois.

Avatar
Nicolas George
Laurent Wacrenier wrote in message
:
S'il n'y a qu'une seule adresse, ça se règle avec un bind().


bind, ça règle l'adresse locale, ça n'a rien à voir. Au vu de quelques
autres messages, une petite révision de la doc de connect s'impose :

« If the initiating socket is not connection-mode, then connect() shall set
the socket's peer address, and no connection is made. For SOCK_DGRAM
sockets, the peer address identifies where all datagrams are sent on
subsequent send() functions, and limits the remote sender for subsequent
recv() functions. If address is a null address for the protocol, the
socket's peer address shall be reset. »

Avatar
Nicolas George
Laurent Wacrenier wrote in message
:
L'adresse en argument de recvfrom() est celle à la source du paquet.


Précisons : l'adresse en argument à recvfrom est un pointeur qui sert à
fournir en *retour* l'adresse source du paquet.

Avatar
Thomas Nemeth
Laurent Wacrenier a tapoté :

Thomas Nemeth écrit:
Il manque un bind() pour définir l'adresse et le port d'attente.


Hum... recvfrom() ne le fait pas ?


Et pourquoi le paquet irait à ton démon et pas à un autre ?
L'adresse en argument de recvfrom() est celle à la source du paquet.


Je pensais, bêtement, que ça suffisait pour que la socket reçoive
les paquets transitant par l'adresse et le port spécifié...


Effectivement, cependant ça m'oblige à ouvrir une nouvelle
socket. C'est une solution à envisager : je la garde sous le
coude :)


Mieux vaut éviter les dialogues en broadcast.
Ça fini en dialogues de sourds et en inondations du réseau.


Je le note :)

Merci pour tout !


Thomas.



Avatar
Laurent Wacrenier
Thomas Nemeth écrit:
Je pensais, bêtement, que ça suffisait pour que la socket reçoive
les paquets transitant par l'adresse et le port spécifié...


recvfrom() ne spécifie pas d'adresse ou de port, d'où le bind(). Il
récupère juste celle de l'éméteur (en mode connecté on aurait
l'information avec getpeername()).

Avatar
Thomas Nemeth
Laurent Wacrenier a tapoté :

Thomas Nemeth écrit:
Je pensais, bêtement, que ça suffisait pour que la socket reçoive
les paquets transitant par l'adresse et le port spécifié...


recvfrom() ne spécifie pas d'adresse ou de port, d'où le bind(). Il
récupère juste celle de l'éméteur (en mode connecté on aurait
l'information avec getpeername()).


Ok, donc son utilisation ne nécessite pas de lui passer une variable
sockaddr_in paramétrée, mais sert plutôt à stocker les données
venant de l'émetteur.
Effectivement (après un détour par le man) c'est bien ce que dit
entre les lignes la description de la fonction...


Thomas.