Removing MessageBodyStream prematurely in WCF

I use WCF to upload a very long file. This is a self-service net-tcp binding service. On the client, I read the stream and write it to disk in the background stream. There is a cancel button in the user interface. I am canceling the read / write cycle using the CancellationToken .

The problem is,

if the thread is premature (not EOF), then it must be disposed of for too long.

on server (C #):

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

on the client (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 

When a CancellationToken throws an OperationCancelledException , all three usage blocks try to destroy their resources. Now, when the second block in use tries to recycle MessageBodyStream , it freezes for 10 minutes. but if the stream is fully read, it quickly terminates.

I suspected that it had something to do with ReceiveTimeout 10 minutes. so I changed it to 30 seconds and alt! Disposal now takes 30 seconds.

One more thing. The Dispose operation actually disables. He eats my OperationCancelledException and throws a TimeoutException message The sockket transfer timed out after 00:00:00... bla bla bla

next is a stack trace

 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... 

I am not sure that once I can not cancel the stream without reading it completely. On the other hand, I can’t just forget the stream and let it go without recycling. It should be a blocking wait until a safe exit.

Can anyone help me here?

Edit

The stack trace shows:

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

So, I changed the use block to a try-finally block. there I put wcfStrm.close and then wcfStrm.Dispose . to my surprise, the close statement passed quickly, and the order was timed. Now, if inside the order the actual culprit was Close , then why didn't the explicit closure hang? and then destroy the hanged again, even when the stream is closed?

+6
source share
3 answers

To clarify, the implementation of Stream.Dispose() is to call Stream.Close() . The basic implementation of Stream.Close() is a call to Stream.Dispose(bool) . This contradicts the recommendations on how you usually implement IDisposable , so it’s worth noting.

The MessageBodyStream.Close() method is implemented to first close the read Message , then close the XmlDictionaryReader associated with the stream.

Looking at your full stack trace, the problem is that this reader will ultimately call SingletonConnectionReader.Close(TimeSpan) . This takes TimeSpan as a timeout, and this is the source of your TimeoutException , which replaces OperationCancelledException .

This method attempts to read the rest of the stream to complete the close operation. I can not explain the reason for this, but it is.


To fix the problem, you should stop using your proxy class as it is. Despite being IDisposable , it is not safe to use any WCF proxy in the using block, because calling Dispose() calls Close() , and in case of an exception, this is not what you mean.

In this case, calling the proxy Abort() completely fix the problem, because it means: abort the operation.

 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 

I'm not a VB developer, so I apologize if my syntax is terrible.

+2
source

You might consider splitting a large file into smaller chunks (streams). You will still have a delay, but significantly shorter than your approach.

0
source

It really depends on what logic was planned for the "Dispose" function.

The dispose function can mean (see here )

  • End current operation and free resources
  • Stop current operation and free resources

I assume that the implementation of the implementation function "1" and the implementation of the closing function "2"

You can check this with reflection or documentation.

I have not tested it because I have no clue what type of "wcfStrm"

0
source

Source: https://habr.com/ru/post/955413/


All Articles