2015-02-17 4 views
0

Я разработал программное обеспечение С, которое использует различные петли для открытия сокета на 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));' –

+2

Вы можете» t повторно подключите соединение к одному сокету после его успешного подключения. Вам необходимо создать новый сокет после каждого успешного соединения (и следить за всеми из них) или закрыть существующий и заново создать его. В первом случае вы, скорее всего, исчерпаете файловые дескрипторы, если у вас много машин в этой сети. Если вызов подключиться, кажется, висит, тогда целевая машина либо не существует, либо не отвечает, и вы не получаете сообщений ICMP, рассказывающих об этом быстро. Вместо этого, вызов для подключения, в конечном счете, истечет через некоторое время. – jschultz410

+0

Спасибо. Я не совсем понимаю, как закрыть и снова открыть сокет. Могли бы вы изменить код, который у меня есть, с этими операциями? –

ответ

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; 
    } 

Я добавляю нон подход блокировки с помощью выбора с очень небольшим количеством тайм-аута и нитками. Должно быть переделано, потому что проверка ошибок не выполняется должным образом. Но надеюсь, что вы получите эту идею.

+0

Благодарим вас за ответ. Проблема все еще существует, я имею в виду, что поиск останавливается после некоторого цикла, и через некоторое время он начинается снова. Как я могу его изменить, при непрерывном поиске? Кроме того, можете ли вы дать некоторый намек на использование в OpenMP, чтобы ускорить его? –

+0

Программа останавливается и работает медленнее из-за таймаута соединения. Как на странице man connect: «Обратите внимание, что для IP-сокетов таймаут может быть очень длинным ...». Я никогда не использовал OpenMPI. Но также я думаю, что это можно сделать с неблокирующими сокетами и select или epoll. – taliezin

+0

Благодарим вас за ответ. Так что проблема в том, что какой-то сокет останавливает выполнение программы, не так ли? Как я могу прервать их в своем коде. Можете ли вы изменить его? –

0

Вот мой совет:

  • используют не блокирующие операции.
  • Запуск нескольких сокетов одновременно с контролируемым таймаутом.

При запуске потоковых сокетов возникает несколько проблем: их число является конечным. Старые реализации не могут открыть более 65 тыс. Сокетов для каждого интерфейса. Более новые могут сделать лучше, но это все еще много ресурсов (так как вы планируете полностью сканировать сокеты 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 против poll - select?

  • epoll позволяет связывать пользовательские данные с дескриптором файла (сокета), что очень удобно для программирования.
  • select не слишком хорошо, так как вам придется иметь дело с ограничениями размера FD_SET, и в любом случае он использует poll внутри.
  • poll хорошо и стабильно, но это ваш выбор.

Полезные ссылки:

Смежные вопросы