这不是又一篇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模式-即不返回任务..令人困惑)。
首先,有没有想过为什么.NET 4.5中的Socket类在任何情况下都未以任何方式实现TAP模式,即ReadAsync和WriteAsync不返回任务(即使以不同的方式调用以保留向后兼容性)?
无论如何,从APM模型方法对生成任务方法很容易,所以假设我调用此异步方法(用于读取),即ReadAsyncTAP(返回一个Task)。
好了?那么现在假设我想编写一个客户端方法
基于TcpClient的这种方法实现将通过调用GetStream来获取NetworkStream,并包含一个异步循环,等待ReadAsync调用,直到缓冲区满。
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模式-即不返回任务..令人困惑)。
首先,有没有想过为什么.NET 4.5中的Socket类在任何情况下都未以任何方式实现TAP模式,即ReadAsync和WriteAsync不返回任务(即使以不同的方式调用以保留向后兼容性)?
无论如何,从APM模型方法对生成任务方法很容易,所以假设我调用此异步方法(用于读取),即ReadAsyncTAP(返回一个Task)。
好了?那么现在假设我想编写一个客户端方法
async Task<Byte[]> ReadNbBytes(int nbBytes)
,我将从我的代码中调用它来异步读取特定数量的字节。基于TcpClient的这种方法实现将通过调用GetStream来获取NetworkStream,并包含一个异步循环,等待ReadAsync调用,直到缓冲区满。
基于Socket的该方法实现将包含一个异步循环,等待ReadAsyncTAP直到缓冲区满。
从客户端代码的角度来看,在这两种情况下,调用await ReadNbBytes
都会立即“返回”。但是,在幕后,它们可能存在一些区别...对于使用NetworkStream进行依赖的TcpClient,与直接使用Socket相比,读取是否在任何时候被阻塞,如果没有,则在同步阻塞模式下讨论TcpClient时的备注是否有误?
如果有人能够澄清,将不胜感激!
谢谢。
WebClient
也有类似的情况,其中*Async
名称用于EAP(基于事件的异步模式)方法。他们解决这个问题的方式是将TAP方法命名为*TaskAsync
。 - svickSocket
中的命名情况比较棘手。在整个 BCL 中,只有这个类使用*Async
来表示“使用SocketAsyncEventArgs
”,而不是 EAP。因此,您可以对 APM 风格的包装器使用*TaskAsync
,然后如果遵循现有的 TAP 和Socket
约定,就会得到*AsyncAsync
用于SocketAsyncEventArgs
风格的包装器。对于一个已经过度负担的类型来说,这些方法看起来很丑陋,肯定会让人感到困惑。 - Stephen Cleary