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

Windows下C++ Socket多线程服务器无法并发处理请求问题

Windows下C++多线程Socket服务器并发异常分析与解决

我想构建一个能在Windows系统下并发处理多请求的C++多线程Socket服务器,但当前代码必须等第一个请求完成后才能处理第二个请求。发送3个GET请求时,第一个耗时3秒,第二个6秒,第三个9秒,明明用了std::thread处理连接,为什么线程还是互相依赖?

以下是我的代码:

主函数代码

#include <iostream>
#include <winsock2.h>
#include <thread>
#include <string>
#include <chrono>

#define PORT 8080
#define BUFFER_SIZE 1024

using namespace std;

int handle_connection(SOCKET client_socket);

int main() {
    //server startup
    WSADATA wsaData;
    int server_start = WSAStartup(MAKEWORD(2,2),&wsaData);
    if(server_start) {
        cout<<"Error at startup socket!"<<endl;
        return 0;
    }

    //server init
    SOCKET server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if(!server_socket) {
        printf("socket function failed with error: %u\n", WSAGetLastError());
        return 0;
    }

    sockaddr_in server_address;
    memset(&server_address,0,sizeof(server_address));
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = INADDR_ANY;
    server_address.sin_port = htons(PORT);

    //server binding
    if(bind(server_socket,(sockaddr*)&server_address,sizeof(server_address))) {
        cout<<"socket function fail at binding"<<endl;
        return 0;
    }

    //server listening
    if(listen(server_socket,5) == -1) {
        cout<<"Fail at listening"<<endl;
        return 0;
    } 
    cout<<"Server listening at port "<<PORT<<endl;

    while(true) {
        //init client data
        sockaddr_in client_address;
        socklen_t client_address_len = sizeof(client_address);

        //server accept
        SOCKET client_socket = accept(server_socket,(sockaddr*)&client_address,&client_address_len);
        cout<<"Client socket init : "<<client_socket<<endl;
        if(!client_socket) {
            cout<<"Error when accepting connection"<<endl;
            continue;
        }
        cout<<"Accepeted connection from "
            <<inet_ntoa(client_address.sin_addr)<<":"<<ntohs(client_address.sin_port)<<endl;
        
        thread(handle_connection, client_socket).detach();
    }

    WSACleanup();
    return 0;
}

连接处理函数代码

int handle_connection(SOCKET client_socket) {
    cout<<"Thread with id "<<this_thread::get_id()<<" with socket "<<client_socket<<endl;

    //handle HTTP request
    char buffer[BUFFER_SIZE];
    memset(buffer,0,BUFFER_SIZE);
    SSIZE_T bytes_recived = recv(client_socket,buffer,BUFFER_SIZE,0);
    if(bytes_recived <= 0) {
        cout<<"Failed to read the request!"<<endl;
        return 0;
    }

    //parse request
    string request(buffer,bytes_recived);
    SSIZE_T first_space = request.find(' ');
    string method = request.substr(0, first_space);
    SSIZE_T second_space = request.find(' ', first_space + 1);
    string path = request.substr(first_space+1, second_space-first_space-1);

    cout<<"Received "<<method<<" request for "<<path<<endl;
    if(method == "GET") {
        //simulate heavy task with delay
        chrono::system_clock::time_point delay_time = chrono::system_clock::now() + chrono::seconds(3);
        while(delay_time > (chrono::system_clock::now())) {}
        cout<<"Done delay!"<<endl;
        string response = "HTTP/1.1 200 OK\nContent-Type: text/html\n\n<html><body><h1>Hello World!</h1></body></html>";
        send(client_socket,response.c_str(),response.length(),0);
    } else {
        std::string response = "HTTP/1.1 404 Not Found\nContent-Type: text/html\n\n<html><body><h1>404 Not Found</h1></body></html>";
        send(client_socket, response.c_str(), response.length(), 0);
    }

    closesocket(client_socket);
    return 0;
}

问题原因分析

你的代码多线程逻辑本身是正确的,但出现串行现象的原因通常有两个:

  1. HTTP持久连接(Keep-Alive):现代浏览器/HTTP客户端默认启用HTTP/1.1持久连接,会复用同一个TCP连接发送多个请求。此时服务器必须处理完当前请求才能读取下一个,导致请求串行执行,和线程无关。
  2. std::cout的输出同步锁:std::cout是线程安全的,但内部会加全局锁,多个线程的输出会被串行化,让你误以为线程是串行执行的,实际线程是并行运行的。

另外,你用忙等待(while(delay_time > now))模拟耗时任务,会占用CPU核心,但不会影响线程并发,只是效率极低。


技术解决方案

1. 验证线程并发状态

把忙等待替换为std::this_thread::sleep_for,同时打印线程启动时间戳,确认线程是否并行:

// 在handle_connection的GET分支替换忙等待
auto start_time = chrono::system_clock::now();
time_t start_t = chrono::system_clock::to_time_t(start_time);
cout << "Thread " << this_thread::get_id() << " started task at " << ctime(&start_t);

// 替换忙等待为sleep,释放CPU
this_thread::sleep_for(chrono::seconds(3));

auto end_time = chrono::system_clock::now();
time_t end_t = chrono::system_clock::to_time_t(end_time);
cout << "Thread " << this_thread::get_id() << " finished task at " << ctime(&end_t);

运行后会看到多个线程的启动时间几乎一致,证明线程是并行的。

2. 处理HTTP持久连接

如果是客户端复用连接导致的串行,可通过以下方式解决:

  • 强制关闭连接:在响应头中添加Connection: close,告诉客户端不要复用当前连接:
string response = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n<html><body><h1>Hello World!</h1></body></html>";
  • 支持多请求复用连接:修改handle_connection逻辑,循环读取请求直到客户端关闭连接,适用于高并发场景。

3. 优化线程管理

  • 避免使用detach(),改用std::vector<std::thread>管理线程,防止资源泄漏:
// 在main函数中定义线程容器
vector<thread> threads;

// 替换detach()为emplace_back
threads.emplace_back(handle_connection, client_socket);

// 可选:定期清理已结束的线程
threads.erase(remove_if(threads.begin(), threads.end(), [](thread& t) {
    if (t.joinable()) {
        t.join();
        return true;
    }
    return false;
}), threads.end());
  • 高并发场景下建议使用线程池,避免频繁创建销毁线程的开销。

4. Socket通信优化

  • 检查send和recv的返回值,确保数据完整传输(send可能只发送部分数据,需要循环发送):
// 安全的send封装
bool send_all(SOCKET sock, const char* data, int len) {
    int sent = 0;
    while (sent < len) {
        int ret = send(sock, data + sent, len - sent, 0);
        if (ret == SOCKET_ERROR) return false;
        sent += ret;
    }
    return true;
}
  • 完善错误处理,在accept、recv、send失败时调用WSAGetLastError()打印具体错误码,便于排查问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 18:50:53