Network Programming IV: Creating a Multi-Threaded HTTP Server, Study notes of Windows Programming

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

2011/2012

Uploaded on 08/07/2012

anishay
anishay 🇮🇳

4.2

(25)

118 documents

1 / 12

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Network Programming Part IV 2
30.1 Server Architecture
Server architecture will be based on:
Dialog-based GUI application
Most of the processing is at back-end
Running on TCP port 5432 decimal
30.2 HTTP Web Server Application
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
docsity.com
pf3
pf4
pf5
pf8
pf9
pfa

Partial preview of the text

Download Network Programming IV: Creating a Multi-Threaded HTTP Server and more Study notes Windows Programming in PDF only on Docsity!

30.1 Server Architecture

Server architecture will be based on:

  • Dialog-based GUI application
  • Most of the processing is at back-end
  • Running on TCP port 5432 decimal

30.2 HTTP Web Server Application

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

  • to accept client connection •terminateCommunicationThreads
    • to do housekeeping when communication threads terminate •serveClient
  • to do actual communication to receive and serve an HTTP request

30.1 Server Shut down user interface

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.