I love coding
I love coding

Reputation: 1191

How to manage huge amount of socket

I have developed a C software which uses different loops to open socket on a IP and port (for example 192.168.1.10 port = 80), and check if someone is opened; Here is the code:

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

In this second version I've added the connection_nonblocking method, which reduce the possible problems available. This is the version which uses the OpenMP directive to improve performances; do you have any idea to improve it ?

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

Upvotes: 0

Views: 193

Answers (2)

taliezin
taliezin

Reputation: 916

As mentioned in comments you are not closing socket. You can use close as this is a file descriptor. But if you scan a lot of addresses it will be time consuming if connect fails with timeout. I am adding your code a little bit revised:

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

I am adding a non blocking approach using select with a very little timeout and threads. Should be remade because a lot of error checking is not done in a proper way. But hopes you will get the idea.

Upvotes: 1

Valeri Atamaniouk
Valeri Atamaniouk

Reputation: 5163

Here my advise:

  • Use non-blocking operations.
  • Run a number of sockets at a time with controlled timeout.

There are several issues when running stream sockets: their number is finite. Old implementations can't open more than 65K sockets per interface. Newer ones can do better, but that's still a lot of resources (as you are planning to scan 260+K sockets totally).

Stream sockets do not have synchronous operations (by default). Both open and close operation initiate a sequence of packets so that both ends would be aware. You can enforce them close, of course, but that might cause problems to your algorithm as well, as kernel would still be receiving packets for those improperly terminated connections.

Even when you scan local network, the time to try to connect may be pretty high because remote host might have ICMP response disable.

So, the algorithm might look like:

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
}

Note: stream socket is connected, when file descriptor becomes ready for writing (EPOLLOUT).

Why epoll vs poll vs select?

  • epoll allows association of user data with file descriptor (socket), which is very convenient for programming.
  • select is not too good, as you will have to deal with FD_SET size restrictions, and it anyway uses poll inside.
  • poll is good and stable, but it's your choice.

Useful links:

Upvotes: 0

Related Questions