【发布时间】:2012-08-16 08:37:59
【问题描述】:
我目前正在实现一个依赖 TCP/IP 进行传输(持久连接)的应用程序协议库。
我正在尝试使用 C#5 async/await 构造依赖于 TAP 模式来实现一个不错的异步实现,主要是为了将我迄今为止只在理论上看到的概念付诸实践。
客户端可以连接到远程服务器并向其发送请求。 客户端接收来自服务器的响应以及请求(全双工模式)。
从客户端代码来看,异步调用我的库向服务器发送请求并接收相关响应很简单:
var rsp = await session.SendRequestAsync(req);
在我的协议库中,我只是在构建请求,将其转换为字节(将在网络流上发送),然后在流上调用 WriteAsync,然后等待在发送之前创建的任务请求,利用TaskCompletionSource对象,它基本上是在等待相关响应被接收(并在tcs上设置结果),然后将响应返回给客户端调用者。
这部分看起来不错。
现在“问题”涉及服务器向客户端发送请求的部分。服务器可以向客户端发送不同类型的请求。
我的协议库使用异步循环来监听底层流(接收来自服务器的传入响应或请求)。 这个循环在流上异步读取响应/请求,然后如果来自服务器的请求,它会引发对应于请求类型的事件(例如 ReceivedRequestTypeA)。客户端代码可以订阅这些事件,以便在从服务器接收到特定请求类型时得到通知。这些事件参数包含与请求相关的所有参数以及一个响应对象,由客户端设置,一旦事件处理程序代码完成,它将由库异步发送到流上。
异步监听循环的代码如下。请不要介意while true,不是很漂亮(应该使用取消模式),但这不是重点!
private async Task ListenAsync()
{
while(true)
{
Request req = await ReadRequestAsync();
await OnReceivedRequest(req);
}
}
所以循环正在调用异步方法ReadRequestAsync,它只是在流中异步读取一些字节,直到有完整的请求或响应可用。
然后它将请求转发到异步方法OnReceivedRequest,代码如下:
private async Task OnReceivedRequest(Request req)
{
var eventArgs = new ReceivedRequestEventArgs { Req = req };
if (req is RequestTypeA)
{ ReceivedRequestTypeA(this, eventArgs); }
[... Other request types ...]
await SendResponseAsync(eventArgs.Resp);
}
此异步方法引发适当的请求类型事件。 客户端代码订阅了此事件,因此调用其相应的事件处理程序方法......客户端代码对请求执行任何所需的操作,然后构造响应并将其设置在 EventArgs 对象中 - 事件处理程序方法的结尾 -。代码在库中的 OnReceivedRequest 中恢复,并异步发送响应(在底层流上调用 WriteAsync)。
我认为这不是一个好方法,因为如果客户端的事件处理程序代码正在执行冗长的阻塞操作,它可以完全阻塞库中的异步循环(再见完全异步协议库,你现在由于客户端代码而变得以某种方式同步)。如果我使用基于异步任务的委托来处理事件并等待它,也会发生同样的情况。
我在想,我可以有一个异步方法GetRequestTypeAAsync(),而不是使用事件,这将使用库中的TaskCompletionSource 对象来实现,并使用OnReceivedRequest 中的请求设置tcs 结果。而在客户端代码方面,不是订阅ReceivedRequestTypeA 事件,而是代码宁愿包含一个围绕GetRequestTypeAAsync() 的循环。仍然由于客户端代码必须以某种方式向要发送到服务器的库提供响应,我不知道这是如何工作的......
我的大脑现在完全模糊,无法真正思考清楚。任何关于漂亮设计的建议将不胜感激。
谢谢!
【问题讨论】:
标签: c# async-await