c++socketswinsock2

Socket in C++ : Handle multiple concurrent request in Windows OS


I try to build a multi thread server so when there is multiple concurrent request, the server can handle them at the same time. But, my code still require for waiting first request to be done before starting second request.

Below is my code

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;
}

If I do 3 GET request to the server, the first request will require 3 seconds, second request require 6 seconds and third request 9 seconds. Isn't that the thread should handle the connection separately? Why the other thread still depending on the first connection? Any advise will be greatly appreciated


Solution

  • First off, this code is not even remotely close to being a viable HTTP server, as you are not receiving and parsing the HTTP requests correctly.

    But, aside from that, although you are handling multiple connections ok (although using detached threads is questionable), an HTTP 1.x connection simply cannot handle more than 1 request at a time. That is just how the HTTP protocol works, at least prior to HTTP/2 with its multiplexing capabilities. For HTTP 1.x, to run multiple requests in parallel, you need multiple connections, but browsers tend to pool their connections and are not guaranteed to use multiple connections to the same server at a time, and you can't force a browser to do so.

    Something else to consider - even if you did have multiple connections at a time, your client thread has a busy loop that doesn't yield CPU time to other threads. You should use std::this_thread::sleep_for() or std::this_thread::sleep_until() instead to put the calling thread to sleep and allow other threads a chance to run in the meantime, eg:

    if(method == "GET") {
        //simulate heavy task with delay
        std::this_thread::sleep_for(chrono::seconds(3));
        /*...*/
    }