0

我正在尝试使用 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. 将侦听套接字描述符设置为非阻塞,EAGAIN尝试accept()连接时出现错误(资源暂时不可用)

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

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

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

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

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);
    }
}
...

仅当返回 1时才调用该startListening()函数:initSocket()

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

    return 0;
}

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

4

1 回答 1

0

由于您的REST 请求是一种HTTP 方法,因此它具有明确定义的HTTP 消息长度,因此您只需recv()等待完整的消息到达。

于 2016-10-06T06:59:34.643 回答