The C++ framework for developing highly scalable, high performance servers on Windows platforms.

Example Servers - Echo Server Read Timeout

This example shows you how to build a server which imposes a timeout on the overlapped reads that it issues. If you've looked at the documentation for WSARecv() you'll realise that is harder than it sounds as WSARecv() does not provide this functionality. The basic structure of this server is very similar to the Basic Echo Server example and you should go and read about that first and have a good understanding of how everything fits together. This document will only cover the differences between the Basic Echo Server example and this example.

This example is shipped with all licensed versions of The Server Framework and it requires the core server framework libraries (see here for licensing options). You can always download the latest version of this example from here; and although you will need the correct libraries to be able to build it you can look at the example code and see how it works and perhaps get ideas from it. A compiled, unicode release, build of this example is available on request if you require it for performance analysis of the framework.

We implement the read timeout using JetByteTools::Socket::CReadTimeoutStreamSocketConnectionFilter which is an instance of JetByteTools::Socket::IFilterStreamSocketConnections. We discuss connection filters here. Bear in mind that the way we solve the problem in this example server is just one way to solve the problem and it may not be the most performant option available, but for servers which only need to deal with a few thousand connections it works pretty well.

Our ServerMain.cpp is identical to the Basic Echo Server, but our CSocketServer class contains instances of JetByteTools::Socket::CReadTimeoutStreamSocketConnectionFilter and JetByteTools::Win32::CThreadedCallbackTimerQueue as member variables and inherits from JetByteTools::Socket::CReadTimeoutStreamSocketConnectionFilter::TimerCallback in addition to the base classes used in previous examples. During construction we wire up the connection filter and the timer queue and register the filter with the server base class.

 CSocketServer::CSocketServer(
    const string &welcomeMessage,
    const IFullAddress &address,
    const ListenBacklog listenBacklog,
    IIOPool &pool,
    IAllocateStreamSockets &socketAllocator,
    IAllocateBuffers &bufferAllocator,
    ILimitConnections &connectionLimiter)
    :  CStreamSocketServerEx(address, listenBacklog, *this, pool, socketAllocator, bufferAllocator, NoZeroByteRead, connectionLimiter),
       m_timeoutFilter(*this, socketAllocator, m_timerQueue),
       m_welcomeMessage(welcomeMessage)
 {

 }

As you can see, this isn't especially complex. The filter takes care of registering itself and the timer queue's default constructor is all we need.

 void CSocketServer::OnConnectionEstablished(
    IStreamSocket &socket,
    const IAddress & /*address*/)
 {
    Output(_T("OnConnectionEstablished"));

    socket.Write(m_welcomeMessage.c_str(), GetStringLengthAsDWORD(m_welcomeMessage));

    m_timeoutFilter.SetReadTimeout(socket, 1000, *this, 1);

    socket.TryRead();
 }

During connection establishment we call SetReadTimeout() on the connection filter and this sets a reoccurring timer on the connection. The timer is set after every read completes on the connection and when the timer goes off (i.e. when a read does not complete within the given timeout) the OnTimer() method of the timer callback is called. and the user data (in this case the constant value 1) is passed to the callback. Our implementation of OnTimer() looks like this.

 void CSocketServer::OnTimer(
    IStreamSocket &socket,
    TimerCallback::UserData userData)
 {
    if (userData < 5)
    {
       const string message = "Read timeout! " + ToStringA(userData) + "\r\n";

       socket.Write(message.c_str(), GetStringLengthAsDWORD(message));

       const Milliseconds timeout = static_cast<Milliseconds>(userData);

       m_timeoutFilter.SetSingleReadTimeout(socket, 1000 * timeout, *this, timeout + 1);
    }
    else
    {
       const string message = "Too many timeouts!\r\n";

       socket.Write(message.c_str(), GetStringLengthAsDWORD(message));

       socket.Shutdown(ShutdownSend);
    }
 }

This displays a message to the user and then backs off the timer by 1 second. It does this five times and then disconnects the user with another message. Note that this code uses SetSingleReadTimeout() which means that the timer is not reset automatically when a read completes. This means that if you send data after the timer has gone off once or twice then the timer is reset back to a one second timer as the reoccurring timer is used.
The best way to see this server in action is to use telent and connect to the port that it is listening on. Type some data and then stop and watch the timeout messages, type more data and see them reset to 1, etc.

Generated on Sun Sep 12 19:06:45 2021 for The Server Framework - v7.4 by doxygen 1.5.3