<Previous Lesson

Visual Programming

Next Lesson>

Lesson#30

Network Programming-4

30.1 SERVER ARCHITECTURE 2

30.2 HTTP WEB SERVER APPLICATION 2

30.3 VARIABLE INITIALIZATION 7

30.4 INITIALIZE WINSOCK LIBRARY 7

30.5 WIN32 ERROR CODES 7

30.6 HTTP WEB SERVER APPLICATION 7

SUMMARY 13

EXERCISES 13

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

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

Network Programming Part IV 3

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

Network Programming Part IV 4

Accept Client Connection (Thread Routine)

Terminate communication threads (thread routine)

accept()

Create a new

communication thread:

serveClient

serveClient

Client Socket Descriptor

The newly generated

Communication Thread

(discussed later)

Wait for some thread

termination event

Wait for thread

object to go

signalled

Close thread handle;

Destroy its relevant

stored data;

Network Programming Part IV 5

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

Network Programming Part IV 6

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

Network Programming Part IV 7

30.3 Variable Initialization

for(i=0; i<MAX_CLIENTS; ++i)

{

clientSockets[i] = INVALID_SOCKET;

hCommunicationThreads[i] = NULL;

dwCommunicationThreads[i] = 0;

hEventsThreadTermination[i] = NULL;

}

30.4 Initialize WinSock

if(WSAStartup(MAKEWORD(1,1), &wsaData))

{

MessageBox(NULL,

"Error initialising sockets .",

"WinSock Error",

MB_OK | MB_ICONSTOP);

return 1;

}

30.5 Win32 Error Codes

int WSAGetLastError(void);

- get error code for the last unsuccessful Windows Sockets operation

DWORD GetLastError(VOID);

- retrieve calling threads last-error code

30.6 HTTP Web Server Application

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

Network Programming Part IV 8

… … …

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

);

Network Programming Part IV 9

“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:

Network Programming Part IV 10

We can accept more connections here because semaphore object is signaled

clientSocket = accept(… … …);

clientSocket = accept(… … …);

Connection accepted! Look for the first empty slot to save the new socket descriptor

for(i=0; i<MAX_CLIENTS; i++)

{

if(clientSockets[i] == INVALID_SOCKET)

break;

}

nextClientIndex = i;

clientSockets[nextClientIndex]=clientSocket;

nextClientIndex is used as an index in ALL arrays to store information relevant to this

new client connection

clientSockets[nextClientIndex]=clientSocket;

hCommunicationThreads[nextClientIndex] = CreateThread(…, …, serveClient,

//thread procedure

(LPVOID)nextClientIndex, thread parameter

CREATE_SUSPENDED,

…);

Index for this client in all arrays is passed to this thread routine

DWORD WINAPI serveClient(LPVOID clientNumber)

{

char msg[2046] = "";

Receiving an HTTP request from browser

recv( clientSockets[(UINT)clientNumber], msg,2046,0);

//nextClientIndex is used as an index in ALL arrays to store information relevant to this

//new client connection

clientSockets[nextClientIndex]=clientSocket;

hCommunicationThreads[nextClientIndex] = CreateThread(…, …, serveClient,

(LPVOID)nextClientIndex,thread parameter, CREATE_SUSPENDED,…);

Network Programming Part IV 11

Sample Request

Request parsing: understanding what the client has demanded GET /courses/win32.html

HTTP/1.0

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

Network Programming Part IV 12

}

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.

Network Programming Part IV 13

CGI

CGI is Common Gateway Interface. Win32 executable execute by the server. All browser

request data is available at stdin (read using scanf() etc.) and all output sent to stdout

(output using printf etc.) is sent to the browser instead of the server screen.

Summary

In this lecture, we designed a web server which listens on port 80 and can receive

requests from the clients and send message to the client. Our server supports maximum

five clients at a time.

Note: For more on Windows Programming, connect to the Virtual University resource

online. Examples, source codes can be found online.

Exercises

1. Practise to create such applications as explained in this lecture and in previous

lectures with different ideas.

<Previous Lesson

Visual Programming

Next Lesson>

Home

Lesson Plan

Topics

Go to Top

Next Lesson
Previous Lesson
Lesson Plan
Topics
Home
Go to Top