Multithread server class with example of HTTP server

Environment: VC6 SP5, NT4/2000

This article covers the following topics

  • Win32 Threads
  • Synchronization objects
  • Asynchronous sockets
  • Basic HTTP protocol
  • Using abstract classes
  • STL

Introduction

When writing a socket server, I had to choose between using a Single Thread technique and using a Multithread technique. I discovered the Multithread technique has several advantages, including the following:

  • No effort is required to overcome with 64 handles when using WSAWaitForMultipleObjects(...).
  • No effort is required to overcome passing through big files to the client without delaying other requests.

Although these advantages make Multithread Servers appropriate in many situations, they do not justify the use of Multithread Servers always and everywhere. For example, if a computer carries a server component that is not very powerful, using a Static Thread solution may be the best choice. The following article describes an implementation of a Multithread Server based on Asynchronous sockets. I built a web server as an example using my server class as a base. The implementation of HTTP is just to serve basic HTTP requests. So let's go through some details of implementation.

Implementation

There are number of threads that are always alive and waiting or processing something. The first important thread is the AcceptThread. Its responsibility is to initialize socket library and listen to all incoming connections. Each time a new connection arrives a new thread is created. This new thread is called the ClientThread, and its responsibility is to take care of the needs of the newly connected client. When the client has disconnected, the request to close the thread's handle will be passed to the HelperThread through the HandleList object.

Next I'll describe what some of the important functions in the CGenericServer class is designed for. The functions appear in the order in which they are implemented in sources.

  • GetStats(...)
    Returns useful statistics about how much traffic is present, how many visitors with unique IP addresses have been connected, how many total requests to the server have taken place, and how many clients are currently connected to the server.


  • AddClient(...)
    Notifies the derived class about new connection arrivals, and creates a working thread for serving child socket needs.


  • Run(...)
    Launches Accept and Helper threads.


  • Shutdown(...)
    Kills Accept and Helper threads and waits until ClientThreads also are killed.


  • Reset(...)
    Resets statistics values to 0.


  • CleanupThread(...)
    Cleans up all opened handles and allocated memory used by the ClientThread. The ID of the thread passed to the HelperThread for further termination using this function. Used solely by ClientThread.


  • CleanupThread(...)
    Closes main listen socket and some handles. Used by AcceptThread only.


  • AcceptThread(...)
    Main working thread that serves all incoming connections.


  • ClientThread(...)
    Serves all established client connections.


  • HelperThread(...)
    If I'm not using _endthread(...), someone should close the threads handle after it is no longer alive. That function is assigned to the HelperThread.



The following abstract functions should be implemented if you derive your class from the CGenericServer:

  • IsComplete(...)
    This function is very important to implement carefully. You have to determine whether the whole message is in the buffer or not based on the data you have when this function has been called. In my example of the HTTP server, the condition is double \r\n.


  • ParseRequest()
    This is also a very important function. It will provide you with the data received from the client and must take response data from you. If you return a false, connection will be closed immediately, and the thread will be killed. Otherwise, it will remain opened, unless you assign the KeepAlive variable as 1. You will find basic HTTP implementation in my example.


  • GotConnection(...)
    This will give you the IP address and port number of the connected client.


  • DataSent(...)
    This should inform you each time data has been sent to the client, and gives you the number of bytes sent.


Additional

My HTTP server supports two of the most common HTTP errors:

  • 404 Resource not found
  • 504 Method not implemented

Each of these errors have corresponding html files. The names of those files are currently hard coded in HTTPServer.h. These files must be in the home directory of the server.

Also I've added several MIME types to the CHTTPServer class, so browsers can recognize those types. Of course there are many more types available.
A simple CLog class has been used to log any errors that may occur. The log file will be placed in the Windows directory, and the filename is "UMServer.log" by default.

This is not a fully featured HTTP server and does not implement all the functionalities of the HTTP protocol, as this was not the main objective of this project.

Downloads

Download demo project - 39 Kb
Download source - 10 Kb


Comments

  • problem..

    Posted by Legacy on 07/24/2002 12:00am

    Originally posted by: photon

    When I telneted to localhost 80 and typed just one symbol,  programm died with an exception... Do you have any ideas?
    
    

    Regards.


    ---------------------

    P.S.

    Wuff I found the problem...
    It is here...


    BOOL CHTTPServer::IsComplete(string szRequest)
    {
    //----------------- SHOULD ADD THIS----
    if(szRequest.size()<4) return FALSE;
    //---------------------------------

    if(szRequest.substr(szRequest.size() - 4, 4) == "\r\n\r\n")
    return TRUE;
    else
    return FALSE;
    }

    Reply
  • if you have some about CWinThread and CAsynchrosocket

    Posted by Legacy on 04/26/2002 12:00am

    Originally posted by: getter

    it will be prefect

    Reply
  • HOW CAN I USE SOCKS5 UDP PROXY PROTOCOL

    Posted by Legacy on 12/28/2001 12:00am

    Originally posted by: xyqmouse

    Hello:
    Can you tell me the detail of using socks v5 udp
    proxy protocol.
    Thanks a million!

    Reply
  • Good for a tutorial bu the solution doesn't scale.

    Posted by Legacy on 12/04/2001 12:00am

    Originally posted by: J.D.

    This solution will not scale under any amount of real-world pressure. One thread per client is not the recommended solution under Windows. If anything overlapped I/O and I/O completion ports should be used to handle asynchronous I/O under WinNT/2000/XP. Under Win9x/Me you should use overlapped I/O and the I/O callback functionality provided by the WSAxxx line of calls.

    Reply
  • Shouldn't use MFC and _beginthreadex

    Posted by Legacy on 10/10/2001 12:00am

    Originally posted by: Robin Hilliard

    hi -

    The code presented here uses the native Win32 thread primitives which are not compatible with MFC, or at least which will ultimately cause trouble with MFC. The reasons are obscure, but you are much better off using CWinThread with C++/MFC and _beginthread(ex) with native C code. Check the MFC source to CWinThread::CreateThread() to see what's being done for you.

    Also, you are using TerminateThread() to kill locked threads. This should never be necessary -- the documentation states that kernel objects are likely to be left in an indeterminate state, as well as the more obvious problems of C++ objects being left undestructed and WinSock left with possible open connections. It's better to design the code so that TerminateThread() is not used (particularly when the timeout is as short as 30 seconds).

    - robin.

    Reply
  • WWW server

    Posted by Legacy on 10/08/2001 12:00am

    Originally posted by: Andy Bantly

    That's good clean code. Would you mind posting the whole project, resources and all. Some may not realize that they would need to link to ws2_32 libraries. It would also be benificial to see your message handling mechanism, as it is apparent that you are using mfc's message handlers for these. My socket experience has grown out of win32 handling, and I would greatly appreciate seeing another technique for this. Regards, Andy Bantly

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: September 10, 2014 Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild." This loop of continuous delivery and continuous feedback is how the best mobile …

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds