简体   繁体   中英

How to cancel an awaiting task?

I am trying to cancel a task awaiting for network IO using CancellationTokenSource, but I have to wait until TcpClient connects:

try
{
    while (true)
    {
        token.Token.ThrowIfCancellationRequested();
        Thread.Sleep(int.MaxValue); //simulating a TcpListener waiting for request
    }
}

Any ideas?

Secondly, is it OK to start each client in a separate task?

When you start the task you can use an overload of the StartNew to pass a cancellation token that your task will check for cancellations.

Alternatively you could just use AcceptAsync and continue doing other work. AcceptAsync will call the OnCompleted method attached via the SocketAsyncEventArgs param you define.

internal class Program
{
    private static EventWaitHandle _signalFromClient;
    private static readonly string NameThatClientKnows = Guid.NewGuid().ToString();
    private static readonly CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();

    private const int PingSendTimeout = 30000;
    private static Socket _connectedClientSocket;
    private static Socket _tcpServer;

    private static void Main(string[] args)
    {
        _signalFromClient = new EventWaitHandle(false, EventResetMode.AutoReset, NameThatClientKnows);

        _tcpServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _tcpServer.Bind(new IPEndPoint(IPAddress.Loopback, 0));
        _tcpServer.Listen(1);

        var asyncOpInfo = new SocketAsyncEventArgs();
        asyncOpInfo.Completed += CompletedConnectRequest;
        _tcpServer.AcceptAsync(asyncOpInfo);

        Console.WriteLine("Console stays open, connecting client will say something.");
        Console.ReadLine();
    }

    private static void CompletedConnectRequest(object sender, SocketAsyncEventArgs e)
    {
        Console.WriteLine("Client connected");

        _connectedClientSocket = e.AcceptSocket;

        Task.Factory.StartNew(SendSimpleMessage, CancellationTokenSource.Token);
    }

    private static void SendSimpleMessage()
    {
        while (!CancellationTokenSource.Token.IsCancellationRequested && _connectedClientSocket.Connected)
        {
            try
            {
                _connectedClientSocket.Send(Encoding.UTF8.GetBytes("PING"));
                _signalFromClient.WaitOne(PingSendTimeout);
            }
            catch (SocketException) { Dispose(); }
        }
    }

    private static void Dispose()
    {
        CancellationTokenSource.Cancel();

        _connectedClientSocket.Close();
        _tcpServer.Close();
    }
}

Of course setup the SocketAsyncEventArgs with a buffer and other necessary items/behaviour. In Dispose(), I cancel the task and catch any SocketExceptions that may be thrown by calling Socket.Close on both client and server ø_Ø.

I used the CancellationToken handle instead of creating a new one, it makes the code more simple

var task = Listener.AcceptTcpClientAsync();
task.Wait(MainToken.Token);
MainToken.Token.ThrowIfCancellationRequested();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM