2010-04-08 18 views
26

Tengo un programa simple para verificar si un puerto está abierto, pero quiero acortar la longitud de tiempo de espera en la conexión de socket porque el valor predeterminado es demasiado largo. Aunque no estoy seguro de cómo hacerlo. Aquí está el código:C: tiempo de espera de conexión de 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

¿En qué plataforma estás? – Duck

+0

Estoy usando linux –

+0

Agregaste en tu respuesta "fcntl (calcetín, F_SETFL, O_NONBLOCK)" ¡Ten en cuenta que después de esto, la próxima lectura del zócalo también se convierte en no bloqueante! –

Respuesta

27

Este artículo podría ayudar: http://developerweb.net/viewtopic.php?id=3196. Parece que pones el zócalo en modo sin bloqueo hasta que te hayas conectado, y luego lo vuelves a poner en modo de bloqueo una vez que se establece la conexión.

+0

Agradable. Esta es una mejor sugerencia que la mía. – asveikau

+1

Esta es una muy buena respuesta, ¿por qué la has convertido en wiki de la comunidad? Deberías ganarte un poco de reputación por sugerir el recurso. –

+2

El foro vinculado parece haber cambiado su software, por lo que el enlace está muerto ahora. – Jorenko

46

Establezca el socket sin bloqueo y use select() (que toma un parámetro de tiempo de espera). Si un socket sin bloqueo está intentando conectarse, entonces select() indicará que el socket es grabable cuando el connect() finaliza (ya sea con éxito o sin éxito). A continuación, utiliza getsockopt() para determinar el resultado 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

Esto funciona en * nix, pero no funciona en Windows. En Windows, usted determina si el socket está conectado mirando el valor de retorno de "seleccionar" en su código anterior. En Windows, seleccione devuelve 1 si la conexión se completó, o devuelve 0 si la conexión no fue exitosa. Si mira so_error, Windows siempre devuelve 0, incluso cuando la conexión falló. Eso es ventanas para ti, como dicen. – deltamind106

+0

¿Qué ocurre si no deseo usar el modo sin bloqueo para otras operaciones de socket (como leer, escribir)? ¿Puedo borrar el indicador 'O_NONBLOCK' después de conectar el socket? Si es posible, ¿es seguro? –

+1

@anton_rh: Sí, es seguro borrar 'O_NONBLOCK' y poner el socket nuevamente en modo de bloqueo. – caf

3

Ésta ha parametrizado IP, el puerto, el tiempo de espera en segundos, controlar los errores de conexión y le dará tiempo de conexión en milisegundos:

#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

El las respuestas sobre el uso de select()/poll() son correctas y el código debe escribirse de esta manera para ser portátil.

Sin embargo, ya que estás en Linux, se puede hacer esto:

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

Ver man 7 tcp y man setsockopt.

Lo usé para acelerar el tiempo de espera de conexión en un programa que necesitaba para parchar rápidamente. Hackear el tiempo de espera a través de select()/poll() no era una opción.

Cuestiones relacionadas