处理异步时TcpClient vs Socket

这不是另一个TcpClient vs Socket .

TcpClient是Socket类的一个包装器,用于简化开发,同时也暴露了底层的Socket .

还是......

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

TcpClient类提供了在同步阻塞模式下通过网络连接,发送和接收流数据的简单方法 .

而对于Socket类:

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

要仅通过TcpCient异步发送/接收某些数据,必须调用GetStream,通过在TAP模式上调用ReadAsync和WriteAsync方法,从中检索基础NetworkStream,可以异步读/写数据 . (可能使用async / await构造) .

要通过Socket异步发送/接收一些数据(我不是专家,但我认为我做对了),我们可以通过调用BeginRead / EndRead BeginWrite / EndWrite(或者只是ReadAsync或者)来直接读取/写入套接字实例本身 . WriteAsync ..不暴露TAP模式 - 即不返回任务..令人困惑) .

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

无论如何,很容易从APM模型方法对构建一个Task方法,所以让我说我调用这个异步方法(用于读取)ReadAsyncTAP(返回一个Task) .

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

完全基于TcpClient的此方法的实现将通过调用GetStream来获取NetworkStream,并且将包含等待ReadAsync调用的异步循环,直到缓冲区满 .

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

在一天结束时,从客户端代码的角度来看,我认为它没有任何区别 . 在这两种情况下,对 await ReadNbBytes 的调用将立即'return' . 但是,我认为它在幕后产生了影响......对于TcpClient,依赖于NetworkStream,与直接使用套接字相比,读取是否会以某种方式阻塞或不阻止?如果没有,那么在谈论同步阻塞模式时,TcpClient的注释是错误的吗?

如果有人能澄清,将非常感激!

谢谢 .

回答(1)

3 years ago

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

Stream 类型是"interesting":默认情况下, Stream 基类将通过阻塞同步I / O上的线程池线程来实现异步I / O.因此,您不希望对 MemoryStream 这样只提供同步方法的事情进行异步I / O.

NetworkStream 确实提供了异步I / O,因此 NetworkStream 实例上的异步I / O实际上是异步的 . 但情况并非总是如此: FileStream 特别是usually not asynchronous but it is if you construct the instance just right .

关于为什么 Socket 不希望过度复杂化API - Socket已经有同步和两个异步API覆盖同一组操作( SendSendToReceiveReceiveFromConnectAcceptDisconnect ) . TAP反过来又要求为该全套设置两个额外的异步API . 这至少会导致一个有趣的命名情况(已经采用 *Async 名称,并且他们将为每个操作添加两个 *Async 名称) .

附注:"additional" API用于高性能异步 Socket 通信 . 他们使用 SocketAsyncEventArgs ,这不是那么容易使用,但产生的内存垃圾更少 . 如果将TAP API添加到 Socket ,则他们希望提供易于使用的版本(包装 Begin / End )和更高性能的版本(包装 Async ) .

如果你有兴趣为 Socket 制作TAP方法,那么一个很好的起点是Stephen Toub的Awaiting Socket Operations(他只提供高性能API的包装器) . 我为 async -enabled套接字使用类似的东西 .