【问题标题】:Receiving data from socket using recv not working使用recv从套接字接收数据不起作用
【发布时间】:2014-01-14 16:17:46
【问题描述】:

我正在尝试使用 BSD 套接字创建一个简单的代理服务器,它在端口上侦听请求,然后将该请求传递到另一台服务器,然后再将服务器的响应发送回浏览器。

我可以使用以下代码从浏览器接收 REST 请求:

void *buffer = malloc(512);
long length = 0;    

while (1) {
    void *tempBuffer = malloc(512);
    long response = recv(acceptedDescriptor, tempBuffer, 512, 0);

    if (response == 0 || response < 512) {
        free(tempBuffer);

        printf("Read %lu bytes\n", length);
        break;
    }

    memcpy(buffer + length, tempBuffer, response);
    free(tempBuffer);

    length += response;

    realloc(buffer, length + 512);
}

然而,recv() 应该在连接被对等方(在本例中为浏览器)关闭时返回 0,但情况并非如此。我能够检测连接是否已关闭的唯一方法是检查响应是否小于从recv() 请求的最大数量,512 字节。这有时会出现问题,因为我看到的一些请求不完整。

如果没有更多数据要接收,recv() 会阻塞并且永远不会返回,并且将接受的描述符设置为非阻塞意味着读取循环将永远继续,永远不会退出。

如果我:

  1. 将监听套接字描述符设置为非阻塞,当我尝试accept() 连接时出现EAGAIN 错误(资源暂时不可用)

  2. 将接受的套接字描述符设置为非阻塞,recv() 永远不会返回 0 并且循环会一直持续下去

  3. 将它们都设置为非阻塞,尝试accept() 连接时出现“错误文件描述符”错误

  4. 不要将它们中的任何一个设置为非阻塞,循环永远不会退出,因为recv() 永远不会返回。

socket 本身是按如下方式创建的,但是由于它能够检测到请求,所以我看不出它的初始化有什么问题:

int globalDescriptor = -1;
struct sockaddr_in localServerAddress;
...
int initSocket() {
    globalDescriptor = socket(AF_INET, SOCK_STREAM, 0);

    if (globalDescriptor < 0) {
        perror("Socket Creation Error");
        return 0;
    }

    localServerAddress.sin_family = AF_INET;
    localServerAddress.sin_addr.s_addr = INADDR_ANY;
    localServerAddress.sin_port = htons(8374);

    memset(localServerAddress.sin_zero, 0, 8);

    int res = 0;
    setsockopt(globalDescriptor, SOL_SOCKET, SO_REUSEADDR, &res, sizeof(res));

    //fcntl(globalDescriptor, F_SETFL, O_NONBLOCK);

    return 1;
}
...
void startListening() {
    int bindResult = bind(globalDescriptor, (struct sockaddr *)&localServerAddress, sizeof(localServerAddress));

    if (bindResult < 0) {
        close(globalDescriptor);
        globalDescriptor = 0;

        perror("Socket Bind Error");
        exit(1);
    }

    listen(globalDescriptor, 1);

    struct sockaddr_in clientAddress;
    int clientAddressLength = sizeof(clientAddress);

    while (1) {
        memset(&clientAddress, 0, sizeof(clientAddress));
        clientAddressLength = sizeof(clientAddress);

        int acceptedDescriptor = accept(globalDescriptor, (struct sockaddr *)&clientAddress, (socklen_t *)&clientAddressLength);
        //fcntl(acceptedDescriptor, F_SETFL, O_NONBLOCK);

        if (acceptedDescriptor < 0) {
            perror("Incoming Connection Error");
            exit(1);
        }

        void *buffer = malloc(512);
        long length = 0;

        while (1) {
            void *tempBuffer = malloc(512);
            long response = recv(acceptedDescriptor, tempBuffer, 512, 0);

            if (response == 0) {
                free(tempBuffer);

                printf("Read %lu bytes\n", length);
                break;
            }

            memcpy(buffer + length, tempBuffer, response);
            free(tempBuffer);

            length += response;

            realloc(buffer, length + 512);
        }

        executeRequest(buffer, length, acceptedDescriptor);

        close(acceptedDescriptor);
        free(buffer);
    }
}
...

startListening() 函数仅在initSocket() 返回 1 时调用:

int main(int argc, const char *argv[]) {
    if (initSocket() == 1) {
        startListening();
    }

    return 0;
}

我可能在这里做了一些愚蠢的事情,但如果您能提供有关此问题以及如何解决它的任何信息,我将不胜感激。

【问题讨论】:

  • recv() 只会在对等端完全关闭连接时返回0。否则返回值为-1。最好检查 recv() 返回的值是否大于 0
  • @alvits 正如预期的那样,recv() 返回读取的字节数(通常 recv() 之前和之后放置一个printf(),那么在第二次迭代中,第二个printf() 永远不会被调用。
  • 如果发件人停止发送,recv() 将阻止。发件人如何发送?调用send() 时使用什么标志?如果发送方使用标志 MSG_MORE,那么内核将不会发送,直到 MSG_EOR 内核接收到非 MSG_MORE 标志或内核接收到 MSG_EOR,然后内核才会将消息发送到您的服务器。当您收到小于您期望的大小 (512) 时,您已经收到了完整的消息。如果您期望的消息超过最大值 (>512),那么您将不得不多次执行 recv()。
  • 换句话说,如果你正好收到 512,缓冲区中可能还有更多,因此需要使用 recv() 从缓冲区中获取更多。如果你收到少于 512,那就结束了。

标签: c unix proxy recv berkeley-sockets


【解决方案1】:

由于您的REST 请求 是一个HTTP 方法,它具有明确定义的HTTP Message Length,因此您只需recv() 直到完整的消息到达.

【讨论】:

    猜你喜欢
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 2015-08-19
    • 1970-01-01
    • 2018-09-16
    • 2020-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多