我有一个使用自定义FTP库的C#应用程序。目前,我使用Socket.Send发送数据,但我想知道是否更好地使用NetworkStream.Write,通过套接字初始化NetworkStream。
使用其中一种方法有什么优势吗?
我有一个使用自定义FTP库的C#应用程序。目前,我使用Socket.Send发送数据,但我想知道是否更好地使用NetworkStream.Write,通过套接字初始化NetworkStream。
使用其中一种方法有什么优势吗?
NetworkStream
的优势主要在于它是一个Stream
。而Socket
的劣势在于,通用代码从抽象I/O源(如Stream
)读取和写入时,无法处理Socket
。
NetworkStream
的主要用途是当你有其他某些代码从Stream
中读取或写入数据,并且你希望能够将其用于Socket
时。如果你处于这种情况下,NetworkStream
将会非常有帮助!Stream
。然后,你的序列化方法可以接受FileStream
、PipeStream
或NetworkStream
,甚至可以接受MemoryStream
。这就是抽象的好处,因为创建了流之后,方法可以与其交互而不知道它是哪种类型的流。NetworkStream
使用了适配器设计模式。它将Socket
API转换为Stream
API,以便期望使用Stream
的客户端可以使用它。NetworkStream
是Socket
的Stream
适配器,那么我们应该使用哪个?嗯,如果你需要一个Stream
,那么NetworkStream
是你唯一的选择。如果你不需要Stream
,那么你可以使用你最熟悉的API。如果你已经成功使用Socket
,则没有迫切的理由要切换到NetworkStream
。NetworkStream
的创建与其作为抽象Stream
进行处理分开 - 这样您就能够更改传输方式,或者仅为测试创建Stream
存根。NetworkStream.Write
内部只有一个操作(除了状态检查),即streamSocket.Send(buffer, offset, size, SocketFlags.None);
- 因此它大多数情况下和在套接字上调用它是相同的。使用.NET Framework实现的NetworkStream::Write
的一个缺点是,如果底层网络(OSI层1-4)无法接收整个数据缓冲区,则您不会得到通知(除非通过检查.NET Networking性能计数器)。
类似于这样的内容出现在Microsoft的.NET FrameworkSystem.dll中的NetworkStream::Write
中:
try
{
socket.Send(buffer, offset, size, SocketFlags.None);
}
/* ... catch{throw;} */
Send()
写入的字节数,被忽略了。这表明NetworkStream::Write
在次优网络(漫游/无线)或可能超出可用带宽的网络中不可靠。
您可以找到其他实现,它们会写入所有字节,直到所有buffer
都被写入(或发生其他故障)。这是发送数据的标准方法,并且始终会表现出以下行为:
var count = 0;
while (count < size)
{
count += socket.Send(buffer, offset + count, size - count, ...);
}
通过直接调用Socket::Send
,您可以编写适当且可靠的send()代码,但是如果调用NetworkStream::Write
,则无法编写适当且可靠的send()代码。
另外,其他变体的NetworkStream
没有BCL分解所显示的相同问题(请参见mono,cosi2),而仍有一些类似的问题尽管他们已经尽最大努力(请参见flourinefx)。
socket.Send
的文档:“在非阻塞模式下,即使发送的字节数少于您请求的字节数,发送操作也可能成功完成。您的应用程序有责任跟踪发送的字节数并重试操作,直到应用程序发送所需的字节数。” - 因此,这个错误不适用于不使用非阻塞套接字的情况,并且非阻塞是默认的,因此大多数用户应该没有问题。 - DaiNetworkStream
的构造函数中传入一个非阻塞的Socket
实例(if( !socket.IsBlocking ) {
),它将抛出一个IOException
异常。 - Dai
NetworkStream
没有直接实现基于任务的异步支持,而是依赖于Stream
自己的APM-to-Task功能,这并不像“本地”的基于任务的异步那样理想(它使用TaskFactory.FromAsync
)。此外,BeginRead
/BeginWrite
(APM)方法只是包装了Socket.BeginReceive
/Socket.BeginSend
,这些方法不如(名称令人困惑的非基于任务的)Socket.SendAsync
和Socket.ReceiveAsync
方法高效。 - Dai