Duplex communication using NetTcpBinding - ContractFilter mismatch?
Asked Answered
V

2

2

I'm making slow and steady progress towards having a duplex communication channel open between a client and a server, using NetTcpBinding. (FYI, you can observe my newbie progress here and here!)

I'm now at the stage where I have successfully connected to my server, through the server's firewall, and the client can make requests of the server.

In the other direction, however, things aren't quite so happy. It works fine when testing on my own machine, but when testing over the internet, when I try to initiate a callback from the server side, I get an error:

The message with Action 'http://MyWebService/IWebService/HelloWorld' cannot be
processed at the receiver, 
due to a ContractFilter mismatch at the EndpointDispatcher. 
This may be because of either a contract mismatch (mismatched Actions between 
sender and receiver) 
or a binding/security mismatch between the sender and the receiver.  
Check that sender and receiver have the same contract and the same binding 
(including security requirements, e.g. Message, Transport, None).

Here are some of the key bits of code. First, the web interface:

[ServiceContract(Namespace = "http://MyWebService", SessionMode = SessionMode.Required, CallbackContract = typeof(ISiteServiceExternal))]
public interface IWebService {
  [OperationContract]
  void Register(long customerID);
}

public interface ISiteServiceExternal {
  [OperationContract]
  string HelloWorld();
}

Here's the implementation of IWebService:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class WebService : IWebService {
  void IWebService.Register(long customerID) {
    Console.WriteLine("customer {0} registering", customerID);
    var callbackService = OperationContext.Current.GetCallbackChannel<ISiteServiceExternal>();
    RegisterClient(customerID, callbackService);
    Console.WriteLine("customer {0} registered", customerID);
  }
}

Then, on the client side (I was fiddling with these attributes without really knowing what I'm doing):

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, Namespace="http://MyWebService")]
class SiteServer : IWebServiceCallback {
  string IWebServiceCallback.HelloWorld() {
return "Hello World!";
  }
  ...
}

So what am I doing wrong here?

EDIT: Adding app.config code. From server:

<system.serviceModel>
  <diagnostics>
    <messageLogging logMalformedMessages="true" logMessagesAtServiceLevel="true"
        logMessagesAtTransportLevel="true" logEntireMessage="true" maxMessagesToLog="1000" maxSizeOfMessageToLog="524288" />
  </diagnostics>
  <behaviors>
    <serviceBehaviors>
      <behavior name="mex">
        <serviceDebug includeExceptionDetailInFaults="true"/>
        <serviceMetadata/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <services>
    <service name ="MyWebService.WebService" behaviorConfiguration="mex">
      <endpoint address="net.tcp://localhost:8000" binding="netTcpBinding" contract="MyWebService.IWebService"
                bindingConfiguration="TestBinding" name="MyEndPoint"></endpoint>
      <endpoint address ="mex"
                binding="mexTcpBinding"
                name="MEX"
                contract="IMetadataExchange"/>
      <host>
        <baseAddresses>
          <add baseAddress="net.tcp://localhost:8000"/>
        </baseAddresses>
      </host>
    </service>
  </services>
  <bindings>
    <netTcpBinding>
      <binding name="TestBinding" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" portSharingEnabled="false">
        <readerQuotas maxDepth="32" maxStringContentLength ="8192" maxArrayLength ="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
        <security mode="None"/>
      </binding>
    </netTcpBinding>
  </bindings>
</system.serviceModel>

and on the client side:

<system.serviceModel>
  <bindings>
    <netTcpBinding>
      <binding name="MyEndPoint" closeTimeout="00:01:00" openTimeout="00:01:00"
          receiveTimeout="00:10:00" sendTimeout="00:01:00" transactionFlow="false"
          transferMode="Buffered" transactionProtocol="OleTransactions"
          hostNameComparisonMode="StrongWildcard" listenBacklog="10"
          maxBufferPoolSize="524288" maxBufferSize="65536" maxConnections="10"
          maxReceivedMessageSize="65536">
        <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
        <reliableSession ordered="true" inactivityTimeout="00:10:00"
            enabled="false" />
        <security mode="None">
          <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign">
            <extendedProtectionPolicy policyEnforcement="Never" />
          </transport>
          <message clientCredentialType="Windows" />
        </security>
      </binding>
    </netTcpBinding>
  </bindings>
  <client>
    <endpoint address="net.tcp://mydomain.gotdns.com:8000/" binding="netTcpBinding"
        bindingConfiguration="MyEndPoint" contract="IWebService" name="MyEndPoint" />
  </client>
</system.serviceModel>
Vrablik answered 26/12, 2010 at 18:21 Comment(6)
How does your App.config/Web.config look like? How are you instantiating your auto-generated service (are you passing in an instance of SiteServer)? A few suggestions to simplify things: Don't specify a namespace anywhere (on the client or the server), don't implement the IWebServiceCallback explicitly (by removing IWebServiceCallback. from string IWebServiceCallback.HelloWorld() {.Plains
@Allon - why not implement IWebServiceCallback explicitly? Why should that make a difference?Vrablik
Have edited question to show app.config on either sideVrablik
@Shaul: No, I don't think it wouldn't make a difference, that was a suggestion to clean up the code, unless you actually need to make it explicit for some reason. Though I think such a reason is that your SiteServer class may be used for other things (e.g. business logic), which I recommend against, to allow for the flexibility to UseSynchronizationContext without affecting other aspects of ther class. Also, you should remove the ServiceBehaviorAttribute from the client side as it's not needed at this point.Plains
@Shaul, looks good. One question you didn't answer: are you passing an instance of SiteServer inside an instance of InstanceContext to the constructor of the generated WebServiceClient class?Plains
@Allon - in answer to your question I've added the WebService class code to the question... and I think I've just noticed something that may be the problem... that InstanceContextMode is not the same as the client (PerSession vs PerCall). Could that be it...?Vrablik
V
3

Kudos to @Allon Guralnek, who helped me notice what was wrong:

On the server side I had:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class WebService : IWebService { ... }

And on the client side I had:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, Namespace="http://MyWebService")]
class SiteServer : IWebServiceCallback { ... }

The conflict was between PerCall and PerSession. I just changed the server side to PerSession, and - Houston, we have lift-off!

Now, to get this working with security... watch SO for the next exciting installment in my WCF learning curve! :)

Vrablik answered 27/12, 2010 at 9:11 Comment(0)
M
0

You listed your callback contract as ISiteServiceExternal but your client implements IWebServiceCallback. Fix that first and see if you have success.

Mechanics answered 26/12, 2010 at 19:9 Comment(1)
Makes no difference. The IWebServiceCallback interface is generated by svcutil, and it ignores the original name of the callback interface. In any event that wouldn't explain why the callback works when I'm testing on my own machine, but not when testing over the internet.Vrablik

© 2022 - 2024 — McMap. All rights reserved.