OVH Cloud OVH Cloud

1 connect() = 1 accept() non ?

6 réponses
Avatar
Sylvain Bénot
Bonjour,

J'ai un petit problème avec mes sockets que je ne m'explique pas. J'ai
deux applications qui font un connect() sur une troisième qui fait un
accept() pour "ouvrir" la socket. Mon problème c'est que les deux
accept() me disent qu'ils sont connectés alors que la troisième
application n'as fait qu'un seul accept(). Et en plus la socket est
configuré pour ne recevoir qu'une seule connexion ( listen( ..., 0) ).
Pour moi l'un des deux connect() devrair retourner une erreur puisque la
socket est déjà "ouverte". Précision supplémentaire, les deux
applications font leur connect() quasi simultanément et les sockets sont
non-bloquante. Je ne sais pas si c'est la cause du problème.

J'espère avoir été clair dans mon explication et que quelqu'un aurra une
idée. Ci joint le code de la fonction que fait le connect(), il y a
peut-être une erreur dedans !!!

Merci d'avance

Sylvain

static int connectWithTimeout( s, name, namelen, timeout )
int s; /* socket descriptor */
struct sockaddr *name; /* adress of the socket to connect */
int namelen; /* length of the socket, in bytes */
struct timeval *timeout; /* time-out value */
{
int on;
fd_set writefds;
int retval;
int peernamelen;
struct sockaddr peername;


if ( timeout == NULL )
{
return (connect( s, name, namelen ));
}

retval = -1;

/* put the socket s in non-blocking I/O mode to have the connect () */
/* return without pending */

on = 1;

#ifdef UNIX

ioctl( s, FIONBIO, &on );

#else

ioctlsocket( s, FIONBIO, &on );

#endif

if ( connect( s, name, namelen ) < 0 )
{
#ifdef WIN32
errno = WSAGetLastError ();
#endif

/* when socket is set to non-blocking mode, connect () will return*/
/* with EINPROGRESS or EWOULDBLOCK if a connection cannot */
/* be setup immediatly */
if ( ( errno == EINPROGRESS ) || ( errno == EWOULDBLOCK ) )
{
/* we do synchronous polling on the write side of this socket */
/* using select() with the given <timeout> value until the */
/* connection gets established */

Sleep(10);

FD_ZERO( &writefds );
FD_SET( (unsigned int)s, &writefds );
// MODIF MH 051099
if ( select( 2000, NULL, &writefds, NULL, timeout ) > 0 )
{
/* select() is successful, see if our socket is actually */
/* selected for writing */

if (FD_ISSET (s, &writefds))
{
/* connection attempt has completed. We now see if it */
/* was a successful attempt by trying to get the */
/* remote perr's address. If getpeername () succeeds */
/* we have a connection, otherwise the connect() has */
/* failed */

peernamelen = sizeof (peername);
if (getpeername (s, &peername, &peernamelen) != (-1))
{
retval = 0;
}
}
}
else
{
errno = ETIMEDOUT;
}
}
}
else
{
retval = 0;
}

/* turn OFF the non-blocking I/O */
on = 0;

#ifdef UNIX

ioctl (s, FIONBIO, &on);

#else

ioctlsocket (s, FIONBIO, &on);

#endif

return (retval);
}

6 réponses

Avatar
Cyrille Szymanski
> J'ai deux applications qui font un connect() sur une troisième qui fait un
accept() pour "ouvrir" la socket. Mon problème c'est que les deux accept() me
disent qu'ils sont connectés alors que la troisième application n'as fait
qu'un seul accept().



Je ne comprends pas vraiment. Comment accept() peut dire que les socket client
sont connectées ?

Et en plus la socket est configuré pour ne recevoir qu'une seule connexion (
listen( ..., 0) ).



Ça c'est le "backlog" (la taille de la file d'attente pour les demandes de
connexion *simultanées*)

Ce mélange unix/winsock est pas terrible. Avec winsock les types sont en
majuscules, les paramètres socket ne sont pas des int mais des SOCKET (qui sont
des entiers jusqu'à aujourd'hui).

SOCKET_ERROR est effectivement défini à (-1) mais c'est beaucoup mieux
d'utiliser la macro.


static int connectWithTimeout( s, name, namelen, timeout )
int s; /* socket descriptor */



SOCKET s;

struct sockaddr *name; /* adress of the socket to connect */



SOCKADDR *name;

