Search code examples
c#windows-runtimewindows-store-appstask-parallel-librarywinrt-async

Task not cancelled after a specified timeout


I'm using a StreamSocketListener to await a connection on a port. I don't want it to listen forever, it must cancel after a specific number of seconds, for which I'm using the following code.

 remoteListener = new StreamSocketListener();
    try
    {
        CancellationTokenSource ctsTimeout = new CancellationTokenSource();
        ctsTimeout.CancelAfter(1000); // in milliseconds
        await remoteListener.BindServiceNameAsync(receivingPortForRemoteRequests.ToString()).AsTask(ctsTimeout.Token);
        remoteListener.ConnectionReceived += remoteListener_ConnectionReceived;
    }
    catch (Exception exc) // supposed to produce a TaskCanceledException
    {
        isCancelled = true;
    }

My problem is that this code never throws the Exception after any interval of time, but just keeps listening. The code is based on what I'd found from this MSDN page.

Does anyone know what I'm doing wrong ? Thanks in advance!


Solution

  • I would say that the primary mistake you have made is that you are passing the cancellation token to the task that binds the socket, and not any operation that actually listens. The binding operation simply assigns a port to the socket, and generally will complete within milliseconds at the worst, quite likely faster in the typical case. There's no way this operation would ever still be in progress after a full second.

    Without a good Minimal, Complete, and Verifiable example that clearly illustrates your question, it's impossible to provide a thorough answer. However, some suggestions:

    1. First, don't bother using the cancellation token. It's not how you should stop a socket from listening. Instead, just close the socket after the requisite time. You can use a timer for this purpose, or an async method that first calls await Task.Delay(...) and then closes the socket.
    2. In the future, if you do have a scenario where using a cancellation token is appropriate, you should catch only TaskCanceledException. Never use catch (Exception) for routine exception handling; the only place it's appropriate is for scenarios where you intend to simply log or otherwise report the exception and then terminate the process. Otherwise, catch only the exceptions you expect and for which you have a good plan for handling.
    3. You should subscribe to the ConnectionReceived event before you bind the socket. Otherwise, there is a chance (very small, granted) that a connection attempt would be made before your code is ready to be notified via the event.

    The first and third points above are addressed in the MSDN documentation, which has a useful summary of the proper use of this class. From the documentation for StreamSocketListener:

    The typical order of operations is as follows:
    • Create the StreamSocketListener.
    • Use the Control property to retrieve a StreamSocketListenerControl object and set the socket quality of service required.
    • Assign the ConnectionReceived event to an event handler.
    • Call the BindServiceNameAsync or BindEndpointAsync method to bind to a local TCP port number or service name. For Bluetooth RFCOMM, the local service name parameter is the Bluetooth Service ID.
    • When a connection is received, use the StreamSocketListenerConnectionReceivedEventArgs object to retrieve the Socket property with the StreamSocket object created.
    • Use the StreamSocket object to send and receive data.
    • Call the Close method to stop listening for and accepting incoming network connections and release all unmanaged resources associated with the StreamSocketListener object. Any StreamSocket objects created when a connection is received are unaffected and can continue to be used as needed.