Building a Web Server with CHttpBlockingSocket

If you need a Web server, your best bet is to buy one or to use the Microsoft Internet Information Server (IIS) that comes bundled with Windows NT Server. Of course, you'll learn more if you build your own server and you'll also have a useful diagnostic tool. And what if you need features that IIS can't deliver? Suppose you want to add Web server capability to an existing Windows application, or suppose you have a custom ActiveX control that sets up its own non-HTTP TCP connection with the server. Take a good look at the server code in EX34A, which works under Windows NT, Windows 95, and Windows 98. It might work as a foundation for your next custom server application.

EX34A Server Limitations

The server part of the EX34A program honors GET requests for files, and it has logic for processing POST requests. (POST requests are described in Chapter 35.) These are the two most common HTTP request types. EX34A will not, however, launch Common Gateway Interface (CGI) scripts or load Internet Server Application Programming Interface (ISAPI) DLLs. (You'll learn more about ISAPI in Chapter 35.) EX34A makes no provision for security, and it doesn't have FTP capabilities. Other than that, it's a great server! If you want the missing features, just write the code for them yourself.

EX34A Server Architecture

You'll soon see that EX34A combines an HTTP server, a Winsock HTTP client, and two WinInet HTTP clients. All three clients can talk to the built-in server or to any other server on the Internet. Any client program, including the Telnet utility and standard browsers such as Microsoft Internet Explorer 4.0, can communicate with the EX34A server. You'll examine the client sections a little later in this chapter.

EX34A is a standard MFC SDI document-view application with a view class derived from CEditView. The main menu includes Start Server and Stop Server menu choices as well as a Configuration command that brings up a tabbed dialog for setting the home directory, the default file for blind GETs, and the listening port number (usually 80).

The Start Server command handler starts a global socket listening and then launches a thread, as in the simplified HTTP server described previously. Look at the ServerThreadProc function included in the file \vcpp32\ex34a\ServerThread.cpp of the EX34A project on the companion CD-ROM. Each time a server thread processes a request, it logs the request by sending a message to the CEditView window. It also sends messages for exceptions, such as bind errors.

The primary job of the server is to deliver files. It first opens a file, storing a CFile pointer in pFile, and then it reads 5 KB (SERVERMAXBUF) blocks and writes them to the socket sConnect, as shown in the code below:

char* buffer = new char[SERVERMAXBUF];
DWORD dwLength = pFile->GetLength();
nBytesSent = 0;
DWORD dwBytesRead = 0;
UINT uBytesToRead;
while(dwBytesRead < dwLength) {
    uBytesToRead = min(SERVERMAXBUF, dwLength - dwBytesRead);
    VERIFY(pFile->Read(buffer, uBytesToRead) == uBytesToRead);
    nBytesSent += sConnect.Write(buffer, uBytesToRead, 10);
    dwBytesRead += uBytesToRead;
}

The server is programmed to respond to a GET request for a phony file named Custom. It generates some HTML code that displays the client's IP address, port number, and a sequential connection number. This is one possibility for server customization.

The server normally listens on a socket bound to address INADDR_ANY. This is the server's default IP address determined by the Ethernet board or assigned during your connection to your ISP. If your server computer has several IP addresses, you can force the server to listen to one of them by filling in the Server IP Address in the Advanced Configuration page. You can also change the server's listening port number on the Server page. If you choose port 90, for example, browser users would connect to http://localhost:90.

The leftmost status bar indicator pane displays "Listening" when the server is running.

Using the Win32 TransmitFile Function

If you have Windows NT 4.0, you can make your server more efficient by using the Win32 TransmitFile function in place of the CFile::Read loop in the code excerpt shown. TransmitFile sends bytes from an open file directly to a socket and is highly optimized. The EX34A ServerThreadProc function contains the following line:

if (::TransmitFile(sConnect, (HANDLE) pFile >m_hFile, dwLength, 0, 
    NULL, NULL, TF_DISCONNECT))

If you have Windows NT, uncomment the line

#define USE_TRANSMITFILE

at the top of ServerThread.cpp to activate the TransmitFile logic.

Building and Testing EX34A

Open the \vcpp32\ex34a project in Visual C++, and then build the project. A directory under EX34A, called Website, contains some HTML files and is set up as the EX34A server's home directory, which appears to clients as the server's root directory.

If you have another HTTP server running on your computer, stop it now. If you have installed IIS along with Windows NT Server, it is probably running now, so you must run the Internet Service Manager program from the Microsoft Internet Server menu. Select the WWW Service line, and then click the stop button (the one with the square). EX34A reports a bind error (10048) if another server is already listening on port 80.

Run the program from the debugger, and then choose Start Server from the Internet menu. Now go to your Web browser and type localhost. You should see the Welcome To The Inside Visual C++ Home Page complete with all graphics. The EX34A window should look like this.

Click to view at full size.

Look at the Visual C++ debug window for a listing of the client's request headers.

If you click the browser's Refresh button, you might notice EX34A error messages like this:

WINSOCK ERROR--SERVER: Send error #10054 -- 10/05/96 04:34:10 GMT

This tells you that the browser read the file's modified date from the server's response header and figured out that it didn't need the data because it already had the file in its cache. The browser then closed the socket, and the server detected an error. If the EX34A server were smarter, it would have checked the client's If-Modified-Since request header before sending the file.

Of course, you can test the server on your $99 intranet. Start the server on one computer, and then run the browser from another, typing in the server's host name as it appears in the HOSTS file.

Using Telnet

The Telnet utility is included with Windows 95, Windows 98, and Windows NT. It's useful for testing server programs such as EX34A. With Telnet, you're sending one character at a time, which means that the server's CBlockingSocket::Receive function is receiving one character at a time. The Telnet window is shown here.

The first time you run Telnet, choose Preferences from the Terminal menu and turn on Local Echo. Each time thereafter, choose Remote System from the Connect menu and then type your server name and port number 80. You can type a GET request (followed by a double carriage return), but you'd better type fast because the EX34A server's Receive calls are set to time-out after 10 seconds.