int namelen; /* length of the socket, in bytes */
struct timeval *timeout; /* time-out value */
{
int on;
fd_set writefds;
int retval;
int peernamelen;
struct sockaddr peername;



SOCKARRD peername; /* note : n'est plus nécessaire */

if ( timeout == NULL ) { return (connect( s, name, namelen )); }

retval = -1;



retval = SOCKET_ERROR;

/* put the socket s in non-blocking I/O mode to have the connect () */
/* return without pending */

on = 1;

ioctlsocket( s, FIONBIO, &on );

if ( connect( s, name, namelen ) < 0 )



== SOCKET_ERROR

{
errno = WSAGetLastError ();

/* when socket is set to non-blocking mode, connect () will return*/
/* with EINPROGRESS or EWOULDBLOCK if a connection cannot */
/* be setup immediatly */



Je ne suis pas certain que EINPROGRESS soit une erreur acceptable dans
ce contexte.

Les erreurs sont WSAE... : WSAEINPROGRESS et WSAEWOULDBLOCK.

if ( ( errno == EINPROGRESS ) || ( errno == EWOULDBLOCK ) )
{
/* we do synchronous polling on the write side of this socket */
/* using select() with the given <timeout> value until the */
/* connection gets established */

Sleep(10);



Ouh, pas beau du tout. Beurk !

Utiliser select() c'est une très vieille méthode. Pourquoi ne pas
opter pour WSAEventSelect() ?

wsaeConnectEvent = WSACreateEvent();
WSAEventSelect( s, wsaeConnectEvent, FD_CONNECT | FD_CLOSE );
WSAWaitForMultipleEvents( 1, &wsaeConnectEvent, TRUE, timeout, FALSE );

Et on vire tout le reste de la fonction. Il ne reste plus qu'à
tester si on est connecté ou pas.

http://tangentsoft.net/wskfaq/

--
Cyrille Szymanski
Avatar
Sylvain Bénot
Cyrille Szymanski a écrit:
J'ai deux applications qui font un connect() sur une troisième qui fait un
accept() pour "ouvrir" la socket. Mon problème c'est que les deux accept() me
disent qu'ils sont connectés alors que la troisième application n'as fait
qu'un seul accept().




Je ne comprends pas vraiment. Comment accept() peut dire que les socket client
sont connectées ?




C'est une coquille, ce sont les connect() qui m'indique que la connexion
a été acepté.

Et en plus la socket est configuré pour ne recevoir qu'une seule connexion (
listen( ..., 0) ).




Ça c'est le "backlog" (la taille de la file d'attente pour les demandes de
connexion *simultanées*)



Ce que je voulais dire c'est que 1) la tache "serveur" n'attend qu'une
connexion et 2) on est d'accord que si la tache "serveur" ne fait qu'un
seul accept() il n'y a qu'un seul connect() qui peut réussir non ?

Ce mélange unix/winsock est pas terrible. Avec winsock les types sont en
majuscules, les paramètres socket ne sont pas des int mais des SOCKET (qui sont
des entiers jusqu'à aujourd'hui).

SOCKET_ERROR est effectivement défini à (-1) mais c'est beaucoup mieux
d'utiliser la macro.



static int connectWithTimeout( s, name, namelen, timeout )
int s; /* socket descriptor */




SOCKET s;


struct sockaddr *name; /* adress of the socket to connect */




SOCKADDR *name;


