How To Set Timeouts on WebSocket Operations

This topic shows how to set timeouts on WebSockets operations to limit the time to wait for an operation to complete. The MessageWebSocket and StreamWebSocket classes uses an internal system service to send WebSocket client requests and receive responses from a server. The default timeout value used for a WebSocket connect operation is 60 seconds. If the HTTP server that supports WebSockets is temporarily down or blocked by a network outage and the server doesn’t or can’t respond to the WebSocket connection request, the internal system service waits the default 60 seconds before it returns an error which causes an exception to be thrown on the WebSocket ConnectAsync method. If the name query for an HTTP server name in the URI returns multiple IP addresses for the name, the internal system service tries up to 5 IP addresses for the site each with a default timeout of 60 seconds before it fails. An app making a WebSocket connection request could wait several minutes trying to connect to multiple IP addresses before an error is returned and an exception is thrown. This behavior could appear to the user as if the app stopped working. The default timeout used for send and receive operations after a WebSocket connection has been established is 30 seconds.

To make the app more responsive and minimize these issues, an app could set a shorter timeout on MessageWebSocket and StreamWebSocket connect requests so that the operation fails sooner from the timeout rather than from the default settings.

The basic model to set a timeout on WebSocket operations is the same for both types of WebSockets. The discussion below uses a StreamWebSocket as an example, but the same process can be used with a MessageWebSocket.

The following example creates one task that completes after a specified delay, and creates a second rask that cancels a task after a specified delay. These classes can be used with the StreamWebSocket and the MessageWebSocket classes when trying to establish a connection to set a specific timeout. An example usage would be calling the StreamWebSocket.ConnectAsync method in a task with a cancellation_token_source that supports cancellation. If timeout completes first, then the cancellation_token_source is used to cancel the task for the WebSocket connect operation.

    #include <agents.h>
    #include <ppl.h>
    #include <ppltasks.h>

    using namespace concurrency;
    using namespace std;

    // Creates a task that completes after the specified delay.
    task<void> complete_after(unsigned int timeout)
    {
        // A task completion event that is set when a timer fires.
        task_completion_event<void> tce;

        // Create a non-repeating timer.
        shared_ptr<timer<int>> fire_once(new timer<int>(timeout, 0, nullptr, false));
        
        // Create a call object that sets the completion event after the timer fires.
        shared_ptr<call<int>> callback(new call<int>([tce](int)
        {
            tce.set();
        }));

        // Connect the timer to the callback and start the timer.
        fire_once->link_target(callback.get());
        fire_once->start();

        // Create a task that completes after the completion event is set.
        task<void> event_set(tce);

        // Create a continuation task that cleans up resources and
        // and return that continuation task.
        return event_set.then([callback, fire_once]()
        {
        });
        
    }

    // Cancels the provided task after the specifed delay, if the task
    // did not complete.
    template<typename T>
    task<T> cancel_after_timeout(task<T> t, cancellation_token_source cts, unsigned int timeout)
    {
        // Create a task that returns true after the specified task completes.
        task<bool> success_task = t.then([](T)
        {
            return true;
        });
        // Create a task that returns false after the specified timeout.
        task<bool> failure_task = complete_after(timeout).then([]
        {
            return false;
        });

        // Create a continuation task that cancels the overall task  
        // if the timeout task finishes first. 
        return (failure_task || success_task).then([t, cts](bool success)
        {
            if (!success)
            {
                // Set the cancellation token. The task that is passed as the 
                // t parameter should respond to the cancellation and stop 
                // as soon as it can.
                cts.cancel();
            }

		        // Return the original task. 
		        return t;
        });
    }  

See also

Xbox Live Services API Reference

MessageWebSocket

StreamWebSocket

Windows.Networking.Sockets