Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1359 connectés 

  FORUM HardWare.fr
  Programmation
  C

  broadcast UDP sous linux et UDP + connect

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

broadcast UDP sous linux et UDP + connect

n°2134929
xilebo
noone
Posté le 04-04-2012 à 21:38:03  profilanswer
 

Bonsoir,
 
Je suis en train de coder une application nécessitant d'envoyer et de recevoir des broadcast sous linux et je rencontre quelques difficultés ( ou plutot interrogations ).
 
Je ne trouve pas de littérature sur internet m'expliquant en détail le fonctionnement, les pages man de chaque fonction sont insuffisantes à ce niveau.
 
Voici mon problème :
 
J'ai codé un petit test unitaire sous windows qui crée une socket DGRAM , et qui envoie en broadcast sur un port X des datas bien précis. Une application test ( sous windows egalement et uniquement ) se charge de réceptionner ces datas , et d'en renvoyer d'autre sur le même port X toujours en broadcast.
 
Ce test fonctionne sous windows, pour cela, j'ai juste eu besoin de créer ma socket ( DGRAM / IPPROTO_UDP ) , et de faire un bind sur l'adresse de l'interface sur laquelle je souhaite envoyer le broadcast. J'ai volontairement plusieurs interfaces, et ca envoie bien sur la 2eme ( vérifié sur wireshark des 2 cotés ).
 
Par contre, sous linux, le bind sur l'adresse de l'interface ne fonctionne pas. J'envoie bien mon message en broadcast, je vois bien qu'il part, qu'il est réceptionné par mon application test, celle-ci renvoie bien le message retour sur l'adresse broadcast, mais je ne le recois pas sous linux. Le seul moyen est de ne pas faire de bind sur l'adresse de l'interface comme sous windows, mais de le faire sur l'adresse broadcast.
 
Voici mes questions :  
   - Pourquoi cette différence, et où est-elle documentée ( je n'ai pas trouvé ) ? (je précise, j'ai bien fait le setsockopt qui positionne le broadcast à ACTIVE ).
   - pourquoi quand je fais le bind cette fois-ci sur l'adresse d'interface cela ne fonctionne pas ?
   - pourquoi quand je fais le bind sur la valeur INADDR_BROADCAST, cela ne fonctionne pas non plus ? Quelle différence avec l'adresse broadcast ( je vois bien la différence, INADDR_BROADCAST vaut 0xFFFFFFFF )
   - pourquoi quand je fais le bind sur la valeur INADDR_ANY cela marche bien ?  
 
 
Je ne peux pas poster de code ce soir, je ne suis pas à mon poste :p Je le ferai dès demain.
 
Merci de m'avoir lu :)


Message édité par xilebo le 05-04-2012 à 09:57:54
mood
Publicité
Posté le 04-04-2012 à 21:38:03  profilanswer
 

n°2134966
xilebo
noone
Posté le 05-04-2012 à 09:21:41  profilanswer
 

J'en profite pour poser une 2ème question : l'UDP unicast ne fonctionne pas sous linux alors que le même code fonctionne sous windows.
 
A priori , c'est l'appel à la fonction connect qui fait échouer.
 
Voici les portions de code ( dialogue entre 2 machines : 172.17.0.163 le test unitaire sous linux , 172.17.0.160 le serveur qui réceptionne le test sous windows )
 
Initialisation de la socket

