Je lis depuis le socket en utilisant la fonction recv
. J'ai un problème lorsqu'aucune donnée n'est disponible pour la lecture. Mon programme s'arrête. J'ai découvert que je peux définir un délai d'expiration à l'aide de la fonction select
. Mais il semble que le délai d'attente affecte la fonction de sélection elle-même et que recv
qui va après la sélection attend toujours sans interruption.
fd_set set;
struct timeval timeout;
FD_ZERO(&set); /* clear the set */
FD_SET(s, &set); /* add our file descriptor to the set */
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC;
timeout.tv_usec = 0;
int rv = select(s, &set, NULL, NULL, &timeout);
if((recv_size = recv(s , rx_tmp , bufSize ,0)) == SOCKET_ERROR)
{
...
}
Comment demander à la fonction recv
de revenir après un certain délai?
Vous devez vérifier la valeur de retour de select
. select
renverra 0
en cas d'expiration du délai, vous devez donc vérifier l'erreur et appeler recv
uniquement si select
a renvoyé une valeur positive:
En cas de succès, select () et pselect () renvoient le nombre de descripteurs de fichier contenus dans les trois ensembles de descripteurs renvoyés (c'est-à-dire le nombre total de bits définis dans readfds, writefds, exceptfds) qui peut être nul si le délai expire avant qu'il ne se passe quelque chose d'intéressant.
int rv = select(s + 1, &set, NULL, NULL, &timeout);
if (rv == SOCKET_ERROR)
{
// select error...
}
else if (rv == 0)
{
// timeout, socket does not have anything to read
}
else
{
// socket has something to read
recv_size = recv(s, rx_tmp, bufSize, 0);
if (recv_size == SOCKET_ERROR)
{
// read failed...
}
else if (recv_size == 0)
{
// peer disconnected...
}
else
{
// read successful...
}
}
Une autre façon de définir un délai d'expiration sur recv()
elle-même sans utiliser select()
consiste à utiliser setsockopt()
pour définir la prise SO_RCVTIMEO
option (sur les plates-formes qui le prennent en charge).
Sous Windows, le code ressemblerait à ceci:
DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
//...
recv_size = recv(s, rx_tmp, bufSize, 0);
if (recv_size == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAETIMEDOUT)
//...
}
Sur d'autres plates-formes, le code ressemblerait plutôt à ceci:
struct timeval timeout;
timeout.tv_sec = SOCKET_READ_TIMEOUT_SEC;
timeout.tv_usec = 0;
setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
//...
recv_size = recv(s, rx_tmp, bufSize, 0);
if (recv_size == -1)
{
if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
//...
}
utilisez la macro FD_ISSET () pour tester s'il y a des données à lire. S'il renvoie faux, ne faites pas la lecture.