2010-04-08 153 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()超時是不是一個選項。