int namelen; /* length of the socket, in bytes */
struct timeval *timeout; /* time-out value */
{
int on;
fd_set writefds;
int retval;
int peernamelen;
struct sockaddr peername;




SOCKARRD peername; /* note : n'est plus nécessaire */


if ( timeout == NULL ) { return (connect( s, name, namelen )); }

retval = -1;




retval = SOCKET_ERROR;


/* put the socket s in non-blocking I/O mode to have the connect () */
/* return without pending */

on = 1;

ioctlsocket( s, FIONBIO, &on );

if ( connect( s, name, namelen ) < 0 )




== SOCKET_ERROR


{
errno = WSAGetLastError ();

/* when socket is set to non-blocking mode, connect () will return*/
/* with EINPROGRESS or EWOULDBLOCK if a connection cannot */
/* be setup immediatly */




Je ne suis pas certain que EINPROGRESS soit une erreur acceptable dans
ce contexte.

Les erreurs sont WSAE... : WSAEINPROGRESS et WSAEWOULDBLOCK.


if ( ( errno == EINPROGRESS ) || ( errno == EWOULDBLOCK ) )
{
/* we do synchronous polling on the write side of this socket */
/* using select() with the given <timeout> value until the */
/* connection gets established */

Sleep(10);




Ouh, pas beau du tout. Beurk !

Utiliser select() c'est une très vieille méthode. Pourquoi ne pas
opter pour WSAEventSelect() ?

wsaeConnectEvent = WSACreateEvent();
WSAEventSelect( s, wsaeConnectEvent, FD_CONNECT | FD_CLOSE );
WSAWaitForMultipleEvents( 1, &wsaeConnectEvent, TRUE, timeout, FALSE );

Et on vire tout le reste de la fonction. Il ne reste plus qu'à
tester si on est connecté ou pas.

http://tangentsoft.net/wskfaq/




Je suis d'accord avec toi c'est pas terrible mais j'ai pas le choix du
code :(
Avatar
Cyrille Szymanski
On 2005-04-03, Sylvain Bénot wrote:
J'ai deux applications qui font un connect() sur une troisième qui fait un
accept() pour "ouvrir" la socket. Mon problème c'est que les deux accept() me
disent qu'ils sont connectés alors que la troisième application n'as fait
qu'un seul accept().



Je ne comprends pas vraiment. Comment accept() peut dire que les socket client
sont connectées ?



C'est une coquille, ce sont les connect() qui m'indique que la connexion
a été acepté.



Ok, j'aurai du m'en rendre compte tout seul.

Normalement l'un des deux connect() renvoie 0 et l'autre SOCKET_ERROR. Est-ce
bien le cas ? Je pense qu'il y a un problème avec les traitements qui sont
faits quand la socket est non bloquante.

Ce que je voulais dire c'est que 1) la tache "serveur" n'attend qu'une
connexion et 2) on est d'accord que si la tache "serveur" ne fait qu'un
seul accept() il n'y a qu'un seul connect() qui peut réussir non ?



Exactement, même avec un backlog de 20, une seule connexion à la fois sera
acceptée.

Utiliser select() c'est une très vieille méthode. Pourquoi ne pas
opter pour WSAEventSelect() ?

wsaeConnectEvent = WSACreateEvent();
WSAEventSelect( s, wsaeConnectEvent, FD_CONNECT | FD_CLOSE );
WSAWaitForMultipleEvents( 1, &wsaeConnectEvent, TRUE, timeout, FALSE );

Et on vire tout le reste de la fonction. Il ne reste plus qu'à
tester si on est connecté ou pas.

http://tangentsoft.net/wskfaq/




Je suis d'accord avec toi c'est pas terrible mais j'ai pas le choix du
code :(



Dans ce cas, garde le code BSD Socket dans un #ifdef et mets la version
Winsock avec les WSAEventSelect() dans le #else. La fonction ioctl() tu
la mets en macro aussi et ça rendra le code plus clair.

Cette histoire de tester le hostent est une bidouille et elle ne doit pas
fonctionner de la même façon avec Winsock.

--
Cyrille Szymanski
Avatar
Sylvain Bénot
Cyrille Szymanski a écrit:
On 2005-04-03, Sylvain Bénot wrote:

J'ai deux applications qui font un connect() sur une troisième qui fait un
accept() pour "ouvrir" la socket. Mon problème c'est que les deux accept() me
disent qu'ils sont connectés alors que la troisième application n'as fait
qu'un seul accept().



Je ne comprends pas vraiment. Comment accept() peut dire que les socket client
sont connectées ?




C'est une coquille, ce sont les connect() qui m'indique que la connexion
a été acepté.




Ok, j'aurai du m'en rendre compte tout seul.

Normalement l'un des deux connect() renvoie 0 et l'autre SOCKET_ERROR. Est-ce
bien le cas ? Je pense qu'il y a un problème avec les traitements qui sont
faits quand la socket est non bloquante.



Pour ce que me souvient des tests que j'ai fait vendredi, les deux
connect() commencent par me retourner SOCKET_ERROR avec EWOULDBLOCK
comme code d'erreur et qu'ensuite avec le test du select() ça passe.


Ce que je voulais dire c'est que 1) la tache "serveur" n'attend qu'une
connexion et 2) on est d'accord que si la tache "serveur" ne fait qu'un
seul accept() il n'y a qu'un seul connect() qui peut réussir non ?




Exactement, même avec un backlog de 20, une seule connexion à la fois sera
acceptée.


Utiliser select() c'est une très vieille méthode. Pourquoi ne pas
opter pour WSAEventSelect() ?

wsaeConnectEvent = WSACreateEvent();
WSAEventSelect( s, wsaeConnectEvent, FD_CONNECT | FD_CLOSE );
WSAWaitForMultipleEvents( 1, &wsaeConnectEvent, TRUE, timeout, FALSE );

Et on vire tout le reste de la fonction. Il ne reste plus qu'à
tester si on est connecté ou pas.

http://tangentsoft.net/wskfaq/




Je suis d'accord avec toi c'est pas terrible mais j'ai pas le choix du
code :(




Dans ce cas, garde le code BSD Socket dans un #ifdef et mets la version
Winsock avec les WSAEventSelect() dans le #else. La fonction ioctl() tu
la mets en macro aussi et ça rendra le code plus clair.

Cette histoire de tester le hostent est une bidouille et elle ne doit pas
fonctionner de la même façon avec Winsock.



Je vais essayer la méthode WSA... pour voir s'il ça fonctionne mieux.
Comment tu ferais pour tester ensuite si la socket est connecté ?
Apparement l'histoire du getpeername() n'as pas l'air super !!

Merci

Sylvain
Avatar
Cyrille Szymanski
> Je vais essayer la méthode WSA... pour voir s'il ça fonctionne mieux.
Comment tu ferais pour tester ensuite si la socket est connecté ?
Apparement l'histoire du getpeername() n'as pas l'air super !!



Oui, c'est une bidouille.

Lis la doc de WSAAsyncEventSelect(), tu y trouveras notamment la phrase "An
FD_WRITE network event is recorded when a socket is first connected with
connect/WSAConnect or accepted with accept/WSAAccept" et d'autres infos qui te
permettront de répondre aux questions que tu te poses.

A+
--
Cyrille Szymanski
Avatar
Sylvain Bénot
Cyrille Szymanski a écrit:
Je vais essayer la méthode WSA... pour voir s'il ça fonctionne mieux.
Comment tu ferais pour tester ensuite si la socket est connecté ?
Apparement l'histoire du getpeername() n'as pas l'air super !!




Oui, c'est une bidouille.

Lis la doc de WSAAsyncEventSelect(), tu y trouveras notamment la phrase "An
FD_WRITE network event is recorded when a socket is first connected with
connect/WSAConnect or accepted with accept/WSAAccept" et d'autres infos qui te
permettront de répondre aux questions que tu te poses.

A+



J'ai écrit le code ci-dessous est ça fait toujours la même chose. Les
deux connect() m'indique qu'il sont connecté à la même adresse même port
alors que la tache "serveur" indique quelle n'as accepté qu'une seule
connexion. J'ai refait le test en virant tout et en laissant juste
l'appel à la fonction connect(), sans mettre les socket en non-bloquant
et résultat identique. Je commence à me demander si le problème ne vient
pas d'ailleur. Mais je ne vois pas où !!!


static int connectWithTimeout( s, name, namelen, timeout )
int s; /* socket descriptor */
struct sockaddr *name; /* adress of the socket to connect */
int namelen; /* length of the socket, in bytes */
struct timeval *timeout; /* time-out value */
{
int on;
int retval;
WSAEVENT evtConnect;
WSANETWORKEVENTS toto;
DWORD result;
int err;

retval = -1;

evtConnect = WSACreateEvent();
WSAEventSelect( s, evtConnect, FD_CONNECT );
if ( connect( s, name, namelen ) == SOCKET_ERROR )
{
err = WSAGetLastError();
if ( err == WSAEWOULDBLOCK )
{
printf( "Attente de connexion.....n" );
result = WSAWaitForMultipleEvents( 1, &evtConnect, TRUE, 1000,
FALSE );
if ( result != WSA_WAIT_FAILED )
{
printf( "EVENT CONNECT OKn" );
if ( result == WSA_WAIT_TIMEOUT )
{
printf( "TIMEOUTn" );
}
else
{
WSAEnumNetworkEvents( s, evtConnect, &toto );
if ( toto.lNetworkEvents & FD_CONNECT )
{
printf( "EVENT CONNECTn" );
if ( toto.iErrorCode[FD_CONNECT_BIT] == 0 )
{
WSAEventSelect( s, evtConnect, FD_WRITE);
result = WSAWaitForMultipleEvents( 1, &evtConnect, TRUE,
1000, FALSE );
if ( result != WSA_WAIT_FAILED )
{
printf( "EVENT WRITE OKn" );
if ( result == WSA_WAIT_TIMEOUT )
{
printf( "TIMEOUT WRITEn" );
}
else
{
WSAEnumNetworkEvents( s, evtConnect, &toto );
if ( toto.lNetworkEvents & FD_WRITE )
{
if ( toto.iErrorCode[FD_WRITE_BIT] == 0 )
{
retval = 0;
}
else
{
printf( "ERREUR WRITEn" );
}
}
}
}
else
{
printf( "ERREUR WSA.... WRITEn" );
}
}
else
{
if ( toto.iErrorCode[FD_CONNECT_BIT] == WSAETIMEDOUT )
{
printf( "iErrorCode TIME OUT= %dn",
toto.iErrorCode[FD_CONNECT_BIT] );
}
else
{
printf( "iErrorCode = %dn",
toto.iErrorCode[FD_CONNECT_BIT] );
}
}
}
}
}
else
{
printf( "Erreur de WSAWaitForMultipleEvents = %sn",
WSAGetLastError() );
}
}
else
{
printf( "Erreur connect no %dn", err );
}
}
else
{
printf( "Connexion direct !!!n" );
retval = 0;
}
WSACloseEvent( evtConnect );
/* turn OFF the non-blocking I/O */

on = 0;
ioctlsocket (s, FIONBIO, &on);


return (retval);


}