gpt4 book ai didi

.net - 在 WCF 中过早处置 MessageBodyStream

转载 作者:行者123 更新时间:2023-12-02 02:09:53 29 4
gpt4 key购买 nike

我正在使用 WCF 下载一个很长的文件。它是一个基于 net-tcp 绑定(bind)的自托管服务。在客户端上,我正在读取流并将其写入后台线程中的磁盘。在 UI 上,有一个取消按钮。我使用 CancellationToken 取消读写循环。

问题是,

如果流是过早的(不是 EOF),那么处理它需要很长时间。

在服务器(c#):

IO.Stream getFile(string filePath) {
return new IO.FileStream(filePath);
}

在客户端(vb):

using proxy as new ServiceReference1.TestServer
using wcfStrm = proxy.getFile("c:\100MB.dat")
using fileStrm = new FileStream("d:\destination\100MB.dat")

dim buff(256) as new Byte

while true
cancellationToken.ThrowIfCancellationRequested

Dim len = wcfStrm.Read(buff, 0, buff.Length)

if len > 0 then
fileStrm.write(buff, 0, len)
else
exit while
end if
end while

end using
end using ' <-------------- this hangs for 10Mins
end using

CancellationToken抛出OperationCancelledException时,所有三个using block 都会尝试释放它们的资源。现在,当第二个 using block 尝试处置 MessageBodyStream 时,它会挂起 10 分钟。但如果流被完全读取,那么它会很快退出。

我怀疑,这与 10 分钟的 ReceiveTimeout 有关。所以我把它改为30秒和中提琴!现在处理需要 30 秒。

还有一件事。 Dispose 操作实际上超时了。它吃掉了我的 OperationCancelledException 并产生一个 TimeoutException套接字传输在 00:00:00 之后超时... bla bla bla

以下是堆栈跟踪

System.TimeoutException: The socket transfer timed out after 00:00:00. You have exceeded the timeout set on your binding. The time allotted to this operation may have been a portion of a longer timeout.
at System.ServiceModel.Channels.SocketConnection.SetReadTimeout(TimeSpan timeout, Boolean synchronous, Boolean closing)
at System.ServiceModel.Channels.SocketConnection.ReadCore(Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout, Boolean closing)
at System.ServiceModel.Channels.SocketConnection.Read(Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout)
at System.ServiceModel.Channels.DelegatingConnection.Read(Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout)
at System.ServiceModel.Channels.PreReadConnection.Read(Byte[] buffer, Int32 offset, Int32 size, TimeSpan timeout)
at System.ServiceModel.Channels.SingletonConnectionReader.SingletonInputConnectionStream.ReadCore(Byte[] buffer, Int32 offset, Int32 count)
at System.ServiceModel.Channels.SingletonConnectionReader.SingletonInputConnectionStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.ServiceModel.Channels.MaxMessageSizeStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.ServiceModel.Channels.SingletonConnectionReader.Close(TimeSpan timeout)
at System.ServiceModel.Channels.SingletonConnectionReader.SingletonInputConnectionStream.Close()
at System.ServiceModel.Channels.DelegatingStream.Close()
at System.Xml.XmlBufferReader.Close()
at System.Xml.XmlBaseReader.Close()
at System.Xml.XmlBinaryReader.Close()
at System.ServiceModel.Dispatcher.StreamFormatter.MessageBodyStream.Close()
at System.IO.Stream.Dispose()
at ...somewhere in my code...

我不相信一旦没有完全阅读它就无法取消流。另一方面,我不能只是忘记流并让它离开而不进行处理。它必须是一个阻塞等待直到安全释放的调用。

有人可以帮我吗?

编辑

堆栈跟踪显示:

'                                                         this is interesting
at System.Xml.XmlBinaryReader.Close() ' VVVVVVVVVVVVV
at System.ServiceModel.Dispatcher.StreamFormatter.MessageBodyStream.Close()
at System.IO.Stream.Dispose()

所以我将 using block 更改为 try-finally block 。我在那里放置了 wcfStrm.close ,然后放置了 wcfStrm.Dispose 。令我惊讶的是,关闭语句很快就过去了,而处理却超时了。现在,如果内部处置真正的罪魁祸首是 Close 那么为什么显式关闭没有挂起?然后即使流关闭,处置也会挂起?

最佳答案

澄清一下,Stream.Dispose() 的实现是调用 Stream.Close()Stream.Close() 的基本实现是调用 Stream.Dispose(bool)。它与通常如何实现 IDisposable 的准则相反,因此值得注意。

实现 MessageBodyStream.Close() 方法首先关闭正在读取的 Message,然后关闭与流关​​联的 XmlDictionaryReader .

查看完整的堆栈跟踪,问题似乎是该读取器最终调用 SingletonConnectionReader.Close(TimeSpan)。这将 TimeSpan 作为超时,这是替换 OperationCancelledExceptionTimeoutException 的来源。

此方法尝试读取流的其余部分以完成关闭操作。我无法解释其背后的原理,但事实就是如此。

<小时/>

要解决您的问题,您必须停止使用代理类。尽管是 IDisposable,但在 using block 中使用任何 WCF 代理并不安全,因为调用 Dispose() 会调用 Close() ,如果出现异常,那不是你的意思。

在这种情况下,在代理上调用 Abort() 将完全解决问题,因为这就是您的意思:中止操作。

using proxy as new ServiceReference1.TestServer
dim wcfStrm = proxy.getFile("c:\100MB.dat")

try
using fileStrm = new FileStream("d:\destination\100MB.dat")

dim buff(256) as new Byte

while true
cancellationToken.ThrowIfCancellationRequested

Dim len = wcfStrm.Read(buff, 0, buff.Length)

if len > 0 then
fileStrm.write(buff, 0, len)
else
exit while
end if
end while

end using
end try
catch
proxy.Abort()
end catch
finally
wcfStrm.Dispose()
end finally
end using

我不是 VB 开发人员,所以如果我的语法很糟糕,我深表歉意。

关于.net - 在 WCF 中过早处置 MessageBodyStream,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19227665/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com