IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

UDP + connect sous linux


Sujet :

C

  1. #1
    Membre du Club
    Inscrit en
    Octobre 2004
    Messages
    59
    Détails du profil
    Informations forums :
    Inscription : Octobre 2004
    Messages : 59
    Points : 49
    Points
    49
    Par défaut UDP + connect sous linux
    Bonjour

    Je rencontre des difficultés pour valider une encapsulation des socket , notamment au niveau de la communication UDP : l'UDP unicast ( avec filtrage par la fonction connect ) 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 : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
     
     
    int oui = 1;
     
     
    	m_socket = socket( AF_INET , SOCK_DGRAM , IPPROTO_UDP );
     
    	if ( m_socket >= 0)
    	{
     
    		// options de socket devant être paramétrées *avant* le bind() ou connect()
     
     
    #ifdef SO_NOSIGPIPE
    		if (setsockopt( m_socket , SOL_SOCKET, SO_NOSIGPIPE, (char *)&oui, sizeof(oui)) < 0)
    		{
    			ERROR(("setsockopt(SO_NOSIGPIPE): %s", strerror_socket(errno)));
    			CloseSocket( );
    			return PRT_FAILED;			
    		}
    #endif
     
    		if (setsockopt( m_socket , SOL_SOCKET, SO_REUSEADDR, (char *)&oui, sizeof(oui)) < 0)
    		{
    			ERROR((setsockopt(SO_REUSEADDR): %s", strerror_socket(errno)));
    			CloseSocket( );
    			return PRT_FAILED;			
    		}
     
    #ifdef SO_REUSEPORT
    		if (setsockopt( m_socket , SOL_SOCKET, SO_REUSEPORT, (char *)&oui, sizeof(oui)) < 0)
    		{
    			ERROR(("setsockopt(SO_REUSEPORT): %s", strerror_socket(errno)));
    			CloseSocket( );
    			return PRT_FAILED;			
    		}
    #endif	
     
     
     
    		ULONG hote_l =INADDR_NONE;
     
     
    		hote_l = inet_addr( m_address_sz );
     
     
    		ULONG peer_l;
     
    		peer_l = inet_addr( m_pair_address_sz );
     
     
    		if ( hote_l == INADDR_NONE )
    		{
    			ERROR(("%s is not a valid IP address", m_address_sz ) );
    			CloseSocket( );
    			return PRT_FAILED;	
    		}
     
    		struct sockaddr_in adresse;
     
    		memset ( &adresse , 0 , sizeof( adresse ) );
     
    		adresse.sin_family		= AF_INET;
     
    		adresse.sin_addr.s_addr = hote_l;
    		adresse.sin_port		= htons ( (USHORT)m_port_l ); // port stocké dans un long, mais < 65535 donc cast OK
     
     
    		struct sockaddr_in adresse_peer;
     
    		memset ( &adresse_peer , 0 , sizeof( adresse_peer ) );
     
    		adresse_peer.sin_family		= AF_INET;
    		adresse_peer.sin_addr.s_addr = peer_l;
    		adresse_peer.sin_port		= htons( (USHORT)m_port_l); // port stocké dans un long, mais < 65535 donc cast OK
     
    		// boucler les tentatives ?
     
     
     
    		if ( bind( m_socket , (struct sockaddr *) &adresse , sizeof (adresse ) ) != 0 )
    		{
    			ERROR(("bind failed %s", strerror_socket(errno) ) );
    			CloseSocket( );
    			return PRT_FAILED;	
    		}
     
    		if ( connect( m_socket , (struct sockaddr *) &adresse_peer , sizeof (adresse_peer ) ) != 0 )
    		{
    			ERROR(("connect failed %s", strerror_socket(errno) ) );
    			CloseSocket( );
    			return PRT_FAILED;	
    		}
     
    		// options de socket devant être paramétrées *apres* le bind() ou connect()
     
     
    		struct timeval timeout;
    		timeout.tv_sec  = 0;
    		timeout.tv_usec = 100000;
     
    		if (setsockopt( m_socket , SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0)
    		{
    			PGAS_ERROR(("setsockopt(SO_RCVTIMEO): %s", strerror_socket(errno)));
    			CloseSocket( );
    			return PRT_FAILED;			
    		}
     
     
    		// connection is OK
     
    		m_connected_b = TRUE;
    		return PRT_SUCCESS;	
    	}
     
    	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 : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
     
     
    while (tot < nBufferSize)
    {
    	int envoye = 0;
     
    	envoye = send( m_socket, retour + tot, nBufferSize - tot, 0 );
     
    	if ( envoye < 0)
    	{
    		ERROR(("erreur d'envoi a %s/%d: %s", m_address_sz, m_port_l, strerror_socket(errno)));						
    		return PRT_FAILED;
    	}
    	else
    	{
    		tot += envoye;
    	}
    }

    code du receive :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
     
    fd_set      fdin;
    timeval     tv;
     
     
    FD_ZERO(&fdin);
    FD_SET(m_socket, &fdin);
    tv.tv_sec = _timeout / 1000 ;
    tv.tv_usec = ( _timeout % 1000 ) * 1000 ; 
     
    data = select(((int)m_socket)+1, &fdin, NULL, NULL, &tv);
     
    if (data == -1)
    {
    	ERROR(("Erreur select : %s",strerror_socket(errno)));
    	return PRT_FAILED;
    }
    else if (FD_ISSET(m_socket, &fdin) != 0)
    {	
     
    	recu = recv(m_socket, entete, BUFFERSIZE, 0 );
    }

    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

  2. #2
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2011
    Messages
    1 255
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 255
    Points : 2 627
    Points
    2 627
    Par défaut
    Salut,

    Il y a quelque chose que m'a fait tiquer dans le titre : l'association UDP et connect me choque.
    un mini cours.
    un super cours.

  3. #3
    Membre du Club
    Inscrit en
    Octobre 2004
    Messages
    59
    Détails du profil
    Informations forums :
    Inscription : Octobre 2004
    Messages : 59
    Points : 49
    Points
    49
    Par défaut
    Citation Envoyé par mala92 Voir le message
    Salut,

    Il y a quelque chose que m'a fait tiquer dans le titre : l'association UDP et connect me choque.
    un mini cours.
    un super cours.
    man connect : http://www.linux-kheops.com/doc/man/...connect.2.html

    Si la socket est du type SOCK_DGRAM, cette fonction indique le correspondant avec lequel la socket doit communiquer, c'est l'adresse à laquelle les datagrammes seront envoyés, et la seule adresse depuis laquelle les datagrammes seront reçus.

    C'est exactement ce que je souhaite faire.

  4. #4
    Membre émérite
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Décembre 2011
    Messages
    1 255
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Décembre 2011
    Messages : 1 255
    Points : 2 627
    Points
    2 627
    Par défaut
    Je n'ai pas l'habitude de ce genre de fonctionnement, bref !!

    Peut-être encore une erreur de ma part, mais quand on fait 'connect', il ne doit pas y avoir 'accept' de l'autre côté ?

    C'est inquiétant que les 2 OS n'aient pas le même comportement !!!

  5. #5
    Membre du Club
    Inscrit en
    Octobre 2004
    Messages
    59
    Détails du profil
    Informations forums :
    Inscription : Octobre 2004
    Messages : 59
    Points : 49
    Points
    49
    Par défaut
    Quand on fait un connect, il faut qu'il y ait un accept de l'autre coté, si les sockets sont crées en mode SOCK_STREAM.

    Ce qui n'est pas le cas ici. Le comportement est plutôt une sorte de filtre sur l'adresse distante d'une part, et permet d'utiliser send et recv plutot que sendto et recvfrom d'autre part.

    Par ailleurs, l'appel à connect peut être fait plusieurs fois sur une socket en mode SOCK_DGRAM, ce qui permet de changer le filtre ( voire de désactiver l'utilisation de la socket en passant NULL ).

    Pour ce qui est du comportement différent sur différentes plateformes, c'est ce qui fait partie des aléas d'un portage. Pour info, je porte également ce genre de code sous iOS et android, et les contraintes sont parfois autrement plus complexes ( pas dans le cas de la pile IP heureusement ).

    Un autre comportement différent entre windows et linux, c'est pour ecouter sur des sockets UDP en mode broadcast : sous windows on écoute l'adresse de l'interface alors que sous linux , il faut l'adresse de broadcast sinon cela ne fonctionne pas ( et inversement ).

Discussions similaires

  1. [FEDORA] se connecter à internet sous linux / sources
    Par psgman113 dans le forum RedHat / CentOS / Fedora
    Réponses: 6
    Dernier message: 07/01/2007, 09h03
  2. Eteindre PC winXP Win2000 sous DOS, connection win/linux
    Par djibril dans le forum Administration système
    Réponses: 7
    Dernier message: 03/05/2006, 18h19
  3. [Oracle] Connecter php5.1.2 à oracle10g release 2 sous Linux
    Par jeromek dans le forum PHP & Base de données
    Réponses: 1
    Dernier message: 24/04/2006, 06h54
  4. Connection distante sous LINUX
    Par JamesP dans le forum Général Java
    Réponses: 9
    Dernier message: 28/02/2006, 13h13
  5. Réponses: 1
    Dernier message: 24/08/2004, 18h10

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo