30.1 S ERVER
ARCHITECTURE
2
30.2 HTTP W EB
SERVER
APPLICATION
2
30.3 V ARIABLE
INITIALIZATION
7
30.4 I NITIALIZE
WINSOCK
LIBRARY
7
30.5 W IN32
ERROR
CODES
7
30.6 HTTP W EB
SERVER
APPLICATION
7
S UMMARY
13
E XERCISES
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.
|