2015-02-17 39 views
0

我開發了一個C軟件,它使用不同的循環來打開IP和端口上的套接字(例如192.168.1.10端口= 80),並檢查是否有人被打開; 下面是代碼:如何管理大量的套接字

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



int main(void) 
{ 
    int sockfd = 0,n = 0; 
    char recvBuff[1024]; 
    struct sockaddr_in serv_addr; 

    memset(recvBuff, '0' ,sizeof(recvBuff)); 
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0))< 0) 
    { 
     printf("\n Error : Could not create socket \n"); 

    } 

    serv_addr.sin_family = AF_INET; 
    int i; 
    char buf[15]; 

    for (i = 1; i < 255; i++) { 
     sprintf(buf, "192.168.1.%d", i); // puts string into buffer 
     for (int port = 0; port<=1024; port++) { 
//   Problem 
//   int sockfd = 0,n = 0; 
//   char recvBuff[1024]; 
//   struct sockaddr_in serv_addr; 
//    
//   memset(recvBuff, '0' ,sizeof(recvBuff)); 
//   if((sockfd = socket(AF_INET, SOCK_STREAM, 0))< 0) 
//   { 
//    printf("\n Error : Could not create socket \n"); 
//    return 1; 
//   } 
      serv_addr.sin_port = htons(port); 
      serv_addr.sin_addr.s_addr = inet_addr(buf); 
      if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))<0) 
      { 
       // printf("The port %d of host %s is not open \n",port,buf); 

      }else{ 
       printf("The port %d of host %s is open \n",port,buf); 
      } 
     } 

    } 
    return 0; 
} 

在我已經添加了connection_nonblocking方法,它可以減少可能出現的問題這第二個版本。 這是使用OpenMP指令來提高性能的版本;你有什麼想法來改進它嗎?

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


int connect_nonblock (int sockfd, const struct sockaddr *saptr, socklen_t salen) 
{ 
    int n, valopt; 
    socklen_t len; 
    fd_set rset; 
    struct timeval tv; 
    long int arg; 

    arg = fcntl(sockfd, F_GETFL, NULL); 
    arg |= O_NONBLOCK; 
    fcntl(sockfd, F_SETFL, arg); 

    n = connect(sockfd, saptr, salen); 

    if (n == 0) { 
     // completed immediately 
     arg &= (~O_NONBLOCK); 
     fcntl(sockfd, F_SETFL, arg); 
     close(sockfd); 

     return 0; 
    } 

    if (n < 0) { 
     if (errno != EINPROGRESS) { 
      // fail somehow... 
      arg &= (~O_NONBLOCK); 
      fcntl(sockfd, F_SETFL, arg); 
      close(sockfd); 
      return -1; 
     } 
     else { 
      tv.tv_sec = 0; 
      tv.tv_usec = 10000; 
      FD_ZERO(&rset); 
      FD_SET(sockfd, &rset); 
      n = select(sockfd + 1, NULL, &rset, NULL, &tv); 
      if (n < 0 && errno != EINTR) { 
       arg &= (~O_NONBLOCK); 
       fcntl(sockfd, F_SETFL, arg); 
       close(sockfd); 
       return -1; 
      } 
      else if (n > 0) { 
       len = sizeof(int); 
       getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &len); 
       if (valopt != 0) { 
        arg &= (~O_NONBLOCK); 
        fcntl(sockfd, F_SETFL, arg); 
        close(sockfd); 
        return -1; 
       } 
      } 
      else { 
       arg &= (~O_NONBLOCK); 
       fcntl(sockfd, F_SETFL, arg); 
       close(sockfd); 
       return -1; 
      } 
     } 
    } 

    arg &= (~O_NONBLOCK); 
    fcntl(sockfd, F_SETFL, arg); 
    close(sockfd); 

    return 0; 
} 

