1 connect() = 1 accept() non ?
Le
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);
}
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);
}

Poser une question


Je ne comprends pas vraiment. Comment accept() peut dire que les socket client
sont connectées ?
Ç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.
SOCKET s;
SOCKADDR *name;
SOCKARRD peername; /* note : n'est plus nécessaire */
retval = SOCKET_ERROR;
== SOCKET_ERROR
Je ne suis pas certain que EINPROGRESS soit une erreur acceptable dans
ce contexte.
Les erreurs sont WSAE... : WSAEINPROGRESS et WSAEWOULDBLOCK.
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
C'est une coquille, ce sont les connect() qui m'indique que la connexion
a été acepté.
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 ?
Je suis d'accord avec toi c'est pas terrible mais j'ai pas le choix du
code :(
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.
Exactement, même avec un backlog de 20, une seule connexion à la fois sera
acceptée.
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
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.
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
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