2010-04-08 131 views
26

我有一个简单的程序来检查端口是否打开,但我想缩短套接字连接的超时长度,因为默认时间太长。我不知道如何做到这一点。代码如下:C:套接字连接超时

#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

你在哪个平台上? – Duck 2010-04-08 04:51:35

+0

我正在使用linux – 2010-04-08 04:52:21

+0

您在回答“fcntl(sock,F_SETFL,O_NONBLOCK)”中添加了“ 请注意,在此之后,下一个套接字读取也变为非阻塞! – 2013-09-18 10:44:28

回答

27

本文可能帮到您:http://developerweb.net/viewtopic.php?id=3196。看起来像是在连接之前将套接字置于非阻塞模式,然后在连接建立后再将其置回阻塞模式。

+0

不错。这比我的建议更好。 – asveikau 2010-04-08 05:17:16

+1

这是一个很好的答案,你为什么让它成为社区wiki?你应该为建议资源赢得一些声望。 – 2010-04-08 05:19:40

+2

论坛链接似乎已经改变了他们的软件,所以现在的链接已经死了。 – Jorenko 2011-06-08 19:14:48

46

设置套接字非阻塞,并使用select()(它需要超时参数)。如果非阻塞套接字尝试连接,则select()将指示当connect()完成(成功或失败)时套接字可写。然后,使用getsockopt()确定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

这适用于* nix,但在Windows中不起作用。在Windows中,通过在上面的代码中查看“select”的返回值来确定套接字是否已连接。在windows中,如果连接完成,则选择返回1;如果连接不成功,则返回0。如果您查看so_error,则即使连接失败,Windows始终返回0。正如他们所说,这是你的窗户。 – deltamind106 2015-05-08 13:32:23

+0

如果我不想将非阻塞模式用于其他套接字操作(如读取,写入),该怎么办?套接字连接后,我可以清除“O_NONBLOCK”标志吗?如果可能,是否安全? – 2016-07-07 12:37:46

+1

@anton_rh:是的,清除'O_NONBLOCK'并将套接字置回阻塞模式是安全的。 – caf 2016-07-07 12:52:48

3

这一项参数化的IP,端口,超时以秒处理连接错误,并给你的连接时间(毫秒):

#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

的关于使用select()/poll()的回答是正确的,并且应该用这种方式编写代码以实现便携。

但是,因为你在Linux上,你可以这样做:

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

man 7 tcpman setsockopt

我用它来加速我需要快速修补的程序中的连接超时。黑客通过select()/poll()超时是不是一个选项。