使用非阻塞套接字时,recv没有响应

时间:2017-10-25 20:03:46

标签: c sockets networking

我正在尝试在c中创建一个portscanner。如果端口是打开的,我想从服务器获得响应。当我使用常规阻塞套接字时,这很好用。例如,我知道对于我网络上的某个地址,如果我检查端口80,它会在我调用recv时将html页面返回给我。我已对此进行了测试,每次都能正常工作。

但是,我想使用非阻塞套接字,因为有时某些服务器不会响应并导致程序挂起。我能够将非阻塞套接字(kindof)工作(代码目前在下面注释掉)。我可以看到哪些端口是打开的,哪些是关闭的,哪些是超时的,但是我无法从服务器获得响应(即使我知道它应该发送一个)。我做错了什么?

tl; dr:当使用非阻塞套接字(vs阻塞)时,recv不会返回任何数据。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include <fcntl.h>

#define MAX_LEN 100000

int main(int argc, char **argv)
{
    int sock, test_sock;
    struct sockaddr_in server_addr;
    struct hostent *hp;
    char buf[MAX_LEN];
    int num_bytes;
    int err_code;
    int START_PORT = 1;
    int END_PORT = 100;

    fd_set fdset;
    struct timeval tv;
    int opts;

    // resolve server name for its IP address, etc.
    hp = gethostbyname(argv[1]);
    if (NULL == hp) {
        perror("gethostbyname");
        exit(2);
    }

    //printf("Here1\n");

    // build remote server addr/port
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
    //server_addr.sin_port = htons(atoi(argv[2]));


    test_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    for(int i=START_PORT; i<=END_PORT; i++) {
        printf("Here2\n");
        sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //created the tcp socket
        //opts = fcntl(sock, F_SETFL, O_NONBLOCK);
        printf("Here3\n");
        if (sock < 0)
        {
            perror("Socket()\n");
            exit(1);
        }  

        server_addr.sin_port = htons(i);

        // connect to server
        printf("Here4\n");
        err_code = connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
        printf("Here5\n");

        /* ... */

        if (err_code < 0) {
            printf("Port %d: connection refused\n", i);
            //exit(3);
        } else {
            printf("Port %d:\n", i);
            memset(buf, 0, MAX_LEN);

            // Create message to send
            char message[256];
            strcpy(message, "GET / HTTP/1.0\r\nHost: ");
            strcat(message, argv[1]);
            strcat(message, "\r\n\r\n");

            unsigned total_bytes_sent = 0;
            num_bytes = send(sock, message, strlen(message), 0);
            if (num_bytes < 0) {
                perror("send");
                exit(4);
            }

            unsigned total_bytes_received = 0;
            while(1) {
                num_bytes = recv(sock, buf+total_bytes_received, MAX_LEN, 0);
                if(num_bytes <= 0){
                    break;
                }
                total_bytes_received += num_bytes;
            }

            // display received ack message
            //printf("Port %d:\n", i);
            fflush(stdout);
            write(1, buf, total_bytes_received);
            printf("\n");
            printf("Done...\n");
        }

        close(sock);
    }



    // close sock to release resource
    close(sock);

    return 0;
}

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

#include <fcntl.h>

#define MAX_LEN 100000

int main(int argc, char **argv)
{
int sock, sock_test;
struct sockaddr_in server_addr;
struct hostent *hp;
char buf[MAX_LEN];
int num_bytes;
int err_code;
int START_PORT = 1;
int END_PORT = 100;
int valid = 1;

fd_set fdset;
struct timeval tv;


// resolve server name for its IP address, etc.
hp = gethostbyname(argv[1]);
if (NULL == hp) {
    perror("gethostbyname");
    exit(2);
}

// build remote server addr/port
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);



for(int i=START_PORT; i<=END_PORT; i++) {
    sock_test = socket(AF_INET, SOCK_STREAM, 0);
    if (sock_test < 0)
    {
        perror("Socket()\n");
        exit(1);
    }  

    fcntl(sock_test, F_SETFL, O_NONBLOCK);
    server_addr.sin_port = htons(i);

    connect(sock_test, (struct sockaddr *)&server_addr, sizeof(server_addr));

    FD_ZERO(&fdset);
    FD_SET(sock_test, &fdset);
    tv.tv_sec = 3;            
    tv.tv_usec = 0;

    if (select(sock_test + 1, NULL, &fdset, NULL, &tv) == 1)
    {
        int so_error;
        socklen_t len = sizeof so_error;

        getsockopt(sock_test, SOL_SOCKET, SO_ERROR, &so_error, &len);

        if (so_error == 0) {
            printf("%s:%d is open\n", argv[1], i);

            memset(buf, 0, MAX_LEN);


            // Create message to send
            char message[256];
            strcpy(message, "GET / HTTP/1.0\r\nHost: ");
            strcat(message, argv[1]);
            strcat(message, "\r\n\r\n");

            printf("Here6\n");

            unsigned total_bytes_sent = 0;
            num_bytes = send(sock_test, message, strlen(message), 0);

            printf("Here7\n");

            int retry = 3;
            unsigned total_bytes_received = 0;
            while(retry) {
                num_bytes = recv(sock_test, buf+total_bytes_received, MAX_LEN, 0);
                    if (0 == num_bytes)
                    {
                    /* socket has been closed by peer */            
                    break;
                    }
                    else if(-1 == num_bytes)
                    {
                    if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
                    {
                            /* no data to be read on socket */
                            retry--;
                         /* wait one second */
                            sleep(1);
                    }
                    else
                    {
                            /* other error */
                            perror("recv");
                            break;
                    }
                    }
                    else
                {
                    total_bytes_received += num_bytes;
                    }
            }

            // display received ack message
            //printf("Port %d:\n", i);
            fflush(stdout);
            write(1, buf, total_bytes_received);
            printf("\n");
            printf("Done...\n");
        }
        else
        {
            //printf("%s:%d is closed\n", argv[1], i);
        }
    } else {
        printf("timed out\n");
        valid = 0; //set the boolean flag to false      
    }

    close(sock_test);
}



    // close sock to release resource
    close(sock_test);

return 0;
}

1 个答案:

答案 0 :(得分:2)

正如评论中所指出的,在非阻塞模式下,您必须在何时处理案例 服务器尚未准备好发送数据。

man recv(3)

  

返回值

     

成功完成后,recv()将以字节为单位返回消息的长度。如果没有可以接收的消息并且对等体已经执行了有序关闭,则recv()将返回0.否则,将返回-1并设置errno以指示错误。

     

<强>错误

     

如果出现以下情况,则recv()函数将失败:

     

EAGAIN或EWOULDBLOCK

     

套接字的文件描述符标记为O_NONBLOCK,没有数据等待接收;或者设置了MSG_OOB,并且没有带外数据可用,并且套接字的文件描述符标记为O_NONBLOCK,或者套接字不支持阻塞以等待带外数据。

由于您的客户可能会在服务器发送内容之前尝试阅读,因此您必须这样做 调整你的代码等待:

/* maximum number of retry, one second per retry */
int retry = 10;
unsigned total_bytes_received = 0;
while(retry) {
    num_bytes = recv(sock, buf+total_bytes_received, MAX_LEN, 0);
    if (0 == num_bytes)
    {
        /* socket has been closed by peer */            
        break;
    }
    else if(-1 == num_bytes)
    {
        if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
        {
            /* no data to be read on socket */
            retry--;
            /* wait one second */
            sleep(1);
        }
        else
        {
            /* other error */
            perror("recv");
            break;
        }
    }
    else
    {
        total_bytes_received += num_bytes;
    }
}