Maintaining per-connection state
The final thing that our server may need to do is associate some internal server state with a particular socket connection, the Socket class makes this particularly easy as it provides the following member functions:
void *GetUserPtr() const;
void SetUserPtr(void *pData);
unsigned long GetUserData() const;
void SetUserData(unsigned long data);
These provide access to a single void * user data pointer which is stored in the Socket. The common usage pattern for this user data is as follows: When the connection is established the socket server is notified by OnConnectionEstablished(), the server can allocate a new per-connection data structure and associate it with the socket passed to OnConnectionEstablished() by calling SetUserPtr() in subsequent read and write completions the pointer to the per-connection user data structure can be extracted with GetUserPtr(). When the connection is terminated the server is notified by OnConnectionClosed and the per-connection user data can be retrieved and deleted.
Although there are two versions of the user data access functions, one for a void * and one for an unsigned long there is only a single storage location. The two versions are merely for convenience and to reduce casting if the user data required is simply an index into an internal server structure rather than a pointer.
The example server marshals the OnConnectionEstablished() and OnConnectionClosed() calls across to the business logic thread pool and maintains some fairly trivial per-connection user data there. The data we maintain is the address of the client connection (obtained from the buffer passed into OnConnectionEstablished() and the number of messages that have been processed on this particular connection.
The complete example
The shell of a POP3 server which performs its business logic processing in a seperate thread pool to its IO can be downloaded from here . The server has a call to ::Sleep() within its message processing code so that the processing takes some time and blocks. Notice how the IO on other connections is unaffected by this, and, if you want, add a similar call to the server we developed at the end of the last article and compare the behavior.
As with the other examples, simply telnet to localhost 5001 to test the server. The server runs until a named event is set and then shuts down. The very simple Server Shutdown program, available here , provides the off switch.
Download
The following source was built using Visual Studio 6.0 SP5 and Visual Studio .Net. You need to have a version of the Microsoft Platform SDK installed
Note that the debug builds of the code waste a lot of CPU cycles due to the the debug trace output. It's only worth profiling the release builds.
Download SocketServer4.zip - a dummy POP3 server with a business logic thread pool
Download ServerShutdown.zip - an off switch for the servers
Revision history
- 21st May 2002 - Initial revision.
- 27th May 2002 - Added pause/resume functionality to all servers and the server shutdown program. Use
CSocket
to protect from resource leaks when creating the listening socket. Refactored theSocket
andCIOBuffer
classes so that common list management code is now inCNodeList
and common user data code is now inCOpaqueUserData
. - 29th May 2002 - Linting and general code cleaning
- 18th June 2002 - Removed call to
ReuseAddress()
during the creation of the listening socket as it not required - Thanks to Alun Jones for pointing this out to me. - 28th June 2002 - Adjusted how we handle socket closure.
- 30th June 2002 - Removed the requirement for users to subclass the socket server's worker thread class. All of the work can now be done by simply subclassing the socket server class.
- 15th July 2002 - Socket closure notifications now occur when the server shuts down whilst there are active connections. SocketServer can now be set to ensure read and write packet sequences.
- 23rd July 2002 - Bug fix to
CSocketServer::ProcessDataStream()
. We were reusuing the buffer when we shouldn't have been. Code was fine up until the changes on 30th June and is fine again now. Thanks to an anonymous CodeProject reader for pointing this out to me. - 6th August 2002 - Fixed download link. Thanks to Marco Marigo for reporting the problem.
- 12th August 2002 - Removed the race condition in socket closure - Thanks to David McConnell for pointing this out. Derived class can receive connection reset and connection error notifications. Socket provides a means to determine if send/receive are connected. Dispatch to the thread pool now uses shared enums rather than hard coded constant values. General code cleaning and lint issues.
Comments