web-dev-qa-db-fra.com

Définition du délai d'expiration pour la fonction recv

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?

12
vico

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...
    }
}
9
Nemanja Boric

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))
        //...
}
24
Remy Lebeau

utilisez la macro FD_ISSET () pour tester s'il y a des données à lire. S'il renvoie faux, ne faites pas la lecture.

http://linux.die.net/man/3/fd_set

1
Richard Hodges