Comment paramétrer le délai d'expiration de la socket en C lors de connexions multiples?

j'écris un programme simple qui fait plusieurs connexions à différents serveurs pour vérifier l'état. Toutes ces connexions sont construites sur demande; jusqu'à 10 connexions peuvent être créées simultanément. Je n'aime pas l'idée d'un thread par socket, donc j'ai fait en sorte que toutes ces sockets clients ne bloquent pas, et les ai jetés dans un pool select ().

cela a bien fonctionné, jusqu'à ce que mon client se soit plaint que le temps d'attente est trop long avant qu'ils puissent obtenir le rapport d'erreur quand les serveurs cibles ont cessé de répondre.

j'ai vérifié plusieurs sujets dans le forum. Certains ont suggéré que l'on peut utiliser le signal alarm() ou régler un délai dans l'appel de la fonction select (). Mais j'ai affaire à plusieurs connexions, au lieu d'une. Quand un signal de temporisation pour l'ensemble du processus se produit, je n'ai aucun moyen de distinguer la connexion de temporisation de toutes les autres connexions.

y a-t-il quoi qu'il en soit pour modifier la durée de temporisation par défaut du système ?

59
demandé sur skaffman 2010-11-15 08:55:15

5 réponses

vous pouvez utiliser les options so_rcvtimeo et SO_SNDTIMEO socket pour définir des temps morts pour toutes les opérations de socket, comme ceci:

    struct timeval timeout;      
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;

    if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
                sizeof(timeout)) < 0)
        error("setsockopt failed\n");

    if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
                sizeof(timeout)) < 0)
        error("setsockopt failed\n");

Edit: de la setsockopt l'homme page :

SO_SNDTIMEO est une option pour définir une valeur de timeout pour les opérations de sortie. Il accepte un paramètre struct timeval avec le nombre de secondes et de microsecondes utilisées pour limiter les temps d'attente pour les opérations de sortie à effectuer. Si une opération d'envoi a bloqué pendant autant de temps, elle revient avec un comptage partiel ou avec l'erreur EWOULDBLOCK si aucune donnée n'a été envoyée. Dans la mise en œuvre actuelle, cette minuterie est relancée chaque fois que des données supplémentaires sont fournies au protocole, ce qui implique que la limite s'applique aux portions de sortie de taille allant de la laisse de basse mer à la laisse de haute mer pour la sortie.

SO_RCVTIMEO est une option pour définir une valeur de timeout pour les opérations d'entrée. Il accepte une structure paramètre temporel avec le nombre de secondes et de microsecondes utilisées pour limiter les temps d'attente pour les opérations d'entrée à effectuer. Dans l'implémentation actuelle, cette minuterie est redémarrée chaque fois que des données supplémentaires sont reçues par le protocole, et donc la limite est en fait un minuterie d'inactivité. Si une opération de réception a été bloquée pendant autant de temps sans recevoir de données supplémentaires, elle revient avec un court décompte ou avec L'erreur EWOULDBLOCK si aucune donnée n'a été reçue. Le paramètre struct timeval doit représente un intervalle de temps positif; sinon, setsockopt() retourne avec L'erreur EDOM.

103
répondu Toby 2014-02-12 15:59:09

Je ne suis pas sûr de bien comprendre le problème, mais je suppose qu'il est lié à celui que j'avais, j'utilise Qt avec la communication TCP socket, tous non-bloquants, à la fois Windows et Linux..

voulait obtenir une notification rapide quand un client déjà connecté échouait ou disparaissait complètement, et n'attendait pas les 900 secondes+ par défaut jusqu'à ce que le signal de déconnexion soit levé. L'astuce pour que cela fonctionne était de définir L'option de socket TCP_USER_TIMEOUT de la couche SOL_TCP à la valeur requise, exprimée en millisecondes.

c'est une nouvelle option comparable, pls voir http://tools.ietf.org/html/rfc5482 , mais apparemment il fonctionne très bien, essayé avec WinXP, Win7 / x64 et Kubuntu 12.04 / x64, mon choix de 10 s s'est avéré être un peu plus long, mais beaucoup mieux que tout ce que j'ai essayé avant; -)