Code :
  1. int oui = 1;
  2. m_socket = socket( AF_INET , SOCK_DGRAM , IPPROTO_UDP );
  3. if ( m_socket >= 0)
  4. {
  5.  // options de socket devant être paramétrées *avant* le bind() ou connect()
  6. #ifdef SO_NOSIGPIPE
  7.  if (setsockopt( m_socket , SOL_SOCKET, SO_NOSIGPIPE, (char *)&oui, sizeof(oui)) < 0)
  8.  {
  9.   ERROR(("setsockopt(SO_NOSIGPIPE): %s", strerror_socket(errno)));
  10.   CloseSocket( );
  11.   return PRT_FAILED;
  12.  }
  13. #endif
  14.  if (setsockopt( m_socket , SOL_SOCKET, SO_REUSEADDR, (char *)&oui, sizeof(oui)) < 0)
  15.  {
  16.   ERROR((setsockopt(SO_REUSEADDR): %s", strerror_socket(errno)));
  17.   CloseSocket( );
  18.   return PRT_FAILED;
  19.  }
  20. #ifdef SO_REUSEPORT
  21.  if (setsockopt( m_socket , SOL_SOCKET, SO_REUSEPORT, (char *)&oui, sizeof(oui)) < 0)
  22.  {
  23.   ERROR(("setsockopt(SO_REUSEPORT): %s", strerror_socket(errno)));
  24.   CloseSocket( );
  25.   return PRT_FAILED;
  26.  }
  27. #endif
  28.  ULONG hote_l =INADDR_NONE;
  29.  hote_l = inet_addr( m_address_sz );
  30.  ULONG peer_l;
  31.  peer_l = inet_addr( m_pair_address_sz );
  32.  if ( hote_l == INADDR_NONE )
  33.  {
  34.   ERROR(("%s is not a valid IP address", m_address_sz ) );
  35.   CloseSocket( );
  36.   return PRT_FAILED;
  37.  }
  38.  struct sockaddr_in adresse;
  39.  memset ( &adresse , 0 , sizeof( adresse ) );
  40.  adresse.sin_family  = AF_INET;
  41.  adresse.sin_addr.s_addr = hote_l;
  42.  adresse.sin_port  = htons ( (USHORT)m_port_l ); // port stocké dans un long, mais < 65535 donc cast OK
  43.  struct sockaddr_in adresse_peer;
  44.  memset ( &adresse_peer , 0 , sizeof( adresse_peer ) );
  45.  adresse_peer.sin_family  = AF_INET;
  46.  adresse_peer.sin_addr.s_addr = peer_l;
  47.  adresse_peer.sin_port  = htons( (USHORT)m_port_l); // port stocké dans un long, mais < 65535 donc cast OK
  48.  // boucler les tentatives ?
  49.  if ( bind( m_socket , (struct sockaddr *) &adresse , sizeof (adresse ) ) != 0 )
  50.  {
  51.   ERROR(("bind failed %s", strerror_socket(errno) ) );
  52.   CloseSocket( );
  53.   return PRT_FAILED;
  54.  }
  55.  if ( connect( m_socket , (struct sockaddr *) &adresse_peer , sizeof (adresse_peer ) ) != 0 )
  56.  {
  57.   ERROR(("connect failed %s", strerror_socket(errno) ) );
  58.   CloseSocket( );
  59.   return PRT_FAILED;
  60.  }
  61.  // options de socket devant être paramétrées *apres* le bind() ou connect()
  62.  struct timeval timeout;
  63.  timeout.tv_sec  = 0;
  64.  timeout.tv_usec = 100000;
  65.  if (setsockopt( m_socket , SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0)
  66.  {
  67.   PGAS_ERROR(("setsockopt(SO_RCVTIMEO): %s", strerror_socket(errno)));
  68.   CloseSocket( );
  69.   return PRT_FAILED;
  70.  }
  71.  // connection is OK
  72.  m_connected_b = TRUE;
  73.  return PRT_SUCCESS;
  74. }
  75. return PRT_FAILED;


 
 
code faisant le send : Pas besoin de sendto, l'adresse par défaut est celle spécifiée dans le connect

Code :
  1. while (tot < nBufferSize)
  2. {
  3. int envoye = 0;
  4. envoye = send( m_socket, retour + tot, nBufferSize - tot, 0 );
  5. if ( envoye < 0)
  6. {
  7.  ERROR(("erreur d'envoi a %s/%d: %s", m_address_sz, m_port_l, strerror_socket(errno)));
  8.  return PRT_FAILED;
  9. }
  10. else
  11. {
  12.  tot += envoye;
  13. }
  14. }


 
 
code du receive :  
 

Code :
  1. fd_set      fdin;
  2. timeval     tv;
  3. FD_ZERO(&fdin);
  4. FD_SET(m_socket, &fdin);
  5. tv.tv_sec = _timeout / 1000 ;
  6. tv.tv_usec = ( _timeout % 1000 ) * 1000 ;
  7. data = select(((int)m_socket)+1, &fdin, NULL, NULL, &tv);
  8. if (data == -1)
  9. {
  10. ERROR(("Erreur select : %s",strerror_socket(errno)));
  11. return PRT_FAILED;
  12. }
  13. else if (FD_ISSET(m_socket, &fdin) != 0)
  14. {
  15. recu = recv(m_socket, entete, BUFFERSIZE, 0 );
  16. }


 
 
Alors , avec ce code ( qui est le même sur les 2 machines ) voici comment sont fait les appels :
 
Sur la machine "test unitaire" je fais Init , puis Send, puis Recv , puis Shut.
 
Sur la machine "serveur pour test" , je fais Init , puis Recv , puis Send,  puis Shut  
 
Jusque là, c'est normal.
 
Avec ce code, si les 2 machines sont sous windows, mon test passe.
 
Avec ce même code, si la machine "test unitaire" est sous linux , et la machine "serveur pour test", le test ne passe pas.
 
Où est l'échec ?  
 - l'init fonctionne bien des 2 cotés ( les appels systèmes retournent tous OK ).
 - le premier envoi sous linux se passe bien car il est bien recu sous windows. L'appel connect sous linux est donc cohérent puisque pour l'envoi, je fais un send, sans préciser le destinaire.
 - suite à la réception sous windows, celui-ci envoie une réponse pour être réceptionné sous linux.
 - c'est la réception sous linux qui pose problème, le select retourne "nodata", par conséquent, je ne peux pas faire le recv.
 
 - A savoir que si je mets ( juste coté linux ) en commentaire l'appel à connect  ( ce qui a pour conséquence de m'obliger à remplacer le send par un sendto vers ma machine "serveur pour test" , je ne change pas le recv , qui revient à faire un recvfrom avec sockaddr_in NULL ) , cela fonctionne bien.
 
Je ne comprends pas quelle erreur j'ai pu faire qui fait que le comportement est différent sous linux.
 
Il y a soit un appel de trop qui gêne ( dans les setsockopt par exemple ), soit il manque quelque chose spécifique à linux.
 
Avez vous une idée car je sèche ?
 
Merci :)


Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  C

  broadcast UDP sous linux et UDP + connect

 

Sujets relatifs
installer j3d eclipse linuxAppeler une librairie dynamique écrite en C dans Fortran sous Linux
[Divers/Ada] Ecrire pour un afficheur LCD sur Gnu/Linux et Windows[Ada/Asm] Accéder au port parallèle avec Ada sur Gnu/Linux (résolu)
[C][Gnu] info utilisation memoire, processeur et swap [résolu]Linux C/C++ broadcast UDP sur machine sans gateway
bibliothèque uswitch Unix Tru64 - Linux Red Hat 5Spécial pack-->telecharger des cours complets de: C,C++,JAVA,GTK,Linux
Plus de sujets relatifs à : broadcast UDP sous linux et UDP + connect


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR