我正在使用WCF下载一个非常长的文件。它是在net-tcp绑定上自托管的服务。在客户端,我在后台线程中读取流并将其写入磁盘。在UI上,有一个取消按钮。我使用CancellationToken来取消读写循环。
问题是,如果流还没有到达结尾,也就是EOF(End of File),那么释放它需要太长时间。
在服务器端(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 代码块都会尝试释放它们的资源。当第二个 using 代码块尝试释放 MessageBodyStream
时,会挂起 10 分钟。但是如果流已经完全读取,则退出速度很快。我怀疑这与 10 分钟的
ReceiveTimeout
有关。因此,我将其更改为 30 秒,然后神奇地,现在的处理只需要 30 秒。还有一件事。清理操作实际上超时了。它吞掉了我的
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()
因此,我将使用块更改为try-finally
块。在那里,我放置了wcfStrm.close
,然后是wcfStrm.Dispose
。令我惊讶的是,关闭语句迅速通过,而处理超时。如果在dispose内部实际的罪魁祸首是Close
,那么为什么显式关闭没有挂起?然后再次关闭流,为什么处理会挂起?