Сокет в C++: обработка нескольких одновременных запросов в ОС Windows

Я пытаюсь создать многопоточный сервер, чтобы при наличии нескольких одновременных запросов сервер мог обрабатывать их одновременно. Но мой код по-прежнему требует ожидания выполнения первого запроса перед запуском второго запроса.

Ниже мой код

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 не должен обрабатывать соединение отдельно? Почему другой поток все еще зависит от первого соединения? Любой совет будет принят с благодарностью

Есть много проблем. Одна из них заключается в том, что вы неправильно обнаруживаете ошибки вызовов socket или accept. И это только две из многих проблем.

Some programmer dude 12.08.2024 21:56
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Во-первых, этот код даже отдаленно не похож на жизнеспособный 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) Браузер не может одновременно обрабатывать несколько соединений?

randomize_atk 13.08.2024 07:29

1) Вы ожидаете получить весь HTTP-запрос и только один полный запрос за один recv() вызов. Это просто не так, как работают TCP или HTTP. Вам нужно несколько чтений, и вам придется анализировать данные по пути, чтобы знать, когда запрос был получен полностью и начинается новый запрос. Существует множество вопросов StackOverflow, связанных с чтением TCP-сообщений. 2) один экземпляр браузера может использовать несколько подключений к одному и тому же серверу, но это не гарантируется. Вам может понадобиться несколько браузеров, чтобы делать то, что вы хотите.

Remy Lebeau 13.08.2024 16:39

Другие вопросы по теме