0

所以我在windows上为C开发了一个端口扫描器,但我注意到在一些IP上它运行得非常慢。这是我的代码:

DWORD WINAPI connectPortW(LPVOID lpParam)
{
    HANDLE hStdout;
    PMYDATA pDataArray;

    WSADATA firstsock;
    SOCKET s;
    struct sockaddr_in sa;
    int err;

    char * openPorts = (char *)malloc(sizeof(char)*256);
    memset(&openPorts[0], 0, strlen(openPorts));

    hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    if(hStdout == INVALID_HANDLE_VALUE )
    {
        return 1;
    }

    pDataArray = (PMYDATA)lpParam;

    strncpy((char *)&sa,"",sizeof sa);  
    sa.sin_family = AF_INET;

    if (WSAStartup(MAKEWORD(2,0),&firstsock) != 0)
    {
        fprintf(stderr,"WSAStartup() failed"); 
        exit(1);
    }

    sa.sin_addr.s_addr = inet_addr(pDataArray->ip); 

    s = socket(AF_INET, SOCK_STREAM, 0); //make net a valid socket handle
    if(s < 0)
    {
        perror("\nSocket creation failed");  // perror function prints an error message to stderr
        exit(1);
    }

    sa.sin_port = htons(pDataArray->port);
    err = connect(s, (struct sockaddr *)&sa, sizeof sa);

    //connection not accepted
    if(err == SOCKET_ERROR)
    {
        printf("%s %-5d Winsock Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
        strcpy("NULL", openPorts);
        fflush(stdout);
    }
    //connection accepted
    else
    {
        printf("%s %-5d accepted            \n", pDataArray->ip, pDataArray->port);
        sprintf(openPorts, "%i,", pDataArray->port);
        if(shutdown(s, SD_BOTH ) == SOCKET_ERROR )
        {
            perror("\nshutdown");
            exit(1);   
        }
   } 
   closesocket(s); 

   fflush(stdout);

   strcpy(pDataArray->openPorts, openPorts);

   free(openPorts);

   return 0;
}

请记住,我已经使用了线程,并且每个线程都为同一 IP 上的不同端口(0 - 1024)调用此函数。

那么我怎样才能加快速度呢?我一直看到人们在谈论非阻塞,这会加快速度吗?如果可以,我该如何实现。谢谢!

编辑:在上述“慢”IP 之一上从 0 到 1024 扫描需要 614 秒(10 分钟)

编辑2:我开始尝试使用非阻塞......我这样做对吗?

ioctlsocket(s, FIONBIO, &on);
connect(s, (struct sockaddr *)&sa, sizeof sa);
FD_ZERO(&fds);
FD_SET(s, &fds);

err = select(s, &fds, &fds, &fds, &tv);

if (err != SOCKET_ERROR && err != 0)
{
    sprintf(openPorts + strlen(openPorts),"%i,", pDataArray->port);
}
closesocket(s);

编辑 3:似乎这种新方法给了我不准确的结果,但速度要快得多。与在同一 IP 上运行 nmap 的结果相比,我似乎获得了更多的开放端口。

4

1 回答 1

1

我看到你的线程代码有很多问题:

  • 如果发生故障,它会泄漏内存。

  • strlen()你在调用memset()你的openports变量时误用了。只需完全删除并在分配时memset()使用calloc()or代替. 或者,只使用调用堆栈,因为变量很小: 或者更好的是,根本不使用局部变量,只需在有可用结果时直接写入即可。LocalAlloc(LMEM_ZEROINIT)openportschar openPorts[256] = {0};openportspDataArray->openPorts

  • 你根本不应该使用exit()。改为使用return

  • 从技术上讲,多次调用WSAStartup()/并不是非法的,因为 WinSock 是引用计数的,但是最好在程序启动/退出时只调用一次,而不是每个线程。WSACleanup()但是,由于您正在调用WSAStartup(),因此您必须调用WSACleanup()以保持 WinSock 引用计数平衡。

  • 你想做什么strcpy("NULL", openPorts);?您正在写入只读存储器。我想你的意思是strcpy(openPorts, "NULL");相反。

  • pDataArray->openPorts如果多个线程共享一个缓冲区,则写入不是线程安全的(您,sprintf()字符串中使用的暗示可能是这种情况)。当跨多个线程写入缓冲区时,您需要同步对缓冲区的访问。您可以为此目的使用临界区或互斥锁。

话虽如此,您正在使用阻塞套接字,因此connect()将阻塞线程,直到 WinSock 在内部超时,这在慢速网络上可能需要一段时间。要加快速度,请使用 将套接字切换到非阻塞模式ioctrlsocket(FIONBIO),然后使用select()为 实现您自己的超时connect(),例如:

DWORD WINAPI connectPortW(LPVOID lpParam)
{
    PMYDATA pDataArray = (PMYDATA) lpParam;

    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
    if (hStdout == INVALID_HANDLE_VALUE)
        return 1;

    WSADATA wsa;
    int err = WSAStartup(MAKEWORD(2,0), &wsa);
    if (err != 0)
    {
        fprintf(stderr, "%s %d WSAStartup() failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
        return 1;
    }

    SOCKET s = socket(AF_INET, SOCK_STREAM, 0); //make net a valid socket handle
    if (s == INVALID_SOCKET)
    {
        fprintf(stderr, "%s %d Socket creation failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
        WSACleanup();
        return 1;
    }

    u_long enabled = 1;
    if (ioctlsocket(s, FIONBIO, &enabled) == SOCKET_ERROR)
    {
        fprintf(stderr, "%s %d Socket non-blocking mode failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
        closesocket(s);
        WSACleanup();
        return 1;
    }

    struct sockaddr_in sa = {0};
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = inet_addr(pDataArray->ip); 
    sa.sin_port = htons(pDataArray->port);

    if (connect(s, (struct sockaddr *)&sa, sizeof sa) == SOCKET_ERROR)
    {
        err = WSAGetLastError();
        if (err != WSAEWOULDBLOCK)
        {
            fprintf(stderr, "%s %d Socket connect failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
            closesocket(s);
            WSACleanup();
            return 1;
        }

        fd_set wfd, efd;

        FD_ZERO(s, &wfd);
        FD_SET(s, &wfd);

        FD_ZERO(s, &efd);
        FD_SET(s, &efd)'

        timeval timeout;
        timeout.tv_sec = 5;
        timeout.tv_usec = 0;

        err = select(0, NULL, &wfd, &efd, &timeout);
        if (err == SOCKET_ERROR)
        {
            fprintf(stderr, "%s %d Socket select failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, WSAGetLastError());
            closesocket(s);
            WSACleanup();
            return 1;
        }

        if (err == 0)
        {
            // connect timeout
            closesocket(s);
            WSACleanup();
            return 0;
        }

        if (FD_ISSET(s, &efd))
        {
            err = 0;
            getsockopt(s, SOL_SOCKET, SO_ERROR, (char*)&err, sizeof err);
            closesocket(s);
            WSACleanup();

            switch (err)
            {
                case WSAETIMEDOUT: // connect timeout
                case WSAECONNREFUSED: // port closed
                    return 0;
            }

            fprintf(stderr, "%s %d Socket connect failed, Error Code : %d\n", pDataArray->ip, pDataArray->port, err);
            return 1;
        }
    }

    // connected!
    printf("%s %d accepted\n", pDataArray->ip, pDataArray->port);

    // note, this is not thread-safe! Need to sync access to openPorts...
    sprintf(pDataArray->openPorts + strlen(pDataArray->openPorts), "%d,", pDataArray->port);

    closesocket(s); 
    WSACleanup();
    return 0;
}
于 2017-08-15T18:15:12.487 回答