Reputation: 276
This is a problems I've been investigating in the last week and can't find any solution. Found posts asking the same but never getting an answer, hopefully this will help others as well.
I have a WCF
service that returns an object containing a stream
inside it. I use basicHttpBinding
with streamed transfer and Mtom to send it to the client.
Client calls the WCF
service and closes the proxy immediately after it receives the response object.
Next, the client reads the stream it got from the WCF service and writes it to a file on the local disk. All of this works fine.
My problem is when the client wants to abort the operation and stop downloading the data from the WCF service.
If I call .close()
on the stream, e.g: serverReply.DataStream.Close();
then it blocks and reads the whole stream from the WCF service till its end before continuing. The stream can be quite large and the network is not always fast.
This is very undesirable for both network resource usage, which is basically wasted on data that has no use anymore. And since basicHttpBinding
allows only two concurrent TCP connections (by default) to the WCF service server, it blocks other connection attempts until the stream is read till its end.
I could increase the number of concurrent connections, but that would be a bad solution as it would create an opening for trouble.
For example, 20 aborted downloads that are still downloading the data to throw it away. I need to make the transfer stop completely.
On the client, the stream object is just a regular Stream
class, so it only has the close method, nothing else.
Calling .close()
or .abort()
on the proxy object doesn't help, neither does destroying it using .dispose()
or any other method.
On the server side, I handle the OperationContext.OperationCompleted
event, but it is not fired until the data from the stream
is read till the end.
So the question is, how do I close/abort the stream without reading it entirely?
Upvotes: 17
Views: 1972
Reputation: 423
I think you are merely receiving the buffer after close. You should set maxBufferSize to a lower value.
You might also wish to restrict the amount of data read on the server by wrapping your stream and overriding read. Use smaller chunks if you have a low bandwidth client and return a corresponding count from the read method to match the amount you actually read. This would restrict the fill rate of the buffer.
My own tests on this subject involved me writing a NeverEndingStream and returning data always. At some point i called close on the client then almost immediately the close was called on the server. This suggests that the buffer was merely emptied because clearly it would never be able to read my stream till the end.
If you still experience issues then i suggest you track the timing of the Read method on your overridden stream. If the currentReadtime - the lastReadTime is > x then you might instead call Close and throw an exception. That will kill it for sure.
Upvotes: 0
Reputation: 356
After investigation I found that the WCF client will keep reading from the stream until closeTimeout ellapsed, then it will abort connection. You might decrease closeTimeout on client to minimize the problem.
NOTE: You should wrap the code that disposes a stream into try/catch block. The stream.Dispose() method will throw TimeoutException which brakes a guideline of not throwing exceptions in Dispose method.
Upvotes: 1
Reputation: 784
Have you tried playing around with binding.MaxBufferSize
?
Have you tried playing around with your app.config file settings e.g:
<bindings>
<wsHttpBinding>
<binding name="default" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647"
closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" >
<readerQuotas maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384"
maxDepth="64" maxStringContentLength="2147483647" />
</binding>
</wsHttpBinding>
</bindings>
I would minimize the timeouts anf buffer lengths and try to abort or close and see what happens.
Upvotes: 0