Linux: y a-t-il une lecture ou une recv depuis le socket avec timeout?

106

Comment puis-je essayer de lire les données du socket avec timeout? Je sais, select, pselect, poll, a un champ timeout, mais leur utilisation désactive "tcp fast-path" dans la pile tcp reno.

La seule idée que j'ai est d'utiliser recv (fd, ..., MSG_DONTWAIT) dans une boucle

osgx
la source
Il existe également une option d'utilisation de threads :) mais les signaux de thread sont toujours nécessaires
osgx

Réponses:

189

Vous pouvez utiliser la fonction setsockopt pour définir un délai sur les opérations de réception:

SO_RCVTIMEO

Définit la valeur du délai d'expiration qui spécifie la durée maximale pendant laquelle une fonction d'entrée attend jusqu'à ce qu'elle se termine. Il accepte une structure timeval avec le nombre de secondes et de microsecondes spécifiant la limite de la durée d'attente d'une opération d'entrée pour se terminer. Si une opération de réception s'est bloquée pendant autant de temps sans recevoir de données supplémentaires, elle doit retourner avec un décompte partiel ou un numéro d'erreur mis à [EAGAIN] ou [EWOULDBLOCK] si aucune donnée n'est reçue. La valeur par défaut de cette option est zéro, ce qui indique qu'une opération de réception ne doit pas expirer. Cette option prend une structure timeval. Notez que toutes les implémentations ne permettent pas de définir cette option.

// LINUX
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

// WINDOWS
DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);

// MAC OS X (identical to Linux)
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

Apparemment, sur Windows, cela devrait être fait avant d'appeler bind. J'ai vérifié par expérience que cela pouvait être fait avant ou après bindsous Linux et OS X.

Robert S. Barnes
la source
1
Cette réponse m'a sauvé le cul. J'étais coincé dans la mise en œuvre de cette merde "select" alambiquée, sans succès. Cela a fonctionné immédiatement, tellement plus simple.
MiloDC
Maintenant, c'est pourquoi le délai d'expiration sur Windows ne fonctionne pas car j'utilise le code pour Linux. Merci. Si Windows n'utilise pas, struct timeval tv;cela signifie-t-il que select () ne fonctionnera pas aussi? J'ai essayé de porter mon code select () sur Windows et il expire immédiatement, il semble ignorer la valeur que je définis à timeval.
kuchi
1
J'ai mis la valeur du délai d'expiration à 5 secondes. Pourquoi faut-il toujours 5 secondes pour chaque cycle de lecture, qu'il y ait ou non des données entrantes?
Han
cela fonctionne également sur Windows même après l'opération de liaison. essayé sur windows 10
cahit beyaz
1
@ user463035818 Cette réponse prétend que non.
Tomeamis le
22

Voici un code simple pour ajouter un délai à votre recvfonction en utilisant pollen C:

struct pollfd fd;
int ret;

fd.fd = mySocket; // your socket handler 
fd.events = POLLIN;
ret = poll(&fd, 1, 1000); // 1 second for timeout
switch (ret) {
    case -1:
        // Error
        break;
    case 0:
        // Timeout 
        break;
    default:
        recv(mySocket,buf,sizeof(buf), 0); // get your data
        break;
}
Abdessamad Doughri
la source
cela ne fonctionnerait pas exactement comme prévu. pollattendra la réception d'au moins un octet ou timeout, alors que lors de l'appel de la recvfonction, il attendra des sizeof(buf)octets, provoquant un nouveau blocage si ce décompte n'est pas encore arrivé, mais cette fois sans délai.
LoPiTaL
0

// fonctionne également après l'opération de liaison pour WINDOWS

DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
cahit beyaz
la source
-1

Installez un gestionnaire pour SIGALRM, puis utilisez alarm()ou ualarm()avant un blocage normal recv(). Si l'alarme se déclenche, le recv()renverra une erreur avec errnola valeur EINTR.

caf
la source
8
les alarmes (et les signaux) ne sont pas la bonne manière de réaliser cette tâche. Si je veux utiliser le chemin rapide TCP, j'ai besoin d'une latence minimale. Les signaux sont lents.
osgx
2
@osgx Le signal se produit uniquement s'il y a un timeout.
David Schwartz
-4

LINUX

struct timeval tv;
tv.tv_sec = 30;        // 30 Secs Timeout
tv.tv_usec = 0;        // Not init'ing this can cause strange errors
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,sizeof(struct timeval));

LES FENÊTRES

DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));

REMARQUE : vous avez mis ce paramètre avant bind()l'appel de fonction pour une exécution correcte

vivek
la source
4
Cette question a déjà reçu une réponse il y a des années. Quelle nouvelle valeur apporte votre solution?
Maciej Jureczko
Vous avez mis ce paramètre avant l'appel de la fonction bind () pour une exécution correcte, cette partie n'est pas mentionnée dans ans
vivek