2010-04-08 5 views
26

J'ai un programme simple pour vérifier si un port est ouvert, mais je veux raccourcir la longueur du délai d'attente sur la connexion socket, car la valeur par défaut est beaucoup trop longue. Je ne suis pas sûr de savoir comment faire cela. Voici le code:C: délai de connexion socket

#include <sys/socket.h> 
#include <sys/time.h> 
#include <sys/types.h> 
#include <arpa/inet.h> 
#include <netinet/in.h> 
#include <errno.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <netdb.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 

int main(int argc, char **argv) { 
    u_short port;    /* user specified port number */ 
    char addr[1023];    /* will be a copy of the address entered by u */ 
    struct sockaddr_in address; /* the libc network address data structure */ 
    short int sock = -1;   /* file descriptor for the network socket */ 

    if (argc != 3) { 
     fprintf(stderr, "Usage %s <port_num> <address>", argv[0]); 
     return EXIT_FAILURE; 
    } 

    address.sin_addr.s_addr = inet_addr(argv[2]); /* assign the address */ 
    address.sin_port = htons(atoi(argv[2]));   /* translate int2port num */ 

    sock = socket(AF_INET, SOCK_STREAM, 0); 
    if (connect(sock,(struct sockaddr *)&address,sizeof(address)) == 0) { 
     printf("%i is open\n", port); 
    } 
    close(sock); 
    return 0; 
} 
+0

Sur quelle plateforme êtes-vous? – Duck

+0

J'utilise linux –

+0

Vous avez ajouté dans votre réponse "fcntl (chaussette, F_SETFL, O_NONBLOCK)" Notez que, après cela, le prochain socket lu devient également non bloquant! –

Répondre

27

Cet article peut vous aider: http://developerweb.net/viewtopic.php?id=3196. On dirait que vous mettez le socket en mode non-bloquant jusqu'à ce que vous vous êtes connecté, et ensuite le remettre en mode de blocage une fois la connexion établie.

+0

Nice. C'est une meilleure suggestion que la mienne. – asveikau

+1

Ceci est une très bonne réponse, pourquoi l'avez-vous fait wiki communautaire? Vous devriez gagner une certaine réputation pour suggérer la ressource. –

+2

Le forum lié à semble avoir changé leur logiciel, donc le lien est mort maintenant. – Jorenko

46

