如何将CancellationToken传递给EventHandler

时间:2017-11-16 16:39:23

标签: c# async-await tcplistener cancellationtokensource cancellation-token

拥有以下Async TCP服务器的参考代码,我想将CancellationToken传递给OnDataReceived

public sealed class TcpServer : IDisposable
    {
        private readonly TcpListener _listener;
        private CancellationTokenSource _tokenSource;
        private CancellationToken _token;
        private bool _listening;


        public event EventHandler<ConnectedEventArgs> OnDataReceived;

        public TcpServer(IPAddress address, int port)
        {
            _listener = new TcpListener(address, port);
        }


        public async Task StartAsync(CancellationToken? token = null)
        {
            _tokenSource = CancellationTokenSource.CreateLinkedTokenSource(token ?? new CancellationToken());
            _token = _tokenSource.Token;
            _listener.Start();
            _listening = true;

            try
            {
                while (!_token.IsCancellationRequested)
                {
                    await Task.Run(async () =>
                    {
                        var tcpClientTask = _listener.AcceptTcpClientAsync();
                        var tcpClient = await tcpClientTask;

                        OnDataReceived?.Invoke(tcpClient, new ConnectedEventArgs(tcpClient.GetStream()));
                    }, _token);
                }
            }

            finally
            {
                _listener.Stop();
                _listening = false;
            }
        }

        public void Stop()
        {
            _tokenSource?.Cancel();
        }

        public void Dispose()
        {
            Stop();
        }
    }

    public class ConnectedEventArgs : EventArgs
    {
        public NetworkStream Stream { get; private set; }

        public ConnectedEventArgs(NetworkStream stream)
        {
            Stream = stream;
        }
    }

以下代码显示了我正在使用上述代码的方式,目前位于ReadStreamBytesAsync我正在传递new CancellationToken(),而我想从token传递MainAsync参数:

class Program
    {
        static void Main(string[] args)
        {
            InitAsyncWork();

            Console.WriteLine("ended.");
            Console.ReadKey();
        }

        static void InitAsyncWork()
        {
            var cts = new CancellationTokenSource();
            Console.CancelKeyPress += (s, e) =>
            {
                e.Cancel = true;
                cts.Cancel();
            };
            MainAsync(cts.Token).GetAwaiter().GetResult();
        }
        static async Task MainAsync(CancellationToken token)
        {
            using (var server = new TcpServer(IPAddress.Any, 54001))
            {
                server.OnDataReceived += TcpClientOnlyReceive;
                await server.StartAsync(token);
            }
        }

        private static async void TcpClientOnlyReceive(object sender, ConnectedEventArgs e)
        {
            try
            {
                using (TcpClient client = (TcpClient)sender)
                using (NetworkStream stream = e.Stream)
                {
                    while (Utilities.IsTcpClientConnected(client))
                    {
                        if (client.Available == 0)
                        {
                            await Task.Delay(50);
                            continue;
                        }

                        byte[] rawX = await ReadStreamBytesAsync(stream, client.ReceiveBufferSize, new CancellationToken());

                    }//while(client.Connected)

                }//using(client)using(stream)

            }
            catch (Exception ex)
            {
                Utilities.logger.Error("TcpClientOnlyReceive():Exception!!\r\nMsg: {1}\r\nStack: {2}", ex.Message, ex.StackTrace);
            }
        }

        private static async Task<byte[]> ReadStreamBytesAsync(NetworkStream stream, int maxBytesToRead, CancellationToken token)
        {
            var bytesRead = 0;
            var totalBytesRead = 0;
            var clientMsg = new byte[maxBytesToRead];
            byte[] contentBytes;
            using (MemoryStream ms = new MemoryStream())
            {
                do
                {
                    bytesRead = await stream.ReadAsync(clientMsg, totalBytesRead, clientMsg.Length - totalBytesRead, token);
                    await ms.WriteAsync(clientMsg, totalBytesRead, bytesRead, token);
                    totalBytesRead += bytesRead;
                }
                while (bytesRead > 0 && stream.DataAvailable);
                contentBytes = ms.ToArray();
            }
            return contentBytes;
        }

    }

我应该将cts定义为全局变量吗?或者修改ConnectedEventArgs类,使其接受CancellationToken作为属性?

澄清: 根据收到的意见和1票投票结束这个问题!

  1. 我的主要目标是在用户按ctrl+c后关闭该应用程序,目前该应用程序阻止接受新连接,但它不会取消已建立的连接。
  2. 我从来没有听说过要更换活动的任务,如果这是真的,请分享参考。
  3. 如果它的BAD设计很好,可以分享更好的代码版本! 个人认为它干净而健壮

0 个答案:

没有答案