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>
Community
  • 1
  • 1
Shaul Behr
  • 36,951
  • 69
  • 249
  • 387
  • 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() {`. – Allon Guralnek Dec 26 '10 at 19:21
  • @Allon - why not implement IWebServiceCallback explicitly? Why should that make a difference? – Shaul Behr Dec 26 '10 at 19:26
  • Have edited question to show app.config on either side – Shaul Behr Dec 26 '10 at 19:32
  • @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. – Allon Guralnek Dec 26 '10 at 19:38
  • @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? – Allon Guralnek Dec 26 '10 at 19:53
  • @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...? – Shaul Behr Dec 27 '10 at 07:59

2 Answers2

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! :)

Shaul Behr
  • 36,951
  • 69
  • 249
  • 387
0

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

  • 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. – Shaul Behr Dec 26 '10 at 19:21