






Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
A step-by-step guide on creating a multi-threaded http server using winsock library in c. It covers socket creation, binding, listening, accepting client connections, and handling multiple clients concurrently. The server uses event objects and semaphores for thread synchronization.
Typology: Study notes
1 / 12
This page cannot be seen from the preview
Don't miss anything!







Server architecture will be based on:
Initialize Windows Sockets Library
if(WSAStartup(MAKEWORD(1,1), &wsaData)) { … … … return 1; }
//Get machine’s hostname and IP address
gethostname(hostName, sizeof(hostName)); ptrHostEnt = gethostbyname(hostName);
//Fill the socket address with appropriate values
serverSocketAddress.sin_family = AF_INET; serverSocketAddress.sin_port = htons(SERVER_PORT); … … … memcpy(&serverSocketAddress.sin_addr.S_un.S_addr, ptrHostEnt->h_addr_list[0], sizeof(unsigned long));
Create the server socket
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(serverSocket == INVALID_SOCKET) { … … … WSACleanup(); return 1; }
Bind the socket
if(bind(serverSocket, (struct sockaddr *)&serverSocketAddress, sizeof(serverSocketAddress))) { … … … WSACleanup(); return 1; }
Put the socket in listening mode
if(listen(serverSocket, MAX_PENDING_CONNECTIONS)) { … … … WSACleanup(); return 1; }
Here is the time to accept client connections
Create a thread that will call accept() in a loop to accept multiple client connections
hAcceptingThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) acceptClientConnections, NULL, CREATE_SUSPENDED, &dwAcceptingThread);
Create a thread to do termination house-keeping when some communication thread terminates.
hTerminatingThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) terminateCommunicationThreads, NULL, CREATE_SUSPENDED, &dwTerminatingTThread);
Application Variables and constants
#define MAX_CLIENTS 5 SOCKET clientSockets[MAX_CLIENTS];
HANDLE hCommunicationThreads[MAX_CLIENTS]; DWORD dwCommunicationThreads[MAX_CLIENTS];
HANDLE hAcceptingThread; DWORD dwAcceptingThread;
HANDLE hTerminatingThread; DWORD dwTerminatingTThread;
servClient Communication thread routine
HTTP request served going to disconnect the client
Set an Event object to indicate termination
Communicate with client to receive/serve its HTTP request Use recv() / send() blocking WinSock API calls
Gracefully shutdown and Close client socket
terminateCommunicationThreads thread routine
Thread Procedures Summary
acceptClientConnections
Wait for ANY thread termination event WaitForMultipleObjects(…, hEventsThreadTermination,…);
Wait for thread routine to finish (its object will get signalled) WaitForSingleObject(hCommunicationThreads[i], …);
Close thread handle; Make it NULL; Set its socket to invalid ReleaseSemaphore();
At least one thread sets its termination event
The thread function has actually finished
memcpy(&serverSocketAddress.sin_addr.S_un.S_addr, ptrHostEnt->h_addr_list[0], sizeof(unsigned long));
Create the server socket
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(serverSocket == INVALID_SOCKET) { … … … WSACleanup(); return 1; }
Bind the socket
if(bind(serverSocket,(struct sockaddr *)&serverSocketAddress, sizeof(serverSocketAddress))) { … … … WSACleanup(); return 1; }
Put the socket in listening mode
if(listen(serverSocket, MAX_PENDING_CONNECTIONS)) { … … … SACleanup(); return 1; }
Here is the time to accept client connections
Limiting Maximum Concurrent connections
Create an unnamed semaphore object with MAX_CLIENTS as initial/maximum count
hSemaphoreMaxClients = CreateSemaphore(NULL, MAX_CLIENTS, MAX_CLIENTS, NULL );
“I am dying…”, the thread said
Create an array of non-signalled event objects
for(i=0; i<MAX_CLIENTS; i++) hEventsThreadTermination[i] = CreateEvent(NULL, FALSE, FALSE, NULL);
Create the connection-accepting thread
hAcceptingThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE) acceptClientConnections, NULL, CREATE_SUSPENDED, &dwAcceptingThread);
Create the termination house-keeping thread
hTerminatingThread = CreateThread(… … …);
Display the dialog
DialogBox(…, …, …, mainDialogProc);
Main Dialog Proc
case WM_INITDIALOG: ResumeThread(hAcceptingThread); ResumeThread(hTerminatingThread); return TRUE; break;
Handling the server shut-down button
case IDC_BUTTON_SHUTDOWN: //Perform any shut-down tasks that my be necessary EndDialog(hDlg, 0); break;
Accept Client Connections Thread Routine
Start of the loop to accept client connections Wait for semaphore count to go non-zero
dwWaitResult = WaitForSingleObject( hSemaphoreMaxClients, INFINITE);
switch(dwWaitResult) { case WAIT_OBJECT_0:
Sample Request
Request parsing: understanding what the client has demanded GET /courses/win32.html HTTP/1. Assume F:\ is your server’s home directory, and \courses\is not a virtual directory, server should return the file F:\courses\win32.html
HTTP Redirection Redirecting the client irrespective of the HTTP request!
The string in the #define directive is assumed to be on a single line #define RESPONSE "HTTP/1.1 302 Object Moved\r\n Location: http://www.vu.edu.pk\r\n\r\n"
Sending the hard-coded HTTP response back to browser
send(clientSockets[(UINT)clientNumber], RESPONSE, sizeof(RESPONSE), 0);
Using Port Numbers
There is no compulsion to build all HTTP Web Servers to run on port 80. These are ‘suggested’ port numbers for a Win32 developer Standard servers do run on port 80. Our HTTP Web Server may also need to run on port 80 if put it to public use
Returning HTML Document
#define directive is assumed to be on a single line #define RESPONSE "HTTP/1.0 200 OK\r\n
Content-type: text/html\r\n Content-length: 1325\r\n\r\n" Send the hard-coded HTTP status and headers
send(clientSockets[(UINT)clientNumber], RESPONSE, sizeof(RESPONSE), 0); //Now sends the whole file using character I/O of standard C runtime ch = fgetc(fptr); while(!feof(fptr)) { send(clientSockets[(UINT)clientNumber], &ch, 1, 0); ch = fgetc(fptr);
terminateCommunicationThreads thread routine
Wait for some thread to set a termination event
dwWaitResult = WaitForMultipleObjects(MAX_CLIENTS, hEventsThreadTermination, FALSE, INFINITE); //Get the array index
threadIndex = dwWaitResult - WAIT_OBJECT_0; //Wait for the thread to actually terminate
WaitForSingleObject( hCommunicationThreads[threadIndex], INFINITE); Close handles and set variables to initial values again
CloseHandle(hCommunicationThreads[threadIndex]); hCommunicationThreads[threadIndex] = NULL; clientSockets[threadIndex] = INVALID_SOCKET;
//Resource freed, increase the semaphore value
ReleaseSemaphore(hSemaphoreMaxClients, 1, NULL);
A Flawed Web Server
Fixed sized arrays waste memory and lack run-time flexibility One event per thread to signify termination: WaitForMultipleObjects cannot wait on more than a certain number of objects e.g. 64 on x86 under NT.
Dynamic Web Content
Server blindly dumps HTML files to the clients. This is ‘static content’.
Server reads file and modifies its output e.g.
%%time%% replaced with current system time Every 2 clients connected at different instants of time will receive different content. This is ‘dynamic content’. %%time%% may be called a tag
Microsoft Active Server Pages Macromedia ColdFusion Tags are not sent to the client. These are processed by the server and the resulting output is sent to the browser.