NetworkStream.ReadAsync with a cancellation token never cancels

前端 未结 6 1076
天涯浪人
天涯浪人 2020-11-27 04:02

Here the proof.
Any idea what is wrong in this code ?

    [TestMethod]
    public void TestTest()
    {
        var tcp = new TcpClient() { ReceiveTimeou         


        
6条回答
  •  误落风尘
    2020-11-27 04:34

    Per the description in Softlion's answer:

    Combine the async call with a delay task (Task.Delay) using Task.WaitAny. When the delay elapses before the io task, close the stream. This will force the task to stop. You should handle the async exception on the io task correctly. And you should add a continuation task for both the dealy task and the io task.

    I've made some code that gives you the async read with timeout:

    using System;
    using System.Net.Sockets;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication2013
    {
        class Program
        {
            /// 
            /// Does an async read on the supplied NetworkStream and will timeout after the specified milliseconds.
            /// 
            /// NetworkStream object on which to do the ReadAsync
            /// Socket associated with ns (needed to close to abort the ReadAsync task if the timeout occurs)
            /// number of milliseconds to wait for the read to complete before timing out
            ///  The buffer to write the data into
            /// The byte offset in buffer at which to begin writing data from the stream
            /// The maximum number of bytes to read
            /// 
            /// a Tuple where Item1 is true if the ReadAsync completed, and false if the timeout occurred,
            /// and Item2 is set to the amount of data that was read when Item1 is true
            /// 
            public static async Task> ReadWithTimeoutAsync(NetworkStream ns, Socket s, int timeoutMillis, byte[] buffer, int offset, int amountToRead)
            {
                Task readTask = ns.ReadAsync(buffer, offset, amountToRead);
                Task timeoutTask = Task.Delay(timeoutMillis);
    
                int amountRead = 0;
    
                bool result = await Task.Factory.ContinueWhenAny(new Task[] { readTask, timeoutTask }, (completedTask) =>
                {
                    if (completedTask == timeoutTask) //the timeout task was the first to complete
                    {
                        //close the socket (unless you set ownsSocket parameter to true in the NetworkStream constructor, closing the network stream alone was not enough to cause the readTask to get an exception)
                        s.Close();
                        return false; //indicate that a timeout occurred
                    }
                    else //the readTask completed
                    {
                        amountRead = readTask.Result;
                        return true;
                    }
                });
    
                return new Tuple(result, amountRead);
            }
    
            #region sample usage
            static void Main(string[] args)
            {
                Program p = new Program();
                Task.WaitAll(p.RunAsync());
            }
    
            public async Task RunAsync()
            {
                Socket s = new Socket(SocketType.Stream, ProtocolType.Tcp);
    
                Console.WriteLine("Connecting...");
                s.Connect("127.0.0.1", 7894);  //for a simple server to test the timeout, run "ncat -l 127.0.0.1 7894"
                Console.WriteLine("Connected!");
    
                NetworkStream ns = new NetworkStream(s);
    
                byte[] buffer = new byte[1024];
                Task> readWithTimeoutTask = Program.ReadWithTimeoutAsync(ns, s, 3000, buffer, 0, 1024);
                Console.WriteLine("Read task created");
    
                Tuple result = await readWithTimeoutTask;
    
                Console.WriteLine("readWithTimeoutTask is complete!");
                Console.WriteLine("Read succeeded without timeout? " + result.Item1 + ";  Amount read=" + result.Item2);
            }
            #endregion
        }
    }
    

提交回复
热议问题