Définissez le socket non bloquant et utilisez select() (qui prend un paramètre de délai d'attente). Si une socket non bloquante essaie de se connecter, alors select() indiquera que le socket est accessible en écriture lorsque le connect() se termine (avec ou sans succès). Vous utilisez ensuite getsockopt() pour déterminer le résultat de la connect():

int main(int argc, char **argv) { 
    u_short port;    /* user specified port number */ 
    char *addr;     /* will be a pointer to the address */ 
    struct sockaddr_in address; /* the libc network address data structure */ 
    short int sock = -1;   /* file descriptor for the network socket */ 
    fd_set fdset; 
    struct timeval tv; 

    if (argc != 3) { 
     fprintf(stderr, "Usage %s <port_num> <address>\n", argv[0]); 
     return EXIT_FAILURE; 
    } 

    port = atoi(argv[1]); 
    addr = argv[2]; 

    address.sin_family = AF_INET; 
    address.sin_addr.s_addr = inet_addr(addr); /* assign the address */ 
    address.sin_port = htons(port);   /* translate int2port num */ 

    sock = socket(AF_INET, SOCK_STREAM, 0); 
    fcntl(sock, F_SETFL, O_NONBLOCK); 

    connect(sock, (struct sockaddr *)&address, sizeof(address)); 

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

    if (select(sock + 1, NULL, &fdset, NULL, &tv) == 1) 
    { 
     int so_error; 
     socklen_t len = sizeof so_error; 

     getsockopt(sock, SOL_SOCKET, SO_ERROR, &so_error, &len); 

     if (so_error == 0) { 
      printf("%s:%d is open\n", addr, port); 
     } 
    } 

    close(sock); 
    return 0; 
} 
+6

Cela fonctionne dans * nix, mais ne fonctionne pas sous Windows. Dans Windows, vous déterminez si le socket est connecté en regardant la valeur de retour de "select" dans votre code ci-dessus. Dans Windows, sélectionnez renvoie 1 si la connexion est terminée ou 0 si la connexion a échoué. Si vous regardez so_error, Windows renvoie toujours 0, même lorsque la connexion a échoué. Ce sont des fenêtres pour vous, comme on dit. – deltamind106

+0

Que faire si je ne veux pas utiliser le mode non bloquant pour d'autres opérations de socket (comme lire, écrire)? Puis-je effacer le drapeau 'O_NONBLOCK' après la connexion du socket? Si c'est possible, est-ce sécuritaire? –

+1

@anton_rh: Oui, il est sûr d'effacer 'O_NONBLOCK' et de remettre le socket en mode bloquant. – caf

3

Celui-ci a paramétrées ip, port, délai d'attente en secondes, gérer les erreurs de connexion et vous donner le temps de connexion en millisecondes:

#include <sys/socket.h> 
#include <sys/time.h> 
#include <sys/types.h> 
#include <arpa/inet.h> 
#include <netinet/in.h> 
#include <errno.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <netdb.h> 
#include <stdlib.h> 
#include <string.h> 
#include <unistd.h> 
#include <time.h> 

int main(int argc, char **argv) { 
    struct sockaddr_in addr_s; 
    char *addr; 
    short int fd=-1; 
    int port; 
    fd_set fdset; 
    struct timeval tv; 
    int rc; 
    int so_error; 
    socklen_t len; 
    struct timespec tstart={0,0}, tend={0,0}; 
    int seconds; 

    if (argc != 4) { 
     fprintf(stderr, "Usage: %s <ip> <port> <timeout_seconds>\n", argv[0]); 
     return 1; 
    } 

    addr = argv[1]; 
    port = atoi(argv[2]); 
    seconds = atoi(argv[3]); 

    addr_s.sin_family = AF_INET; // utilizzo IPv4 
    addr_s.sin_addr.s_addr = inet_addr(addr); 
    addr_s.sin_port = htons(port); 

    clock_gettime(CLOCK_MONOTONIC, &tstart); 

    fd = socket(AF_INET, SOCK_STREAM, 0); 
    fcntl(fd, F_SETFL, O_NONBLOCK); // setup non blocking socket 

    // make the connection 
    rc = connect(fd, (struct sockaddr *)&addr_s, sizeof(addr_s)); 
    if ((rc == -1) && (errno != EINPROGRESS)) { 
     fprintf(stderr, "Error: %s\n", strerror(errno)); 
     close(fd); 
     return 1; 
    } 
    if (rc == 0) { 
     // connection has succeeded immediately 
     clock_gettime(CLOCK_MONOTONIC, &tend); 
     printf("socket %s:%d connected. It took %.5f seconds\n", 
      addr, port, (((double)tend.tv_sec + 1.0e-9*tend.tv_nsec) - ((double)tstart.tv_sec + 1.0e-9*tstart.tv_nsec))); 

     close(fd); 
     return 0; 
    } /*else { 
     // connection attempt is in progress 
    } */ 

    FD_ZERO(&fdset); 
    FD_SET(fd, &fdset); 
    tv.tv_sec = seconds; 
    tv.tv_usec = 0; 

    rc = select(fd + 1, NULL, &fdset, NULL, &tv); 
    switch(rc) { 
    case 1: // data to read 
     len = sizeof(so_error); 

     getsockopt(fd, SOL_SOCKET, SO_ERROR, &so_error, &len); 

     if (so_error == 0) { 
      clock_gettime(CLOCK_MONOTONIC, &tend); 
      printf("socket %s:%d connected. It took %.5f seconds\n", 
       addr, port, (((double)tend.tv_sec + 1.0e-9*tend.tv_nsec) - ((double)tstart.tv_sec + 1.0e-9*tstart.tv_nsec))); 
      close(fd); 
      return 0; 
     } else { // error 
      printf("socket %s:%d NOT connected: %s\n", addr, port, strerror(so_error)); 
     } 
     break; 
    case 0: //timeout 
     fprintf(stderr, "connection timeout trying to connect to %s:%d\n", addr, port); 
     break; 
    } 

    close(fd); 
    return 0; 
} 
4

Le les réponses à propos de l'utilisation de select()/poll() ont raison et le code devrait être écrit de cette façon pour être portable.

Cependant, puisque vous êtes sous Linux, vous pouvez le faire:

int synRetries = 2; // Send a total of 3 SYN packets => Timeout ~7s 
setsockopt(fd, IPPROTO_TCP, TCP_SYNCNT, &synRetries, sizeof(synRetries)); 

Voir man 7 tcp et man setsockopt.

Je l'ai utilisé pour accélérer le délai de connexion dans un programme que je devais patcher rapidement. Le piratage à timeout via select()/poll() n'était pas une option.