这不是另一个TcpClient vs Socket。
TcpClient是Socket类的一个包装器,用于简化开发,同时也暴露了底层的Socket。
仍然......
在TcpClient类的MSDN库页面上,可以阅读以下注释:
TcpClient类提供了连接,发送, 并以同步阻塞模式通过网络接收流数据。
对于Socket类:
Socket类允许您同时执行和 使用任何通信协议进行异步数据传输 在ProtocolType枚举中列出。
要仅通过TcpCient异步发送/接收某些数据,必须调用GetStream,通过调用其上的ReadAsync和WriteAsync方法从中检索基础NetworkStream,可以异步读/写数据, TAP模式(可能使用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
的调用都会立即“返回”。但是,我认为它在幕后有所作为......
对于TcpClient,依赖于NetworkStream,与直接使用套接字相比,读取是否会以某种方式阻塞或不进行阻塞?如果没有,那么在谈论同步阻塞模式时,TcpClient的注释是错误的吗?
如果有人能澄清的话会非常感激!
感谢。
答案 0 :(得分:23)
TcpClient
流上的异步I / O不会阻塞。看起来MSDN文档是错误的(您可以通过执行NetworkStream
的异步I / O调用在Reflector中验证这一点。)
Stream
类型是“有趣的”:默认情况下,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
没有TAP方法:这是一个非常好的问题!我认为这是一个疏忽,但现在.NET 4.5已经发布,看起来它是故意遗漏的。可能他们只是不想过度复杂化API - Socket已经有同步和两个异步API,涵盖同一组操作(Send
,SendTo
,{ {1}},Receive
,ReceiveFrom
,Connect
,Accept
)。 TAP反过来又要求为该全套设置两个额外的异步API。这至少会导致一个有趣的命名情况(Disconnect
名称已被采用,并且他们将为每个操作添加两个*Async
名称。
附注:“附加”API用于高性能异步*Async
通信。他们使用Socket
,这不是那么容易使用,但产生的内存垃圾更少。如果将TAP API添加到SocketAsyncEventArgs
,他们会希望提供易于使用的版本(包装Socket
/ Begin
)和更高性能的版本(包装{{1 }})。
如果你有兴趣为End
制作TAP方法,那么一个很好的起点是Stephen Toub的Awaiting Socket Operations(他只提供高性能API的包装器)。我为Async
启用的套接字使用类似的东西。