Я пытаюсь создать многопоточный сервер, чтобы при наличии нескольких одновременных запросов сервер мог обрабатывать их одновременно. Но мой код по-прежнему требует ожидания выполнения первого запроса перед запуском второго запроса.
Ниже мой код
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\r\nContent-Type: text/html\r\n\r\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\r\nContent-Type: text/html\r\n\r\n<html><body><h1>404 Not Found</h1></body></html>";
send(client_socket, response.c_str(), response.length(), 0);
}
closesocket(client_socket);
return 0;
}
Если я сделаю 3 запроса GET к серверу, первый запрос потребует 3 секунды, второй запрос потребует 6 секунд и третий запрос 9 секунд. Разве thread не должен обрабатывать соединение отдельно? Почему другой поток все еще зависит от первого соединения? Любой совет будет принят с благодарностью





Во-первых, этот код даже отдаленно не похож на жизнеспособный HTTP-сервер, поскольку вы неправильно получаете и анализируете HTTP-запросы.
Но помимо этого, хотя вы нормально обрабатываете несколько соединений (хотя использование отдельных потоков сомнительно), соединение HTTP 1.x просто не может обрабатывать более 1 запроса за раз. Именно так работает протокол HTTP, по крайней мере, до появления HTTP/2 с его возможностями мультиплексирования. В HTTP 1.x для параллельного выполнения нескольких запросов необходимо несколько подключений, но браузеры имеют тенденцию объединять свои соединения и не гарантируют одновременное использование нескольких подключений к одному и тому же серверу, и вы не можете заставить браузер сделай это.
Еще кое-что, на что следует обратить внимание: даже если у вас было несколько подключений одновременно, ваш клиентский поток имеет занятой цикл, который не отдает время ЦП другим потокам. Вместо этого вам следует использовать std::this_thread::sleep_for() или std::this_thread::sleep_until(), чтобы перевести вызывающий поток в спящий режим и тем временем дать возможность другим потокам работать, например:
if (method == "GET") {
//simulate heavy task with delay
std::this_thread::sleep_for(chrono::seconds(3));
/*...*/
}
Спасибо за ваше четкое объяснение. Но у меня все еще есть вопрос, не могли бы вы рассказать подробнее? 1) Вы заявляете, что я неправильно получаю и анализирую запрос. Но код работает так, как я ожидал (для части приема и анализа), не могли бы вы объяснить это подробнее? 2) Браузер не может одновременно обрабатывать несколько соединений?
1) Вы ожидаете получить весь HTTP-запрос и только один полный запрос за один recv() вызов. Это просто не так, как работают TCP или HTTP. Вам нужно несколько чтений, и вам придется анализировать данные по пути, чтобы знать, когда запрос был получен полностью и начинается новый запрос. Существует множество вопросов StackOverflow, связанных с чтением TCP-сообщений. 2) один экземпляр браузера может использовать несколько подключений к одному и тому же серверу, но это не гарантируется. Вам может понадобиться несколько браузеров, чтобы делать то, что вы хотите.
Есть много проблем. Одна из них заключается в том, что вы неправильно обнаруживаете ошибки вызовов
socketилиaccept. И это только две из многих проблем.