I. Mode non bloquant, select ou poll▲
Afin d'utiliser un socket non bloquant, nous pouvons avoir recours au mode non bloquant, se servir de select ou de poll, qui ont une utilisation et mise en place identiques à celles que l'on a vues dans l'article précédent dans le cadre d'un serveur.
Quelle que soit la solution choisie, un avantage direct est de ne plus avoir besoin de threads pour traiter le réseau, éliminant ainsi le besoin de synchronisation, l'utilisation de mutex et tous les problèmes habituellement rencontrés dès lors que l'on parle de multithread. De plus vous aurez un meilleur contrôle des actions en cours, en pouvant interrompre à tout moment, par exemple une connexion - chose impossible auparavant.
II. connect▲
Pour un client TCP, connect pose un problème avec le mode non bloquant : bien que connect retourne immédiatement, comment alors savoir quand la connexion est réellement établie ? Ou a échoué ?
Dans ce cas, il faut coupler le mode non bloquant, qui est nécessaire afin que connect ne bloque pas, à l'utilisation de select ou de poll. Quand le socket est prêt en écriture, c'est que le processus de connexion est terminé. Il faut alors récupérer l'état du socket pour vérifier s'il y a une erreur, ou si tout s'est bien passé et la connexion est établie.
Comme pour les appels à recv et send non bloquant, connect retournera une erreur, il faudra alors vérifier de quelle erreur il s'agit : s'il s'agit de EINPROGRESS, c'est juste l'erreur liée au mode non bloquant, l'équivalent de l'erreur EWOULDBLOCK pour recv et send. Sinon il s'agit d'une vraie erreur survenue. Windows aime se faire remarquer et retournera EWOULDBLOCK suite à une erreur d'appel à connect non bloquant. Faites donc attention à ce cas particulier et si votre code est à vocation d'être porté, il faudra sans doute vérifier les deux valeurs.
sockaddr_in server;
inet_pton(AF_INET, "127.0.0.1"
, &
server.sin_addr.s_addr);
server.sin_family =
AF_INET;
server.sin_port =
htons(9999
);
SOCKET client =
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Sockets::
SetNonBlocking(client);
if
(connect(client, reinterpret_cast
<
const
sockaddr*>
(&
server), sizeof
(server)) !=
0
)
{
int
error =
Sockets::
GetError();
if
(error !=
static_cast
<
int
>
(Sockets::Errors::
INPROGRESS) &&
error !=
static_cast
<
int
>
(Sockets::Errors::
WOULDBLOCK))
{
std::
cout <<
"Erreur connect : "
<<
error <<
std::
endl;
return
-
2
;
}
}
bool
connected =
false
;
pollfd fd =
{
0
}
;
fd.fd =
client;
fd.events =
POLLOUT;
while
(true
)
{
int
ret =
poll(&
fd, 1
, 0
);
if
(ret ==
-
1
)
{
std::
cout <<
"Erreur poll : "
<<
Sockets::
GetError() <<
std::
endl;
break
;
}
else
if
(ret >
0
)
{
if
(fd.revents &
POLLOUT)
{
connected =
true
;
std::
cout <<
"Socket connecte"
<<
std::
endl;
break
;
}
else
if
(fd.revents &
(POLLHUP |
POLLNVAL))
{
std::
cout <<
"Socket deconnecte"
<<
std::
endl;
break
;
}
else
if
(fd.revents &
POLLERR)
{
socklen_t err;
int
errsize =
sizeof
(err);
if
(getsockopt(client, SOL_SOCKET, SO_ERROR, reinterpret_cast
<
char
*>
(&
err), &
errsize) !=
0
)
{
std::
cout <<
"Impossible de determiner l'erreur : "
<<
Sockets::
GetError() <<
std::
endl;
}
else
if
(err !=
0
)
std::
cout <<
"Erreur : "
<<
err <<
std::
endl;
break
;
}
}
}
if
(connected)
{
//!
< connexion établie avec succès
}
Si vous êtes familier avec le chapitre 1 et le chapitre 5, ce code ne devrait poser aucun souci de compréhension. Il s'agit de l'application directe de l'explication qui précède, avec réutilisation des codes des parties précédentes. poll peut être remplacé par select selon votre préférence. L'utilisation d'une boucle infinie pour attendre que la connexion soit établie n'est pas représentative d'un exemple réel, mais a juste pour but de montrer comment la logique est réalisée. Dans un code réel, il s'agira d'appeler ce code régulièrement, typiquement une fois par itération de la boucle principale du logiciel ou de la boucle de gameplay, afin de vérifier la progression de la connexion.
Article précédent | Article suivant |
---|---|
<< TCP - Envoi et réception depuis le serveur | TCP - Quelle architecture de client visée ? >> |