You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

HTTP连接生命周期中SHUT_WR与SHUT_RD的使用问题咨询

关于TCP Socket实现HTTP连接的shutdown问题

背景

我用TCP Socket与服务器建立HTTP连接,步骤如下:

  1. 调用connect建立Socket连接
  2. 调用send发送HTTP请求
  3. 调用recv接收响应

遇到的问题:

  • 在步骤2和3之间调用shutdown(socket_id, SHUT_WR)关闭写入端时,recv会获取到0字节数据;
  • 在recv之后尝试关闭写入端时,shutdown函数返回“Socket is not connected”。

目前可行但不理想的解决方法:

  • 在send和shutdown之间等待足够长时间(方案不优)
  • 不关闭Socket

我的问题

  1. 为什么在send后立即关闭Socket写入端会导致recv无法获取数据?
  2. 是否因为HTTP连接在单次请求响应后会自动关闭,导致接收后无法执行shutdown?
  3. 我的作业要求必须在某个时刻显式关闭Socket写入端,这么做的必要性是什么?应该在何时执行?

完整代码(省略shutdown部分)

int download_page(url_info* info, http_reply* reply) {
    printf("Downloading page...\n");
    int ret = 0;

    // ===== 解析主机名为IP地址 =====
    struct addrinfo* res;
    struct addrinfo hints;  // 指定使用TCP建立连接
    memset(&hints, 0, sizeof(hints));
    hints.ai_socktype = SOCK_STREAM;
    char* service = (char*) malloc(10);
    sprintf(service, "%d", info->port);
    printf("Resolving host %s on port %s...\n", info->host, service);
    int errcode = getaddrinfo(info->host, service, &hints, &res);
    if (errcode || !res || !(res->ai_addr)) {
        fprintf(stderr, "Could not resolve host %s\n\t%s (%d)", info->host, strerror(errcode), errcode);
        ret = 1;
        goto clean0;
    }
    printf("Host resolved\n");
    
    char* request_message = http_get_request(info);
    printf("Request message:\n%s\nEND of request message\n", request_message);

    // ===== 创建Socket =====
    int socket_id = socket(res->ai_family, SOCK_STREAM, 0);
    if (socket_id == -1) {
        fprintf(stderr, "Could not create socket\n");
        ret = 1;
        goto clean2;
    }
    printf("Socket created\n");

    // ===== 连接服务器 =====
    if (connect(socket_id, res->ai_addr, res->ai_addrlen)) {
        fprintf(stderr, "Could not connect to server %s\n", strerror(errno));
        ret = 1;
        goto clean2;
    }

    printf("Connected to server :)\n");

    // ===== 发送请求到服务器 =====
    if (send(socket_id, request_message, strlen(request_message), 0) == -1) {
    //if (write(socket_id, request_message, strlen(request_message)) == -1) {
        fprintf(stderr, "Could not send request to server %s\n", strerror(errno));
        ret = 1;
        goto clean2;
    } else {
        printf("Request sent to server: \"%s\"\n", request_message);
    }

    // ===== 接收响应 =====
    int buffer_size = 1024;
    int remaining_space = buffer_size;
    reply->reply_buffer = (char*) malloc(buffer_size + 1);
    reply->reply_buffer_length = 0;
    if (reply->reply_buffer != NULL) {
        char* buffer = reply->reply_buffer;
        int received;
        do {
            printf("Attempting to receive at most %d bytes from server...\n", remaining_space);
            received = recv(socket_id, buffer, remaining_space, 0);
            if (received == -1) {
                fprintf(stderr, "Could not receive data from server %s\n", strerror(errno));
                ret = 1;
                goto clean2;
            } else if (received != 0) {
                reply->reply_buffer_length += received;
                printf("\tReceived %d bytes, total of %d bytes.\n", received, reply->reply_buffer_length);
                buffer_size *= 2;
                reply->reply_buffer = (char*) realloc(reply->reply_buffer, buffer_size + 1);
                buffer = reply->reply_buffer + reply->reply_buffer_length;
                remaining_space = buffer_size - reply->reply_buffer_length;
            }
        } while (received != 0);
        reply->reply_buffer = (char*) realloc(reply->reply_buffer, reply->reply_buffer_length + 1);
        reply->reply_buffer[reply->reply_buffer_length] = '\0';
        //printf("Received %d bytes from server:\n\"%s\"\n", reply->reply_buffer_length, reply->reply_buffer);
        printf("Received %d bytes from server.\n", reply->reply_buffer_length);
    }

    clean2:
    free(request_message);
    close(socket_id);
    clean0:
    freeaddrinfo(res);
    return ret;
}

请求消息

GET / HTTP/1.1
Host: www.google.com
Connection: close




问题解答

1. 为什么send后立即shutdown(SHUT_WR)会导致recv拿不到数据?

send返回成功仅代表数据被写入系统发送缓冲区,不代表已经发送到服务器。此时调用shutdown(SHUT_WR)会向服务器发送FIN包,服务器可能还没完整接收HTTP请求,就判定客户端已结束发送,直接关闭连接,自然不会返回响应,你的recv就会读到0字节(表示连接关闭)。

另外你的请求包含Connection: close头,服务器本应处理完请求后关闭连接,但前提是它完整接收并处理了请求。如果客户端提前发FIN,服务器会直接终止处理流程。

2. 是不是HTTP单次请求响应后自动关闭导致recv后无法shutdown?

是的。因为你指定了Connection: close,服务器发送完响应后会主动关闭连接。当recv读到0字节时,说明服务器已经发送FIN包,TCP连接已处于关闭状态。此时再调用shutdown,自然会返回“Socket is not connected”——连接已被服务器关闭,本地Socket的连接状态已失效。

3. 显式关闭Socket写入端的必要性是什么?该何时执行?

显式调用shutdown(SHUT_WR)的必要性主要有两点:

  • 明确告知服务器客户端已发送完所有数据:对于HTTP这类请求-响应协议,关闭写入端是在告诉服务器“我没有更多请求要发了,你可以处理并返回响应”,这在分块发送大请求等场景中很关键。
  • 避免资源泄漏:虽然close会关闭整个Socket,但shutdown可以更精细地控制读写流,确保TCP连接优雅关闭,避免半开连接占用系统资源。

正确的执行时机是:确认所有请求数据已发送到服务器之后,再调用shutdown(SHUT_WR)。不能在send后立即调用,因为系统发送缓冲区可能还有未发送的数据。可以通过两种方式确保数据发送完成:

  1. 调用send后,用ioctl获取TIOCOUTQ参数检查发送缓冲区,确认缓冲区为空后再调用shutdown;
  2. 更稳妥的方式是,在recv开始接收响应后立即调用shutdown(SHUT_WR)——不过要注意,若服务器因Connection: close关闭连接,需在recv返回0前执行shutdown,或处理shutdown的错误(此时连接可能已关闭)。

内容的提问来源于stack exchange,提问作者user8171079

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 16:57:34