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; }
问题原因分析
你的代码多线程逻辑本身是正确的,但出现串行现象的原因通常有两个:
- HTTP持久连接(Keep-Alive):现代浏览器/HTTP客户端默认启用HTTP/1.1持久连接,会复用同一个TCP连接发送多个请求。此时服务器必须处理完当前请求才能读取下一个,导致请求串行执行,和线程无关。
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
相关产品推荐
相关产品推荐