le seul problème que j'ai rencontré était de trouver les bonnes inclusions, comme apparemment ce n'est pas ajouté à la douille standard comprend encore..enfin, je les ai moi-même définies comme suit:

#ifdef WIN32
    #include <winsock2.h>
#else
    #include <sys/socket.h>
#endif

#ifndef SOL_TCP
    #define SOL_TCP 6  // socket options TCP level
#endif
#ifndef TCP_USER_TIMEOUT
    #define TCP_USER_TIMEOUT 18  // how long for loss retry before timeout [ms]
#endif

paramétrer cette option de socket ne fonctionne que lorsque le client est déjà connecté, les lignes de code ressemblent à:

int timeout = 10000;  // user timeout in milliseconds [ms]
setsockopt (fd, SOL_TCP, TCP_USER_TIMEOUT, (char*) &timeout, sizeof (timeout));

et la défaillance d'une connexion initiale est captée par une minuterie démarrée lors de l'appel de connect (), car il n'y aura pas de signal de Qt pour cela, le signal de connect ne sera pas relevé, car il n'y aura pas de connexion, et le signal de déconnexion ne sera pas non plus soulevé, car il n'y a pas encore eu de connexion..

12
répondu wgr 2012-10-18 06:29:24

ne pouvez-vous pas mettre en œuvre votre propre système de temporisation?

garder une liste triée, ou mieux encore un tas de priorité comme Heath suggère, des événements de timeout. Dans vos appels select ou poll, utilisez la valeur timeout en haut de la liste timeout. Quand ce délai arrive, faites cette action liée à ce délai.

cette action pourrait fermer une prise qui n'est pas encore connectée.

10
répondu Zan Lynx 2013-11-07 20:44:26

connect timeout doit être manipulé avec une socket non-bloquante (GNU LibC documentation sur connect ). Vous obtenez connect pour revenir immédiatement et puis utiliser select pour attendre avec un délai pour la connexion à compléter.

ceci est également expliqué ici: opération maintenant en cours erreur sur Connexion( fonction) erreur .

int wait_on_sock(int sock, long timeout, int r, int w)
{
    struct timeval tv = {0,0};
    fd_set fdset;
    fd_set *rfds, *wfds;
    int n, so_error;
    unsigned so_len;

    FD_ZERO (&fdset);
    FD_SET  (sock, &fdset);
    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    TRACES ("wait in progress tv={%ld,%ld} ...\n",
            tv.tv_sec, tv.tv_usec);

    if (r) rfds = &fdset; else rfds = NULL;
    if (w) wfds = &fdset; else wfds = NULL;

    TEMP_FAILURE_RETRY (n = select (sock+1, rfds, wfds, NULL, &tv));
    switch (n) {
    case 0:
        ERROR ("wait timed out\n");
        return -errno;
    case -1:
        ERROR_SYS ("error during wait\n");
        return -errno;
    default:
        // select tell us that sock is ready, test it
        so_len = sizeof(so_error);
        so_error = 0;
        getsockopt (sock, SOL_SOCKET, SO_ERROR, &so_error, &so_len);
        if (so_error == 0)
            return 0;
        errno = so_error;
        ERROR_SYS ("wait failed\n");
        return -errno;
    }
}
4
répondu Couannette 2017-05-23 12:34:35

bien sûr, la première réponse est la meilleure. Puis-je ajouter quelque chose?

...

après le 2 setsockopt Vous pouvez contrôler si le client a passé le test de timeout, ou a échoué avec ceci:

après le

n = readline(sockd, recvline, MAXLINE);

vous devez insérer

if (n <= 0){
    if(write(sockd,"ERROR. Timeout di 5sec scaduto, sii piu' veloce\n",MAXLINE)<0)
        err_sys("errore nella write");
    close(sockd);
    sockd = 0;
    break;
}
-5
répondu Blink 2013-05-27 15:31:38