Returning Files as a Stream

I have a WCF contract that returns a stream. I am normally returning an XML file. This works fine, but whenever I return a 'null' or binary file, the WCF comm seems to get hosed.

This is a self-hosted console app. Here is my app.config.

<endpoint address="net.tcp://localhost:8002/ProLab/"
binding="netTcpBinding"
bindingConfiguration=""
contract="NCRx.ProLab.Common.IProLabServer" />

The interface is set up as a duplex interface as follows.

[ServiceContract(Namespace = "http://NCRx/ProLab/Server", SessionMode = SessionMode.Required, CallbackContract = typeof(IProLabWorkstation))]
interface IProLabServer
{
/// <summary>
/// Notifies the server that a workstation is ready to perform some work. Also provides access to the workstation.
/// </summary>
[OperationContract(IsOneWay=true)]
void StartSession(string workstationName, ProLabSessionType sessionType);

// functions for other systems to call server for information from the workstation.
}

/// <summary>
/// Defines the callback contract for
/// </summary>
interface IProLabWorkstation
{
// Server will call workstation with the following function form:
[OperationContract]
Stream SendCommand(Stream parameters);
}

The SendCommand callback is the call that gets hosed when passing back a null or binary file. Here is the code inside of SendCommand on the client side that passes back the file...

try
{
FileStream fsout = new FileStream(diffroot + "\\" + filename, FileMode.Open, FileAccess.Read);
Logger.Write("Sent {0}.\n", filename);
return fsout;
}
catch (FileNotFoundException)
{
Logger.Write("File {0} not available.\n", filename);
return (Stream)null;
}

The SendCommand on the server side thows a CommunicationsException: {"The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error."} System.SystemException {System.ServiceModel.CommunicationException}. After this, when the next call is made the client never sees the call.

Does anyone know what is causing WCF to get hosed?

Thank you for your help,

Steve

[2474 byte] By [SteveGraber] at [2007-12-30]
# 1
Can you enable tracing on the client to see if any exception is being thrown at that location to cause the CommunicationException at the server side? Thanks.
CarlosFigueira-MSFT at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 2

When I return 'null', this is the exception that is thrown on the client side...

Exception Type
System.ArgumentNullException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Message
Value cannot be null.
Parameter name: SendCommandResult

When I return the first binary file, size 671 KB, this is the exception that is thrown on the client side...

Exception Type

System.ServiceModel.CommunicationException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Message

The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:00:59.9687500'.

When I return the second binary file, size 68 KB, this is the exception that is thrown on the client side...

Exception Type

System.ServiceModel.CommunicationException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Message

The maximum message size quota for incoming messages has been exceeded for the remote channel. See the server logs for more details.

I guess you aren't allowed to return 'null' for a Stream. Is that true?

Do you have any ideas what is causing the exception for the binary file?

Thank you for your help,

Steve

SteveGraber at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 3

It's true, when you're using the Stream programming model (i.e., methods with a single Stream input parameter or a single Stream output parameter) you shouldn't return null. A workaround for this scenario could be to declare a FaultContract in the SendCommand operation that is thrown whenever a file can't be found:

public interface IProLabWorkstation
{
[
OperationContract]
[
FaultContract(typeof(String))]
Stream SendCommand(Stream parameters);
}

On the workstation implementation:

public Stream SendCommand(Stream parameters)
{
StreamReader sr = new StreamReader(parameters);
string strParameters = sr.ReadToEnd();
Console.WriteLine("strParameters = " + strParameters);
try
{
FileStream fs = File.OpenRead(strParameters);
Console.WriteLine("Sending " + strParameters);
return fs;
}
catch (FileNotFoundException)
{
throw new FaultException<String>(String.Format("File {0} not found", strParameters));
}
}

On the server:

try
{
Stream result = workstation.SendCommand(args);
int bytesRead, totalBytesRead = 0;
byte[] buffer = new byte[1000];
do
{
bytesRead = result.Read(buffer, 0, buffer.Length);
totalBytesRead += bytesRead;
}
while (bytesRead > 0);
Console.WriteLine("Result has {0} bytes", totalBytesRead);
}
catch (FaultException<string> e)
{
Console.WriteLine("Error sending command: " + e.Detail);
}

With respect to the maximum message size quota exception, by default the maximum size of messages that can be received by an endpoint is around 64kb. If you set it to a larger value (like in the config snipped below) you should be able to use larger files.

<bindings>
<netTcpBinding>
<binding name="NewBinding" maxReceivedMessageSize="500000" />
</netTcpBinding>
</bindings>

One extra information about streams. By default, all WCF messages are buffered, i.e., even if you declare the operation parameters as Stream, the whole stream data will be read before the operation code is invoked. You can change this behavior by setting the Streamed transfer mode on the binding (or on the TransportBindingElement, if using custom bindings).

CarlosFigueira-MSFT at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 4

Ok, throwing an exception instead of retuning a null works fine. Thank you.

I am still having a problem with the binary file transfer. I am using maxReceivedMessageSize="1000000" on both the client and server side, but the binary file transfer locks up the communications channel. I get an error when I set the transferMode="Streamed". This appears to cause problems with a duplex interface. I think transferMode="Streamed" works in transferring binary files for non duplex interfaces. We did this on the previous version of the application, using WCF with RC1. Should I be using transferMode="Streamed", and if so, how can I get it to work with a duplex interface?

Thank you for your help,

Steve

SteveGraber at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 5

Does anyone know how to return a binary file as a 'Stream' in a callback method of a WCF duplex interface?

Thank you for your help,

Steve

SteveGraber at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 6

NetTcpBinding supports TransferMode="Streamed", and it is inherently duplex, so I don't think that that is the issue here. Can you post your entire config? What error are you getting when setting TransferMode="Streamed" on your Tcp Binding?

DaveCliffe-MSFT at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 7

When I set the TransferMode to "Streamed" the server appears to start up fine, but when the client attempts to connect, I get the error message "The communication object, System.ServiceModel.DuplexChannelFactory'1[IProLabserver], cannot be used for communication because it is in the Faulted state".

Here is the App.config for my server...

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

<appSettings>
<!-- use appSetting to configure base address provided by host -->
<add key="baseAddress" value="http://localhost:8001/ProLab" />
</appSettings>

<system.serviceModel>

<services>
<service name="NCRx.ProLab.Server.ProLabServer" behaviorConfiguration="ProLabBehaviors">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8001/ProLab" />
</baseAddresses>
</host>

<endpoint address="http://localhost:8001/ProLab"
binding="wsDualHttpBinding"
contract="NCRx.ProLab.Common.IProLabServer" />

<endpoint address="net.tcp://localhost:8002/ProLab/"
binding="netTcpBinding"
bindingConfiguration=""
contract="NCRx.ProLab.Common.IProLabServer" />

<endpoint contract="IMetadataExchange"
binding="mexHttpBinding"
address="http://localhost:8001/ProLab/mex" />

</service>
</services>

<bindings>
<netTcpBinding>
<binding name="StreamedTcp" transferMode="Streamed" maxReceivedMessageSize="1000000">
</binding>
</netTcpBinding>
</bindings>

<behaviors>
<serviceBehaviors>
<behavior name="ProLabBehaviors" >
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>

</system.serviceModel>
</configuration>

Thank you for your help,

Steve

SteveGraber at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 8

I assume that you're actually using the bindingConfiguration that you specified in the binding section on the server, correct?

<endpoint address="net.tcp://localhost:8002/ProLab/"
binding="netTcpBinding"
bindingConfiguration="StreamedTcp"
contract="NCRx.ProLab.Common.IProLabServer" />

I just wanted to check that that wasn't the problem (i.e. you weren't using the settings that you specified). I'll keep digging.

DaveCliffe-MSFT at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 9

I am certain that this is the binding section being used on the server because, when I change the TransportMode to "Buffered" it works correctly.

The client is connecting using the following code.

string address = "net.tcp://" + m_serverAddress + "/ProLab/";

using (ProLabServerClient server = new ProLabServerClient(new InstanceContext(this), "TCPServerEndpoint", address))

Where m_serverAddress contains the IP of the server.

I believe this will connect to the server endpoint:

<endpoint address="net.tcp://localhost:8002/ProLab/"
binding="netTcpBinding"
bindingConfiguration="StreamedTcp"
contract="NCRx.ProLab.Common.IProLabServer" />

Thank you for your help.

Steve

SteveGraber at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 10

Also, here is the client App.config...(I removed the diagnostic section)

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

<system.serviceModel>

<client>

<!--address="net.tcp://192.168.0.142:8002/ProLab/"-->
<!-- Binary streaming endpoint -->
<endpoint name="TCPServerEndpoint"
address="net.tcp://70.104.114.19:8002/ProLab/"
binding="netTcpBinding"
bindingConfiguration="StreamedTcp"
contract="IProLabServer"
>
</endpoint>

</client>

<bindings>
<wsDualHttpBinding>
<binding name="TwoWayWsHttp" sendTimeout="00:01:00"></binding>
</wsDualHttpBinding>

<netTcpBinding>
<binding name="StreamedTcp" transferMode="Streamed" maxReceivedMessageSize="1000000">
</binding>
</netTcpBinding>
</bindings>


</system.serviceModel>
</configuration>

SteveGraber at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 11

Services which require sessions (property SessionMode=SessionMode.Required in the ServiceContract attribute) cannot be used with TransferMode=Streamed. Using streaming mode causes the transfer to be chunked (since the framework doesn't know how much data to send), and this doesn't go well with sessions.

BTW, sorry for the delay in responding to this thread, the notification of new messages isn't working for me.

CarlosFigueira-MSFT at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 12

Carlos,

Is there another way to transfer binary files to and from a service that requires a session (property SessionMode=SessionMode.Requires)? That is, transferring the file as part of the session.

Thank you for your help,

Steve

SteveGraber at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 13

You have two options here:

1) Use TransferMode.Buffered which will allow you to use the Stream programming model but will buffer the message under the covers

2) Implement a chunking channel above something such as TCP or RM where you chunk a Message into multiple smaller Messages.

http://kennyw.com/indigo/91

KennyWolf-MSFT at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 14

We are using the TransferMode.Buffered, but when we send a binary file, the comm channel gets hosed.

We have'nt tried implementing a chunking channel. Are there any examples on how to implement this type of solution?

Thank you for your help,

Steve

SteveGraber at 2007-9-5 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...

Visual Studio Orcas

Site Classified