int main(void) 
{ 
    int sockfd = 0; 
    struct sockaddr_in serv_addr; 
    int i, port; 
    char buf[15]; 
    double end, start = omp_get_wtime(); 
    for (i = 1; i <= 255; i++) { 
     sprintf(buf, "192.168.1.%d", i); // puts string into buffer 
     fprintf(stdout, "Checking address: %s\n", buf); 
     //omp_set_num_threads(1); 
     #pragma omp parallel for private(sockfd,serv_addr,port) 

     for (port = 0; port <= 1024; port++) { 
      if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
      { 
       perror("socket"); 
      } 

      memset(&serv_addr, 0, sizeof(serv_addr)); 
      serv_addr.sin_family = AF_INET; 
      serv_addr.sin_port = htons(port); 
      serv_addr.sin_addr.s_addr = inet_addr(buf); 

      if (connect_nonblock(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) { 
       /* uncoment this but if you want */ 
       //fprintf(stdout, "The port %d of host %s is not open.\n", port, buf); 
       ; 
      } 
      else { 
       fprintf(stdout, "The port %d of host %s is open.\n", port, buf); 
      } 
     } 

    } 
    end = omp_get_wtime(); 
    printf("Elapsed time = %f sec\n", end-start); 
    return 0; 
} 
+0

一個建議:我覺得你的意思'memset的(recvBuff,0,的sizeof(recvBuff));' – 2015-02-17 20:14:21

+2

你可以用」連接成功後,請在同一個套接字上反覆調用連接。您需要在每次成功連接後創建一個新套接字(並跟蹤所有這些套接字)或關閉現有套接字並重新創建套接字。在前一種情況下,如果網絡中有大量計算機,則可能會用完文件描述符。如果連接的呼叫似乎掛起,那麼目標機器不存在或者沒有應答,並且您沒有收到ICMP消息告訴你這麼快。相反,連接調用在很長一段時間後最終會超時。 – jschultz410 2015-02-17 20:25:08

+0

謝謝。我還沒有完全理解如何關閉和重新打開套接字。你能修改我使用這些操作發佈的代碼嗎? – 2015-02-17 20:33:29

回答

1

正如評論中所述,您並未關閉套接字。您可以使用close,因爲這是一個文件描述符。但是如果你掃描了很多地址,如果連接失敗並且超時,這將非常耗時。 我加入你的代碼做一點修改爲:

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

int connect_nonblock (int sockfd, const struct sockaddr *saptr, socklen_t salen) 
    { 
     int n, valopt; 
     socklen_t len; 
     fd_set rset; 
     struct timeval tv; 
     long int arg; 

     arg = fcntl(sockfd, F_GETFL, NULL); 
     arg |= O_NONBLOCK; 
     fcntl(sockfd, F_SETFL, arg); 

     n = connect(sockfd, saptr, salen); 

     if (n == 0) { 
      /* completed immediately */ 
      arg &= (~O_NONBLOCK); 
      fcntl(sockfd, F_SETFL, arg); 
      close(sockfd); 

      return 0; 
     } 

     if (n < 0) { 
      if (errno != EINPROGRESS) { 
       /* fail somehow... */ 
       arg &= (~O_NONBLOCK); 
       fcntl(sockfd, F_SETFL, arg); 
       close(sockfd); 
       return -1; 
      } 
      else { 
       tv.tv_sec = 0; 
       tv.tv_usec = 10000; 
       FD_ZERO(&rset); 
       FD_SET(sockfd, &rset); 
       n = select(sockfd + 1, NULL, &rset, NULL, &tv); 
       if (n < 0 && errno != EINTR) { 
        arg &= (~O_NONBLOCK); 
        fcntl(sockfd, F_SETFL, arg); 
        close(sockfd); 
        return -1; 
       } 
       else if (n > 0) { 
        len = sizeof(int); 
        getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &len); 
        if (valopt != 0) { 
         arg &= (~O_NONBLOCK); 
         fcntl(sockfd, F_SETFL, arg); 
         close(sockfd); 
         return -1; 
        } 
       } 
       else { 
        arg &= (~O_NONBLOCK); 
        fcntl(sockfd, F_SETFL, arg); 
        close(sockfd); 
        return -1; 
       } 
      } 
     } 

     arg &= (~O_NONBLOCK); 
     fcntl(sockfd, F_SETFL, arg); 
     close(sockfd); 

     return 0; 
    } 

    int main(void) 
    { 
     int sockfd = 0, n = 0; 
     char recv_buff[1024]; 
     struct sockaddr_in serv_addr; 
     int i, port; 
     char buf[15]; 

     memset(recv_buff, '0', sizeof(recv_buff)); 

     for (i = 1; i < 255; i++) { 
      sprintf(buf, "192.168.88.%d", i); // puts string into buffer 
      for (port = 0; port <= 1024; port++) { 
       if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
       { 
        perror("socket"); 
        return EXIT_FAILURE; 
       } 

       memset(&serv_addr, 0, sizeof(serv_addr)); 
       serv_addr.sin_family = AF_INET; 
       serv_addr.sin_port = htons(port); 
       serv_addr.sin_addr.s_addr = inet_addr(buf); 

       if (connect_nonblock(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) 
       { 
        printf("The port %d of host %s is not open \n", port, buf); 
       } else { 
        printf("The port %d of host %s is open \n", port, buf); 
       } 

       // you missed this 
       close(sockfd); 
      } 
     } 
     return 0; 
    } 

我加入使用select有一個很小的超時和線程非阻塞的方法。應該重新編寫,因爲很多錯誤檢查沒有以適當的方式完成。但希望你能明白這個主意。

+0

感謝您的回覆。問題仍然存在,我的意思是搜索在一些循環後停止,過了一段時間後,它再次啓動。如何通過連續搜索來修改它?另外,你可以提供一些OpenMP下的使用提示來加速它嗎? – 2015-02-18 15:15:56

+0

由於連接超時,程序停止並運行速度較慢。正如連接的手冊頁中所示:「請注意,對於IP套接字,超時時間可能非常長......」。我從未使用過OpenMPI。但我認爲這可以通過非阻塞套接字和select或epoll來完成。 – taliezin 2015-02-18 16:03:06

+0

謝謝你的回覆。所以問題是一些套接字停止了程序的執行,不是嗎?我如何在我的代碼中放棄他們。你能修改它嗎? – 2015-02-18 21:17:18

0

這裏我的建議:

  • 使用非阻塞操作。
  • 在受控超時的情況下一次運行多個套接字。

運行流套接字時有幾個問題:它們的數量是有限的。舊的實現不能在每個接口上打開超過65K的套接字。較新的可以做得更好,但這仍然是很多資源(因爲您計劃完全掃描260 + K套接字)。

流套接字沒有同步操作(默認情況下)。打開和關閉操作都會啓動一系列數據包,以便兩端都能知道。當然,您可以強制它們關閉,但這也可能會給算法帶來問題,因爲內核仍然會接收那些不正確終止的連接的數據包。

即使當您掃描本地網絡時,嘗試連接的時間可能會相當長,因爲遠程主機可能禁用了ICMP響應。

因此,算法可能是這樣的:

while (true) 
{ 
    while (size_of_socket_table < N && has_more_addr()) 
    { 
     Create socket entry 
      Create socket 
      Mark operation expiration time 
     Initiate connect operation 
     if fail 
     { 
      continue 
     } 
     Add entry to table 
     Register entry for `pollfd` 
    } 

    If entry table is empty 
     break 
    Compute timeout from socket entries 
    Execute EPOLL with timeout 

    Process connected sockets: 
     close 
     release socket entries 
    Process timed out socket entries 
     close 
     release socket entries 
} 

注:流套接字連接,當文件描述符準備好寫(EPOLLOUT)。

爲什麼epoll vs poll vs select

  • epoll允許將用戶數據與文件描述符(套接字)關聯起來,這對編程非常方便。
  • select是不是太好,因爲你將不得不處理FD_SET大小限制,反正裏面使用poll
  • poll是好的和穩定的,但它是你的選擇。

相關鏈接: