等待套接字操作

时间:2015-03-18 07:13:25

标签: c# async-await

鉴于以下代码,修改自Stephen Toub的文章。 http://blogs.msdn.com/b/pfxteam/archive/2011/12/15/10248293.aspx

    public async Task Start(CancellationToken token)
    {
        while (!token.IsCancellationRequested)
        {
            await this.acceptCount.WaitAsync(token).ConfigureAwait(false);
            if (token.IsCancellationRequested)
                break;

            var args = new SocketAsyncEventArgs();
            args.UserToken = new SocketFrame
                                 {
                                     CancellationToken = token,
                                     Buffer = null,
                                     Message = null,
                                 };

            // How do I manage this additional task?
            args.Completed += (s, e) => this.AcceptInbound((Socket)s, e).Wait(token);
            if (!this.socket.AcceptAsync(args))
                await this.AcceptInbound(this.socket, args);
        }
    }

    private async Task AcceptInbound(Socket sender, SocketAsyncEventArgs e)
    {

        var frame = (SocketFrame)e.UserToken;
        this.acceptCount.Release();
        Socket connectedClient = e.AcceptSocket;

        var args = new SocketAsyncEventArgs();
        args.SetBuffer(new byte[0x1000], 0, 0x1000);
        var awaitable = new SocketAwaitable(args);

        while (!frame.CancellationToken.IsCancellationRequested)
        {
            await connectedClient.ReceiveAsync(awaitable);
            var bytesRead = args.BytesTransferred;
            if (bytesRead <= 0) 
                break;

            if (this.AppendByteArrayToFrame(frame, args.Buffer, bytesRead))
                this.reader.MessageReceived(frame.Message);
        }
    }

如何避免Wait事件的args.Completed? 我希望在AcceptInbound中引发的异常会冒出来,所以我真的不想在那里等待。

我想要实现的是将AcceptInbound任务绑定到当前任务,这样当我等待当前任务时,会捕获引发的异常。

2 个答案:

答案 0 :(得分:1)

您可以注册一个async事件处理程序(这是async void唯一适合的地方)。这样您就可以await而不是使用Wait阻止

args.Completed += async (s, e) => await AcceptInbound((Socket)s, e);

如果您希望Start处理所有这些任务的完成和异常,那么我会将它们全部存储起来,直到Start完成,然后使用Task.WhenAll确保所有操作都完成并重新抛出他们可能产生的任何例外:

public async Task Start(CancellationToken token)
{
    var tasks = new ConcurrentBag<Task>();
    while (!token.IsCancellationRequested)
    {
        // ...
        args.Completed += (s, e) => tasks.Add(AcceptInbound((Socket)s, e));
        // ...
    }

    await Task.WhenAll(tasks);
}

答案 1 :(得分:0)

TaskCompletionSource救援!

看看这个:

private TaskCompletionSource<Something> _tcs = new TaskCompletionSource<Something>();

public void FinishAwait(Something result) {
    _tcs.SetResult(result);
}
public void FailAwait(Exception exception) {
    _tcs.SetException(exception);
}

public async Task<Something> M() {
    var result = await _tcs.Task;
    return result;
}

这是一种非常自定义的方式,用于控制特定Task何时以及如何完成。

您可以使用await自定义构建的任务,该任务由AcceptAsync操作的事件处理程序完成手动触发。

在您的情况下,通用参数T可以是object,因为我们只需要它voidobject是所有类型中最为良性的。

然后,您可以在等待Start方法的AcceptInbound之前等待Task方法中的中间任务。

你的问题不是没有传递例外 通过等待呼叫来呼叫来电,而不是你 太快调用AcceptIncomming

true的调用中简单地接收AcceptAsync并不意味着“已发生接受”,而是成功启动。

看一下代码的那一部分:

public async Task Start(CancellationToken token)
{
    while (!token.IsCancellationRequested)
    {
        await this.acceptCount.WaitAsync(token).ConfigureAwait(false);
        if (token.IsCancellationRequested)
            break;

        var args = new SocketAsyncEventArgs();
        args.UserToken = new SocketFrame
                             {
                                 CancellationToken = token,
                                 Buffer = null,
                                 Message = null,
                             };

        TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();

        // How do I manage this additional task?
        args.Completed += (s, e) => {
            switch (e.SocketError) {
                case SocketError.Success:
                    tcs.SetResult(null);
                    break;
                default:
                    tcs.SetException(new SocketException());
                    break;
            }
        };

        if (!this.socket.AcceptAsync(args)) {
            // receiving a true value here 
            // only means that AcceptAsync
            // started successfully
            // we still need to 
            // "await for the Completed event handler"

            await tcs.Task; // this will automatically throw
                            // if tcs.SetException(...) was used
            // also, it will return null if all is well
            // but most importantly, it will allow us to 
            // know when to call the next method

            await this.AcceptInbound(this.socket, args);
        }
    }
}