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

使用C++提供HTML服务时页面加载出现连接重置错误

问题分析与解决方案

你的问题源于两个核心问题:

  • HTTP/1.1 默认启用持久连接(Keep-Alive),浏览器会保持TCP连接等待后续请求,但你的服务器发送响应后立即关闭socket,导致浏览器触发"连接已重置"错误。
  • send() 调用可能因网络缓冲区限制,无法一次性发送全部响应数据,部分内容未传输完成就关闭连接,也会引发异常。

具体修复步骤

1. 添加Connection: close响应头

明确告诉浏览器本次会话结束后关闭连接,避免服务器主动关闭socket时触发错误:

std::string response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello";

2. 确保响应数据完整发送

send() 可能仅发送部分字节,需要循环调用直到全部数据传输完成:

int totalBytesSent = 0;
int bytesLeft = response.size();
char* buffer = const_cast<char*>(response.c_str());

while (totalBytesSent < response.size()) {
    int bytesSent = send(acceptSocket, buffer + totalBytesSent, bytesLeft, 0);
    if (bytesSent == SOCKET_ERROR) {
        std::cerr << "Error sending data to client: " << WSAGetLastError() << std::endl;
        break;
    }
    totalBytesSent += bytesSent;
    bytesLeft -= bytesSent;
}

if (totalBytesSent == response.size()) {
    std::cout << "Served 'Hello' to client." << std::endl;
}

3. (规范优化)读取客户端HTTP请求

HTTP协议要求服务器先接收请求再返回响应,添加请求读取逻辑可避免潜在的连接异常:

void handleClient(SOCKET acceptSocket) {
    // 读取客户端请求缓冲区
    char requestBuffer[1024];
    int bytesReceived = recv(acceptSocket, requestBuffer, sizeof(requestBuffer)-1, 0);
    if (bytesReceived > 0) {
        requestBuffer[bytesReceived] = '\0';
        std::cout << "Received request:\n" << requestBuffer << std::endl;
    }

    // 发送响应(带Connection: close和完整发送逻辑)
    std::string response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello";
    int totalBytesSent = 0;
    int bytesLeft = response.size();
    char* buffer = const_cast<char*>(response.c_str());

    while (totalBytesSent < response.size()) {
        int bytesSent = send(acceptSocket, buffer + totalBytesSent, bytesLeft, 0);
        if (bytesSent == SOCKET_ERROR) {
            std::cerr << "Error sending data to client: " << WSAGetLastError() << std::endl;
            break;
        }
        totalBytesSent += bytesSent;
        bytesLeft -= bytesSent;
    }

    if (totalBytesSent == response.size()) {
        std::cout << "Served 'Hello' to client." << std::endl;
    }

    closesocket(acceptSocket);
}

4. 多线程处理客户端(并发优化)

当前单线程代码会在处理客户端时阻塞新连接,改用线程处理可提升并发能力:

// 在main函数的accept之后替换为线程处理
std::thread clientThread(handleClient, acceptSocket);
clientThread.detach(); // 分离线程,让其独立运行

完整修改后的代码

#include <tchar.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <thread>
#include <string>

#pragma comment(lib, "Ws2_32.lib")

void handleClient(SOCKET acceptSocket) {
    // 读取客户端请求
    char requestBuffer[1024];
    int bytesReceived = recv(acceptSocket, requestBuffer, sizeof(requestBuffer)-1, 0);
    if (bytesReceived > 0) {
        requestBuffer[bytesReceived] = '\0';
        std::cout << "Received request:\n" << requestBuffer << std::endl;
    } else if (bytesReceived == 0) {
        std::cout << "Client disconnected before sending request." << std::endl;
    } else {
        std::cerr << "Error receiving request: " << WSAGetLastError() << std::endl;
    }

    // 构造响应,添加Connection: close头
    std::string response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHello";

    // 确保完整发送响应
    int totalBytesSent = 0;
    int bytesLeft = response.size();
    char* buffer = const_cast<char*>(response.c_str());

    while (totalBytesSent < response.size()) {
        int bytesSent = send(acceptSocket, buffer + totalBytesSent, bytesLeft, 0);
        if (bytesSent == SOCKET_ERROR) {
            std::cerr << "Error sending data to client: " << WSAGetLastError() << std::endl;
            break;
        }
        totalBytesSent += bytesSent;
        bytesLeft -= bytesSent;
    }

    if (totalBytesSent == response.size()) {
        std::cout << "Served 'Hello' to client successfully." << std::endl;
    }

    closesocket(acceptSocket);
}

int main() {
    SOCKET serverSocket, acceptSocket;
    const int PORT = 55555;
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2, 2);
    int wsaerr = WSAStartup(wVersionRequested, &wsaData);

    if (wsaerr != 0) {
        std::cerr << "Winsock dll not found." << std::endl;
        return 0;
    } else {
        std::cout << "Winsock dll was found!" << std::endl;
        std::cout << "Status: " << wsaData.szSystemStatus << std::endl;
    }

    serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (serverSocket == INVALID_SOCKET) {
        std::cerr << "Error at socket()." << std::endl;
        WSACleanup();
        return 0;
    } else {
        std::cout << "Socket() is OK!" << std::endl;
    }

    sockaddr_in service;
    service.sin_family = AF_INET;
    InetPton(AF_INET, _T("127.0.0.1"), &service.sin_addr.s_addr);
    service.sin_port = htons(PORT);

    if (bind(serverSocket, (SOCKADDR*)&service, sizeof(service)) == SOCKET_ERROR) {
        std::cerr << "bind() failed." << std::endl;
        std::cerr << WSAGetLastError() << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return 0;
    } else {
        std::cout << "bind() is OK!" << std::endl;
    }

    if (listen(serverSocket, SOMAXCONN) == SOCKET_ERROR) {
        std::cerr << "listen() error listening on socket." << std::endl;
        closesocket(serverSocket);
        WSACleanup();
        return 0;
    } else {
        std::cout << "listen() is OK!, waiting for connections.." << std::endl;
    }

    while (true) {
        acceptSocket = accept(serverSocket, NULL, NULL);
        if (acceptSocket == INVALID_SOCKET) {
            std::cerr << "accept failed." << std::endl;
            std::cerr << WSAGetLastError() << std::endl;
            closesocket(serverSocket);
            WSACleanup();
            return -1;
        }
        std::cout << "Client connected.." << std::endl;

        // 创建线程处理客户端,避免阻塞主循环
        std::thread clientThread(handleClient, acceptSocket);
        clientThread.detach();
    }

    closesocket(serverSocket);
    WSACleanup();

    return 0;
}

额外说明

  • 编译时需链接Ws2_32.lib,代码中通过#pragma comment(lib, "Ws2_32.lib")自动完成链接。
  • Connection: close头会让浏览器在接收完响应后主动关闭连接,不会因服务器关闭socket触发错误。
  • 循环发送逻辑确保了响应数据完整传输,避免部分内容丢失导致的页面异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 00:08:10