AsyncTcpClient icon indicating copy to clipboard operation
AsyncTcpClient copied to clipboard

How to wait for a transmission's completion?

Open serious-angel opened this issue 5 years ago • 3 comments

Dear Developer,

Thank you for this project. Although, for example, we have a code where:

... // Somehere in AsyncTCPClient's initialization
ReceivedCallback = (c, count) =>
{
    Console.WriteLine("2");
    response = c.ByteBuffer.Dequeue(count);
    if (responseLength + count <= int.MaxValue)
        responseLength += count;
    Console.WriteLine("3, responseLength = {0}", responseLength );
    return Task.CompletedTask;
}
...
ct1 = new CancellationTokenSource(1000); // 1000ms for a request timeout
t1 = this.asyncTCPClient.Send(new ArraySegment<byte>(data, 0, data.Length), ct1.Token);
if (ct1.IsCancellationRequested) // If connection or any answer timeout
{
    Console.WriteLine("Timeout");
}
else
{
    ct1 = new CancellationTokenSource(1000); // 1000ms for a response timeour
    Console.WriteLine("1");
    t1 = this.asyncTCPClient.WaitAsync(ct1.Token); // Wait for any response available
    Console.WriteLine("4, responseLength = {0}", responseLength);
}

And the output would be:

    1
    4, responseLength = 0 // It seems that WaitAsync does not wait while ByteBuffer dequeues.
    2
    3, responseLength = 41

Is it possible to force WaitAsync to wait while the full transaction would be completed?

serious-angel avatar Jun 01 '20 09:06 serious-angel

The AsyncTcpClient.WaitAsync method waits for data to be available in the receive buffer. It doesn't wait until you processed it. If you want that, you should set a signal when you're done, at the end of your ReceivedCallback handler. You could use a TaskCompletionSource for that.

ygoe avatar Jun 02 '20 19:06 ygoe

The AsyncTcpClient.WaitAsync method waits for data to be available in the receive buffer. It doesn't wait until you processed it. If you want that, you should set a signal when you're done, at the end of your ReceivedCallback handler. You could use a TaskCompletionSource for that.

Thank you, but how to wait for TaskCompletionSource if WaitAsync returns immediately when any data was received?

Currently, this one works:

ct1 = new CancellationTokenSource(1000);
this.asyncTCPClient.WaitAsync(ct1.Token); // Wait for any response available
if (!ct1.IsCancellationRequested) // If received something
{
    ct1 = new CancellationTokenSource(1000);
    while (responseLength != expectedResponseLength) // Wait for all requested data transferred
    {
        if (ct1.IsCancellationRequested) // If full data transfer timed out
        {
            fullDataTimeout= true; // Set flag of data's timeout
            break;
        }
        Thread.Sleep(100); // To escape a busy loop issue
    }
}
else
    connectionTimeout= true; // Set flag of connection's timeout

It works. However, it seems like it is not the best solution. Sorry, but what did you mean by TaskCompletionSource?

serious-angel avatar Jun 02 '20 19:06 serious-angel

You're calling AsyncTcpClient.WaitAsync and expect to wait until you finished processing the received data. That's the wrong method. You're waiting for new data to be available. Instead of this you should wait for your own event.

TaskCompletionSource<T> is a class that lets you create tasks you can wait on. Later, you can explicitly complete these tasks "from the outside", by calling a method like TrySetResult. Whoever waits for this task can then continue. It's to common pattern to asynchronously wait for something you do. It's also used in AsyncTcpClient.WaitAsync to wait for either new data or a closed connection.

ygoe avatar Jun 02 '20 20:06 ygoe