27

这不是另一个 TcpClient vs Socket。

TcpClient 是 Socket 类的包装器,以简化开发,同时也暴露了底层的 Socket。

仍然 ...

在 TcpClient 类的 MSDN 库页面上,可以阅读以下注释:

TcpClient 类提供了简单的方法,用于以同步阻塞模式通过网络连接、发送和接收流数据。

对于 Socket 类:

Socket 类允许您使用 ProtocolType 枚举中列出的任何通信协议执行同步和异步数据传输。

要仅通过 TcpCient 异步发送/接收某些数据,必须调用 GetStream 来检索底层 NetworkStream,通过调用 ReadAsync 和 WriteAsync 方法,可以从其上异步读取/写入数据,遵循 TAP 模式(可能使用 async/await 结构)。

要通过 Socket 异步发送/接收一些数据(我不是专家,但我认为我做对了),我们可以通过调用 BeginRead/EndRead BeginWrite/EndWrite(或者只是 ReadAsync 或WriteAsync .. 不暴露 TAP 模式 - 即不返回 Task .. 令人困惑)。

首先,知道为什么 .NET 4.5 中的 Socket 类没有以任何方式实现 TAP 模式,即 ReadAsync 和 WriteAsync 返回 Task(如果以不同方式调用以保持向后兼容的事件)?

无论如何,从 APM 模型方法对构建任务方法很容易,所以假设我将此异步方法(用于读取)称为 ReadAsyncTAP(返回任务)。

好 ?所以现在假设我想编写一个客户端方法async Task<Byte[]> ReadNbBytes(int nbBytes),我将从我的代码中调用该方法以异步地从网络中读取一定数量的字节。

此方法的实现仅基于 TcpClient 将通过调用 GetStream 获取 NetworkStream 并将包含一个异步循环等待 ReadAsync 调用,直到缓冲区满。

这个基于 Socket 的方法的实现将包含一个异步循环,等待 ReadAsyncTAP 直到缓冲区满。

归根结底,从客户端代码的角度来看,我想这没有什么区别。在这两种情况下,调用await ReadNbBytes都会立即“返回”。但是,我想这在幕后会有所不同......对于依赖 NetworkStream 的 TcpClient,与直接使用套接字相比,读取是否在任何时候都会阻塞?如果不是,在谈论同步阻塞模式时,对 TcpClient 的评论是错误的吗?

如果有人能澄清,将不胜感激!

谢谢。

4

1 回答 1

28

流上的异步 I/OTcpClient不会阻塞。看起来 MSDN 文档是错误的(您可以通过遵循NetworkStream的异步 I/O 调用在 Reflector 中验证这一点)。

Stream类型是“有趣的”:默认情况下,Stream基类将通过在同步 I/O 上阻塞线程池线程来实现异步 I/O。所以你永远不想在像 a 这样的东西上做异步 I/O MemoryStream,它只提供同步方法。

NetworkStream 确实提供了异步 I/O,因此NetworkStream实例上的异步 I/O 实际上是异步的。但情况并非总是如此:FileStream特别是通常不是异步的,但如果你构造实例恰到好处

关于为什么Socket没有 TAP 方法:这是一个非常好的问题!我认为这是一个疏忽,但现在 .NET 4.5 发布了,看起来它是故意遗漏的。可能是他们只是不想让 API 过于复杂 - Socket 已经有同步和两个异步 API,涵盖了相同的操作集(Send, SendTo, Receive, ReceiveFrom, Connect, Accept, Disconnect)。反过来,TAP 将需要两个额外的异步 API 用于该完整集。这至少会导致一个有趣的命名情况(*Async名称已经被使用,并且他们*Async将为每个操作添加另外两个名称)。

旁注:“附加”API 用于高性能异步Socket通信。他们使用SocketAsyncEventArgs,这并不容易使用,但产生的内存垃圾更少。如果将 TAP API 添加到Socket,他们会希望同时提供易于使用的版本(包装Begin/ End)和更高性能的版本(包装Async)。

如果您Socket为. 我为我的启用套接字使用了类似的东西。async

于 2012-08-16T10:56:51.377 回答