【问题标题】:HTTP Client getting "Connection reset by peer" (TCP RST) from some servers but not othersHTTP客户端从某些服务器而不是其他服务器获取“对等连接重置”(TCP RST)
【发布时间】:2025-12-23 13:45:11
【问题描述】:

我正在编写一个基本的 HTTP 客户端,但遇到了一个问题 - 一些 HTTP 服务器正在强制重置,从而导致“对等连接重置”错误。许多 HTTP 服务器都会优雅地关闭连接,但似乎没有一个服务器能够保持连接处于活动状态。

但是,我确信这是我的客户端,因为使用非常相似的源代码的 HTTP 客户端不会表现出相同的行为:它们与相同服务器的连接要么正常关闭,要么保持活动状态。

是什么导致了这个看似不一致的问题?

相关代码:

/* socket */
if ((context->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
    perror("Failed to create socket");
    exit(-1);
}

/* connect */
if (connect(context->socket, &context->tx_addr, sizeof(struct sockaddr)) != 0) {
    perror("Couldn't connect to server");
    exit(-1);
}

/* create header */
snprintf(context->packet, BUFF_SIZE,
         "GET %s HTTP/1.1\r\n" \
         "Host: %s\r\n\r\n", 
         conf->request, conf->host);

/* send header */
if ((sendto(context->socket, context->packet, BUFF_SIZE, 0, 
            NULL, 0))) != BUFF_SIZE) {
    perror("Failed to send");
    exit(-1);
}

/* receive response */
do {
    if ((received = recvfrom(context->socket, context->packet, BUFF_SIZE, 0, NULL, 
                             NULL)) < 0) {

        /* THIS is where RST occurs with some servers */

        perror("Failed to receive");
        exit(-1)
    }

    if (received >= 0)     
            context->packet[received] = '\0';
    printf("%s", context->packet);

} while (received > 0);

【问题讨论】:

    标签: c http sockets


    【解决方案1】:

    经过一番调查,很明显我的 HTTP 请求格式不正确。

    问题在于这段代码:

    /* create header */
    snprintf(context->packet, BUFF_SIZE,
             "GET %s HTTP/1.1\r\n" \
             "Host: %s\r\n\r\n", 
             conf->request, conf->host);
    
    /* send header */
    if ((sendto(context->socket, context->packet, BUFF_SIZE, 0, 
                NULL, 0)) != BUFF_SIZE) {
        perror("Failed to send entire buffer");
        exit(-1);
    }
    

    请注意,缓冲区中的BUFF_SIZE 字节已发送,即使在使用snprintf 创建标头时我们几乎肯定不会填满整个缓冲区。生成的标头的垃圾也正在传输。一些服务器只是忽略了错误的请求,其他服务器只是放弃并重置(RST)连接。

    只需将发送代码更改为类似这样即可解决问题:

    /* assuming context->packet is a string */
    int len = strlen(context->packet);
    int sent = 0, total = 0;
    
    while (total < len) {
        if ((sent = sendto(context->socket, context->packet + total, 
                           len - total, 0, NULL, 0)) <= 0) {
            perror("Failed to send");
            exit(-1);
        }
        total += sent;
    }
    

    【讨论】:

    • sendto(...) != BUF_SIZE 是错误的。首先,您现在发送的数量少于此数量。其次,TCP 缓冲的缓冲区可能比你给它发送的要少。
    • 如果我没记错的话,这取决于标志。也就是说,在没有任何标志的情况下,sendto 将阻塞,直到像 write() 一样发送请求的数量。
    • 不,它写入套接字发送缓冲区中可用的任何空间并返回。在简单的情况下您可能看不到这一点,但在流量较大的情况下确实会发生短写入。
    • 你说的都是对的。 sendto 将阻塞,但不能保证在写入整个请求之前它不会返回,例如。如果调用了信号处理程序。顺便说一句,将 sendto 与 TCP 一起使用是没有意义的。使用 send 或通用 write 函数通常更简单(也更便携)。
    • Nikolai N Fetissov 和 Per Johansson:感谢您的反馈。