使用recv工作从套接字接收数据

时间:2013-12-27 03:53:10

标签: c unix proxy recv berkeley-sockets

我正在尝试使用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()永远不会返回。

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

    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()返回1:

    时才会调用initSocket()函数
    int main(int argc, const char *argv[]) {
        if (initSocket() == 1) {
            startListening();
        }
    
        return 0;
    }
    

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

1 个答案:

答案 0 :(得分:0)

由于您的 REST请求 HTTP方法,因此它具有明确定义的HTTP Message Length,因此您必须recv()直到完整的信息已经到了。