Asynchronous IO under ISAPI is often viewed as something of a black art. But it's actually quite straight forward and it will yield the highest level of performance and scalability for your application.
A Project to Demonstrate Asynchronous IO
Browsing the Code Project site, I stumbled across an example ISAPI project by Jorge Lodos Vigil which demonstrated sending back a page of HTML and an image. Two different types of data are returned by Jorges' ISAPI. The page is contained in an in-memory buffer while image is contained in a file on disk. Sending a buffer of data to a client and sending a file to a client just happens to encompass the two possible ways to send data asynchronously using the ISAPI API -
ImageServer ISAPI extension essentially brokers requests for images from some hidden directory.
ImageServer satisfies those requests by returning a page of HTML containing an
IMG element that has, as its source, the
ImageServer extension. The source URL is specially formatted to let
ImageServer know that it should send back the actual image instead of a containing page.
Two Ways to Perform Asynchronous IO
Within the context of an ISAPI module, there are two ways to receive or send data asynchronously. You can use
WriteClient to read/write an arbitrary buffer of data to the client asynchronously or you can use
TransmitFile to send a file to a client asynchronously.
TransmitFile is a Win32 function which is accessed by using the ISAPI function
ServerSupportFunction and specifying the
The MSDN online library provides a good overview of asynchronous IO but in general, you will need to perform these steps:
- Setup a callback through which you will be notified when an IO completion event occurs.
- Start the asynchronous IO.
HSE_STATUS_PENDING from the
- When IO completes, cleanup any context information and notify IIS you're done with this session.
There's really only one other thing to keep in mind about asynchronous IO. That is, whatever data is being sent to the client must survive until a completion notification is received. Which means, it's almost always going to be placed on the heap.
This last requirement usually entails the most grief from a design standpoint. The extreme levels of performance and reliability required from ISAPI extensions precludes a design that constantly accesses the heap. Limiting access to the heap is often accomplished by implementing pools of buffer objects that are re-used.
ImageServers requirements are not as stringent. When
ImageServer needs a buffer, or transmission context, it simply creates it on the heap. Likewise, when it's finished with a buffer, it just destroys it. Although creating a protected buffer pool is not difficult, it would distract from the central purpose of
ImageServer - which is to demonstrate basic asynchronous IO.
Tour of the Code
The code is simple and heavily commented so it should be easy to understand. Here are the highlights of what you'll find.
- There is one main class
CImageServer which encapsulates all the functionality required for this application. This class is declared in Server.h and implemented in Server.cpp.
- A single static instance of
CImageServer resides in ImageServer.cpp. Also in this file are the main entry points for the ISAPI DLL -
HttpExtensionProc. They are all basic boiler plate code except
HttpExtensionProc which delegates its behavior
- There are two other classes which serve as transmission contexts for the asynchronous IO calls. The first is
CFilePacket which is a context for
TransmitFile. The second is
tWriteClientPacket, which is actually a structure not a class. It serves as a context for, you guessed it,
tWriteClientPacket are declared as nested classes/structures inside the declaration of
CImageServer. There was no real need for this but it makes the code a little cleaner as those classes are only used inside the functions of
CImageServer works is best accomplished by reading the source. Everything starts with
CImageServer::ServiceRequest and the comments will guide you from there.
As with any ISAPI module, you'll have to locate the DLL in some path your web server has access too - with execute permissions. You'll also need a directory containing images that matches whatever you've hard coded into
About the Author
The company I work for, Financial Insight Systems Inc., specializes in creating high performance internet applications using Microsoft Back Office products. I have a MCSD, with certifications in Visual C++ and SQL Server, and for the last five years, most of my time has been spent developing ISAPI modules and Winsock based client/server solutions. Suggestions and comments are welcome.
This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.
A list of licenses authors might use can be found here.