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
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));
/*...*/
}