By default, when the client and the service exchange messages, these messages are buffered on the receiving end and delivered once the entire message is received. This is true whether it is the client sending a message to the service or the service returning a message to the client. When the client calls the service, the service is invoked when the message is received in its entirety. The client is unblocked when the returned message with the results of the invocation is received in its entirety. For sufficiently small message sizes, this exchange pattern provides for a simple programming model because the latency caused by receiving the message is usually negligible compared with the message processing itself. However, when it comes to much larger messages, such as ones involving multimedia content, large files, or batches of data, blocking every time until the message is received may be impractical. To handle such cases, WCF enables the receiving side (be it the client or the service) to start processing the data in the message while the message is still being received by the channel. Such processing is called streaming transfer mode. With large payloads, streaming provides improved throughput and responsiveness because neither the receiving nor sending side is blocked when sending or receiving the message.
For message streaming, WCF requires the use of the .NET Stream
class. In fact, the contract operations used for streaming look just like conventional I/O methods. The Stream
class is the base class of all the I/O streams
in .NET (such as the FileStream
, NetworkStream
, and MemoryStream
classes) allowing you to stream content from any of these I/O sources. All you need to do is to return or receive a Stream
as an operation parameter, as shown in Example 5-17.
Example 5-17. Streaming operations
[ServiceContract] interface IMyContract { [OperationContract]Stream
StreamReply1( ); [OperationContract] void StreamReply2(out Stream
stream); [OperationContract] void StreamRequest(Stream
stream); [OperationContract(IsOneWay = true
)] void OneWayStream(Stream
stream); }
Note that you can only define as an operation parameter the abstract class Stream
or a specific serializable subclass such as MemoryStream
. Subclasses such as FileStream
are not serializable and you will have to use the base Stream
.
WCF lets services stream the reply, the request, or both the request and the reply, using one-way streaming.
To stream the reply, either return the Stream
from the operation:
[OperationContract]Stream
GetStream1( );
or provide the stream as an out parameter:
[OperationContract]
void GetStream2(out Stream
stream);
To stream the request, provide a Stream
as a method parameter:
[OperationContract]
void SetStream1(Stream
stream);
Finally, you can even stream the request for a one-way operation:
//One-way streaming [OperationContract(IsOneWay = true
)] void SetStream2(Stream
stream);
Only the BasicHttpBinding
, NetTcpBinding
, and NetNamedPipeBinding
support streaming. With all these bindings streaming is disabled by default, and the binding will buffer the message in its entirety, even when a Stream
is used. You have to enable streaming by setting the TransferMode
Boolean property according to the desired streaming mode; for example, when using BasicHttpBinding
:
public enum TransferMode { Buffered, //Default Streamed, StreamedRequest, StreamedResponse } public class BasicHttpBinding : Binding,... { public TransferMode TransferMode {get;set;} //More members }
TransferMode.Streamed
supports all streaming modes, and is the only transfer mode that can support all the operations in Example 5-17. However, if the contract contains only a specific type of streaming such as streamed reply:
[ServiceContract]
interface IMyContract
{
//Stream reply
[OperationContract]Stream
GetStream1( );
[OperationContract]
int MyMethod( );
}
you can have a buffered request and streamed reply by selecting TransferMode.StreamedResponse
.
You will need to configure the binding on the client or service side (or both) per the required stream mode:
<configuration>
<system.serviceModel>
<client>
<endpoint
binding = "basicHttpBinding"
bindingConfiguration = "StreamedHTTP"
...
/>
</client>
<bindings>
<basicHttpBinding>
<binding name = "StreamedHTTP"transferMode = "Streamed"
>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
It is important to realize that WCF streaming is merely a programming model nicety. The underlying transport itself (such as HTTP) is not streamed, and the default maximum message size is set to 64 KB. This may be a problem with the sort of data you are likely to use streaming with, because streamed messages tend to be very large (hence the motivation for streaming in the first place). You may find the need to increase the max message size on the receiving side to accommodate the large message by setting the MaxReceivedMessageSize
property to the expected maximum message size:
public class BasicHttpBinding : Binding,... { public long MaxReceivedMessageSize {get;set;} //More memebrs }
Typically you would place that piece of configuration in the config file and avoid doing it programmatically, as message size tends to be deployment-specific:
<bindings>
<basicHttpBinding>
<binding name = "StreamedHTTP" transferMode = "Streamed"maxReceivedMessageSize = "120000"
>
</binding>
</basicHttpBinding>
</bindings>
When the client passes a request stream to the service, the service may read from the stream long after the client is gone. The client has no way of knowing when the service is done using the stream. Consequently the client should not close the stream—WCF will automatically close the client-side stream once the service is done using the stream.
A similar problem exists when the client interacts with a response stream. The stream was produced on the service side, and yet the service does not know when the client is done using the stream, nor can WCF help, because it has no idea what the client is doing with the stream. The client is always responsible for closing reply streams.
When you use streaming, you cannot use message-level transfer security. You will see more on security in Chapter 10. When streaming with the TCP binding, you also cannot enable reliable messaging.
There a few additional implications on streamed messages: first you need to synchronize access to the streamed content; for example, by opening the file stream in a read-only mode to allow other parties to access the file, or opening the stream in an exclusive mode to prevent others from accessing it if so required. In addition, you cannot use streaming with a sessionful service—the session implies a lock-step execution and has a well-defined demarcation, unlike streaming, which may be continuous for a long period of time.