如何管理大量的套接字

时间:2015-02-17 20:12:22

标签: c sockets

我开发了一个C软件,它使用不同的循环在IP和端口上打开套接字(例如192.168.1.10 port = 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;
}

2 个答案:

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

我正在使用带有非常小的超时和线程的select添加非阻塞方法。应该重新制作,因为很多错误检查都没有以正确的方式完成。但希望你能得到这个想法。

答案 1 :(得分: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既好又稳定,但这是您的选择。

有用的链接: