I am writing a client/server application in C# using WCF. All my testings went fine, but as soon as I deployed the service, I noticed random problems in communicating with the server. I enabled debugging and saw messages like this in the server:
The communication object, System.ServiceModel.Channels.ServerReliableDuplexSessionChannel, cannot be used for communication because it has been Aborted.
The pattern is like this:
The application goes like this: The service instance provides an API methods to interact with a database and is of type "netTcpBinding". Several clients (about 40) are connected and randomly calling methods from the service. The clients can stay open for several days, even without sending or receiving anything.
Here are the relevant bits:
Service:
[ServiceContract(CallbackContract = typeof(ISVCCallback), SessionMode = SessionMode.Required)]
[ExceptionMarshallingBehavior]
...
and
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext=true)]
public class SVCService : ISVC
...
Service configuration:
<behaviors>
<serviceBehaviors>
<behavior name="behaviorConfig">
<serviceMetadata httpGetEnabled="false" httpGetUrl="" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceThrottling maxConcurrentCalls="50" maxConcurrentSessions="1000"
maxConcurrentInstances="50" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="tcpBinding" closeTimeout="00:01:00" openTimeout="00:10:00"
receiveTimeout="23:59:59" sendTimeout="00:01:30" transferMode="Buffered"
listenBacklog="1000" maxBufferPoolSize="671088640" maxBufferSize="671088640"
maxConnections="1000" maxReceivedMessageSize="671088640" portSharingEnabled="true">
<readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
maxBytesPerRead="671088640" />
<reliableSession inactivityTimeout="23:59:59" enabled="true" />
<security mode="None">
</security>
</binding>
</netTcpBinding>
</bindings>
Client configuration:
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_ISVC" closeTimeout="00:01:00" openTimeout="00:10:00"
receiveTimeout="23:59:59" sendTimeout="00:01:30" transactionFlow="false"
transferMode="Buffered" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="1000"
maxBufferPoolSize="671088640" maxBufferSize="671088640" maxConnections="1000"
maxReceivedMessageSize="671088640">
<readerQuotas maxStringContentLength="671088640" maxArrayLength="671088640"
maxBytesPerRead="671088640" />
<reliableSession ordered="true" inactivityTimeout="23:59:59"
enabled="true" />
<security mode="None">
<message clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
Is there anything wrong here? What is the best configuration for these kind of applications?
I encountered one thing:
In one service contract, I change something and notify all connected clients. It usually works fine, at least in my tests. But last "crash" or "freeze" I stepped through the log and saw that the latest function was where I use callback contracts to notify the clients.
What I want to do there: I save something to the database and at the end I notify all connected clients of the change. I think that the list of connected clients is not current anymore and it runs into a timeout at this step.
Now the question is how to avoid these timeouts.
Ok, problem is solved.
The problem occurred whenever one client died unexpectedly withouth logging out. So whenever a client called a method from the service which itself wanted to broadcast a message to all connected clients, it ran into a timeout which in turn blocked the client. So wrapping all callbacks up into a delegate function helped to solve the problem.
Here is how I did it:
public enum CallbackType
{
callbackfunc1,
callbackfunc2,
callbackfunc3
};
public class CallbackEventArgs : EventArgs
{
public ISVCCallback callback;
public CallbackType type;
public string s1;
public string s2;
public string s3;
public List<string> ls1;
}
// Declare delegate
public delegate void SVCEventHandler(object sender, CallbackEventArgs e);
... now in the service definition
public string Login(...)
{
Client cl = new Client(MyCallbackHandler);
....
CallbackEventArgs ce = new CallbackEventArgs();
ce.callback = cl.CallbackChannel;
ce.type = CallbackType.callbackfunc1;
ce.s1 = "Parameter A";
ce.s2 = "Parameter B";
cl.MyCallBackHandler.BeginInvoke(this, ce, new AsyncCallback(EndAsync), null);
}
private void MyCallbackHandler(object sender, CallbackEventArgs e)
{
try
{
switch (e.type)
{
case CallbackType.callbackfunc1:
e.callback.callbackfunc1(e.s1, e.s2);
break;
default:
throw new MissingFieldException(e.type);
}
}
catch(TimeoutException tex){
// Remove current client, channel timed out
}
catch(Exception ex){
// Do something
}
}
private void EndAsync(IAsyncResult iar)
{
SVCEventHandler d = null;
try
{
System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messaging.AsyncResult)iar;
d = ((SVCEventHandler)asres.AsyncDelegate);
d.EndInvoke(ar);
}
catch(Exception ex)
{
// Do something
}
}
Thanks!!