简体   繁体   English

远程端点请求的确认地址与应用程序消息的地址不同

[英]The remote endpoint requested an address for acknowledgements that is not the same as the address for application messages

I am in the process of writing a WCF Duplex service for a chat application with a WPF client.我正在为带有 WPF 客户端的聊天应用程序编写 WCF Duplex 服务。 The service code is below服务代码如下

IChatCallback聊天回调

public interface IChatCallback
{
    #region Public Methods and Operators

    [OperationContract(IsOneWay = true)]
    void Receive(Person sender, string message);

    [OperationContract(IsOneWay = true)]
    void ReceiveWhisper(Person sender, string message);

    [OperationContract(IsOneWay = true)]
    void UserEnter(Person person);

    [OperationContract(IsOneWay = true)]
    void UserLeave(Person person);

    #endregion
}

IChatService聊天服务

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IChatCallback))]
public interface IChatService
{
    #region Public Methods and Operators

    [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
    void Say(string message);

    [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false)]
    void Whisper(string to, string message);

    [OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false)]
    Person[] Join(Person person);

    [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)]
    void Leave();

    #endregion
}

ChatService聊天服务

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ChatService : IChatService
{
    #region Static Fields

    private static object _syncObj = new object();

    private static Dictionary<Person, ChatEventHandler> _chatters = new Dictionary<Person, ChatEventHandler>();

    #endregion

    #region Fields

    private IChatCallback _callback = null;

    private ChatEventHandler _myEventHandler;

    private Person _person;

    #endregion

    #region Delegates

    public delegate void ChatEventHandler(object sender, ChatEventArgs e);

    #endregion

    #region Public Events

    public static event ChatEventHandler ChatEvent;

    #endregion

    #region Public Methods and Operators

    public void Say(string message)
    {
        ChatEventArgs e = new ChatEventArgs(MessageType.Receive, this._person, message);
        this.BroadcastMessage(e);
    }

    public void Whisper(string to, string message)
    {
        ChatEventArgs e = new ChatEventArgs(MessageType.ReceiveWhisper, this._person, message);
        try
        {
            ChatEventHandler chatterTo;
            lock (_syncObj)
            {
                chatterTo = this.GetPersonHandler(to);
                if (chatterTo == null)
                {
                    throw new KeyNotFoundException(
                        string.Format(
                            CultureInfo.InvariantCulture,
                            "The person with name [{0}] could not be found",
                            to));
                }
            }

            chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
        }
        catch (KeyNotFoundException)
        {
        }
    }

    public Person[] Join(Person person)
    {
        bool userAdded = false;
        this._myEventHandler = new ChatEventHandler(this.MyEventHandler);

        lock (_syncObj)
        {
            if (!this.CheckIfPersonExists(person.Name) && person != null)
            {
                this._person = person;
                _chatters.Add(person, this.MyEventHandler);
                userAdded = true;
            }
        }

        if (userAdded)
        {
            this._callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
            ChatEventArgs e = new ChatEventArgs(MessageType.UserEnter, this._person);
            this.BroadcastMessage(e);
            ChatEvent += this._myEventHandler;
            Person[] list = new Person[_chatters.Count];

            lock (_syncObj)
            {
                _chatters.Keys.CopyTo(list, 0);
            }

            return list;
        }
        else
        {
            return null;
        }
    }

    public void Leave()
    {
        if (this._person == null)
        {
            return;
        }

        ChatEventHandler chatterToRemove = this.GetPersonHandler(this._person.Name);

        lock (_syncObj)
        {
            _chatters.Remove(this._person);
        }

        ChatEvent -= chatterToRemove;
        ChatEventArgs e = new ChatEventArgs(MessageType.UserLeave, this._person);
        this.BroadcastMessage(e);
    }

    #endregion

    private void MyEventHandler(object sender, ChatEventArgs e)
    {
        try
        {
            switch (e.MessageType)
            {
                case MessageType.Receive:
                    this._callback.Receive(e.Person, e.Message);
                    break;

                case MessageType.ReceiveWhisper:
                    this._callback.ReceiveWhisper(e.Person, e.Message);
                    break;

                case MessageType.UserEnter:
                    this._callback.UserEnter(e.Person);
                    break;

                case MessageType.UserLeave:
                    this._callback.UserLeave(e.Person);
                    break;
            }
        }
        catch
        {
            this.Leave();
        }
    }

    private void BroadcastMessage(ChatEventArgs e)
    {
        ChatEventHandler temp = ChatEvent;
        if (temp != null)
        {
            foreach (ChatEventHandler handler in temp.GetInvocationList())
            {
                handler.BeginInvoke(this, e, new AsyncCallback(this.EndAsync), null);
            }
        }
    }

    private bool CheckIfPersonExists(string name)
    {
        foreach (Person p in _chatters.Keys)
        {
            if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
            {
                return true;
            }
        }

        return false;
    }

    private void EndAsync(IAsyncResult ar)
    {
        ChatEventHandler d = null;

        try
        {
            AsyncResult asres = (AsyncResult)ar;
            d = (ChatEventHandler)asres.AsyncDelegate;
            d.EndInvoke(ar);
        }
        catch
        {
            ChatEvent -= d;
        }
    }

    private ChatEventHandler GetPersonHandler(string name)
    {
        foreach (Person p in _chatters.Keys)
        {
            if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
            {
                ChatEventHandler chatTo = null;
                _chatters.TryGetValue(p, out chatTo);
                return chatTo;
            }
        }

        return null;
    }
}

This is hosted in a console application with an endpoint of net.tcp://localhost:33333/chatservice using the netTcpBinding with the following binding configuration这托管在控制台应用程序中,端点为 net.tcp://localhost:33333/chatservice,使用 netTcpBinding 和以下绑定配置

<system.serviceModel>
  <services>
    <service name="Cleo.Services.Chat.ChatService" behaviorConfiguration="CleoChatBehavior">
      <host>
        <baseAddresses>
          <add baseAddress="net.tcp://localhost:33333/chatservice"/>
        </baseAddresses>
      </host>
      <endpoint address="" binding="netTcpBinding" bindingConfiguration="DuplexBinding" contract="Cleo.Services.Chat.IChatService"/>
    </service>
  </services>

  <behaviors>
    <serviceBehaviors>
      <behavior name="CleoChatBehavior">
        <serviceThrottling maxConcurrentSessions="10000"/>
        <serviceDebug includeExceptionDetailInFaults="true"/>
      </behavior>
    </serviceBehaviors>
  </behaviors>

  <bindings>
    <netTcpBinding>
      <binding name="DuplexBinding" maxBufferSize="67108864" maxReceivedMessageSize="67108864" maxBufferPoolSize="67108864" transferMode="Buffered" closeTimeout="00:00:10" openTimeout="00:00:10" receiveTimeout="00:20:00" sendTimeout="00:01:00" maxConnections="100">
        <reliableSession enabled="true" inactivityTimeout="00:20:00" />
        <security mode="None" />
        <readerQuotas maxArrayLength="67108864" maxBytesPerRead="67108864" maxStringContentLength="67108864" />
      </binding>
    </netTcpBinding>
  </bindings>
</system.serviceModel>

In my WPF client I have implemented a proxy to the service using svcutil which is below:在我的 WPF 客户端中,我使用 svcutil 实现了服务的代理,如下所示:

IChatServiceCallback聊天服务回调

[GeneratedCode("System.ServiceModel", "4.0.0.0")]
public interface IChatServiceCallback
{
    #region Public Methods and Operators

    [OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChatService/Receive")]
    void Receive(Person sender, string message);

    [OperationContract(IsOneWay = true, AsyncPattern = true, Action = "http://tempuri.org/IChatService/Receive")]
    IAsyncResult BeginReceive(Person sender, string message, AsyncCallback callback, object asyncState);

    void EndReceive(IAsyncResult result);

    [OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChatService/ReceiveWhisper")]
    void ReceiveWhisper(Person sender, string message);

    [OperationContract(IsOneWay = true, AsyncPattern = true, 
        Action = "http://tempuri.org/IChatService/ReceiveWhisper")]
    IAsyncResult BeginReceiveWhisper(Person sender, string message, AsyncCallback callback, object asyncState);

    void EndReceiveWhisper(IAsyncResult result);

    [OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChatService/UserEnter")]
    void UserEnter(Person person);

    [OperationContract(IsOneWay = true, AsyncPattern = true, Action = "http://tempuri.org/IChatService/UserEnter")]
    IAsyncResult BeginUserEnter(Person person, AsyncCallback callback, object asyncState);

    void EndUserEnter(IAsyncResult result);

    [OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChatService/UserLeave")]
    void UserLeave(Person person);

    [OperationContract(IsOneWay = true, AsyncPattern = true, Action = "http://tempuri.org/IChatService/UserLeave")]
    IAsyncResult BeginUserLeave(Person person, AsyncCallback callback, object asyncState);

    void EndUserLeave(IAsyncResult result);

    #endregion
}

IChatService聊天服务

[GeneratedCode("System.ServiceModel", "4.0.0.0")]
[ServiceContract(ConfigurationName = "IChatService", CallbackContract = typeof(IChatServiceCallback), 
    SessionMode = SessionMode.Required)]
public interface IChatService
{
    #region Public Methods and Operators

    [OperationContract(IsOneWay = true, IsInitiating = false, Action = "http://tempuri.org/IChatService/Say")]
    void Say(string message);

    [OperationContract(IsOneWay = true, IsInitiating = false, AsyncPattern = true, 
        Action = "http://tempuri.org/IChatService/Say")]
    IAsyncResult BeginSay(string message, AsyncCallback callback, object asyncState);

    void EndSay(IAsyncResult result);

    [OperationContract(IsOneWay = true, IsInitiating = false, Action = "http://tempuri.org/IChatService/Whisper")]
    void Whisper(string to, string message);

    [OperationContract(IsOneWay = true, IsInitiating = false, AsyncPattern = true, 
        Action = "http://tempuri.org/IChatService/Whisper")]
    IAsyncResult BeginWhisper(string to, string message, AsyncCallback callback, object asyncState);

    void EndWhisper(IAsyncResult result);

    [OperationContract(Action = "http://tempuri.org/IChatService/Join", 
        ReplyAction = "http://tempuri.org/IChatService/JoinResponse")]
    Person[] Join(Person person);

    [OperationContract(AsyncPattern = true, Action = "http://tempuri.org/IChatService/Join", 
        ReplyAction = "http://tempuri.org/IChatService/JoinResponse")]
    IAsyncResult BeginJoin(Person person, AsyncCallback callback, object asyncState);

    Person[] EndJoin(IAsyncResult result);

    [OperationContract(IsOneWay = true, IsTerminating = true, IsInitiating = false, 
        Action = "http://tempuri.org/IChatService/Leave")]
    void Leave();

    [OperationContract(IsOneWay = true, IsTerminating = true, IsInitiating = false, AsyncPattern = true, 
        Action = "http://tempuri.org/IChatService/Leave")]
    IAsyncResult BeginLeave(AsyncCallback callback, object asyncState);

    void EndLeave(IAsyncResult result);

    #endregion
}

IChatServiceChannel聊天服务频道

[GeneratedCode("System.ServiceModel", "4.0.0.0")]
public interface IChatServiceChannel : IChatService, IClientChannel
{
}

and ChatProxy和 ChatProxy

[DebuggerStepThrough]
[GeneratedCode("System.ServiceModel", "4.0.0.0")]
public class ChatProxy : DuplexClientBase<IChatService>, IChatService
{
    #region Constructors and Destructors

    public ChatProxy(InstanceContext callbackInstance)
        : base(callbackInstance)
    {
    }

    public ChatProxy(InstanceContext callbackInstance, string endpointConfigurationName)
        : base(callbackInstance, endpointConfigurationName)
    {
    }

    public ChatProxy(InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress)
        : base(callbackInstance, endpointConfigurationName, remoteAddress)
    {
    }

    public ChatProxy(
        InstanceContext callbackInstance, 
        string endpointConfigurationName, 
        EndpointAddress remoteAddress)
        : base(callbackInstance, endpointConfigurationName, remoteAddress)
    {
    }

    public ChatProxy(InstanceContext callbackInstance, Binding binding, EndpointAddress remoteAddress)
        : base(callbackInstance, binding, remoteAddress)
    {
    }

    #endregion

    #region Public Methods and Operators

    public void Say(string message)
    {
        this.Channel.Say(message);
    }

    public IAsyncResult BeginSay(string message, AsyncCallback callback, object asyncState)
    {
        return this.Channel.BeginSay(message, callback, asyncState);
    }

    public void EndSay(IAsyncResult result)
    {
        this.Channel.EndSay(result);
    }

    public void Whisper(string to, string message)
    {
        this.Channel.Whisper(to, message);
    }

    public IAsyncResult BeginWhisper(string to, string message, AsyncCallback callback, object asyncState)
    {
        return this.Channel.BeginWhisper(to, message, callback, asyncState);
    }

    public void EndWhisper(IAsyncResult result)
    {
        this.Channel.EndWhisper(result);
    }

    public Person[] Join(Person person)
    {
        return this.Channel.Join(person);
    }

    public IAsyncResult BeginJoin(Person person, AsyncCallback callback, object asyncState)
    {
        return this.Channel.BeginJoin(person, callback, asyncState);
    }

    public Person[] EndJoin(IAsyncResult result)
    {
        return this.Channel.EndJoin(result);
    }

    public void Leave()
    {
        this.Channel.Leave();
    }

    public IAsyncResult BeginLeave(AsyncCallback callback, object asyncState)
    {
        return this.Channel.BeginLeave(callback, asyncState);
    }

    public void EndLeave(IAsyncResult result)
    {
        this.Channel.EndLeave(result);
    }

    #endregion
}

With the client configuration in the main application:使用主应用程序中的客户端配置:

<system.serviceModel>
  <bindings>
    <wsHttpBinding>
      <binding name="CleoDefaultBinding" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="33554432" maxReceivedMessageSize="4194304" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
        <readerQuotas maxDepth="32" maxStringContentLength="4194304" maxArrayLength="32768" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
        <security mode="Transport">
          <transport clientCredentialType="Certificate" />
        </security>
      </binding>
    </wsHttpBinding>
    <netTcpBinding>
      <binding name="DuplexBinding" sendTimeout="00:00:30">
        <reliableSession enabled="true"/>
        <security mode="None"/>
      </binding>
    </netTcpBinding>
  </bindings>
  <client>
    <!-- Cleo Chat Client -->
    <endpoint name="CleoChatWcfServiceClient" address="net.tcp://localhost:33333/chatservice" binding="netTcpBinding" bindingConfiguration="DuplexBinding" contract="IChatService"/>
    <endpoint address="net.tcp://localhost:51638/services/chat/wcf" binding="netTcpBinding" bindingConfiguration="DuplexBinding" contract="CleoChatClient.ICleoChatWcfService" name="chatWcfService" />
  </client>
</system.serviceModel>

Ok, all good except for some reason i am getting an error when running the following code to connect to the service, the code is:好的,一切都很好,除了某种原因我在运行以下代码连接到服务时出错,代码是:

public class ProxySingleton : IChatServiceCallback
{
...
    public void Connect(Person p)
    {
        var site = new InstanceContext(this);
        this._proxy = new ChatProxy(site);
        var iar = this._proxy.BeginJoin(p, this.OnEndJoin, null);
    }

    private void OnEndJoin(IAsyncResult ar)
    {
        try
        {
            var list = this._proxy.EndJoin(ar); --> Errors here!!
            this.HandleEndJoin(list);
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
        }
    }

...
}

The error i am getting is:我得到的错误是:

The remote endpoint requested an address for acknowledgements that is not the same as the address for application messages.远程端点请求的确认地址与应用程序消息的地址不同。 The channel could not be opened because this is not supported.无法打开通道,因为这不受支持。 Ensure the endpoint address used to create the channel is identical to the one the remote endpoint was set up with.确保用于创建通道的端点地址与设置远程端点的地址相同。

My question (and sorry for the very long post but I am completely stuck on this) is simply whether anyone else has come across this and could point me towards an answer please?我的问题(对于很长的帖子很抱歉,但我完全坚持这一点)只是是否有其他人遇到过这个问题并且可以指出我的答案吗?

EDIT: I have updated to include the full serviceModel sections from the server and client and also updated the ProxySingleton to show that it does implement the callback interface编辑:我已更新以包含来自服务器和客户端的完整 serviceModel 部分,并更新了 ProxySingleton 以显示它确实实现了回调接口

Here's a fully functional setup for your ChatService :这是ChatService功能齐全的设置:

Host:主持人:

class ProgramHost
{
    static void Main(string[] args)
    {
        try
        {
            ServiceHost host = new ServiceHost(typeof(ChatLib.ChatService));

            host.Open();

            Console.WriteLine(string.Format("WCF {0} host is running...", host.Description.ServiceType));

            Console.WriteLine("Endpoints:");
            foreach (ServiceEndpoint se in host.Description.Endpoints)
            {
                Console.WriteLine("***********************************************");
                Console.WriteLine(string.Format("Address = {0}", se.Address));
                Console.WriteLine(string.Format("Binding = {0}", se.Binding));
                Console.WriteLine(string.Format("Contract = {0}", se.Contract.Name));
            }

            Console.WriteLine(string.Empty);
            Console.WriteLine("Press <ENTER> to terminate.");

            Console.ReadLine();

            host.Close();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
}

Client:客户:

class ProgramClient
{
    static void Main(string[] args)
    {
        try
        {
            if (args.Length != 1)
                Console.WriteLine("usage: clientconsole username");
            else
            {
                Person user = new Person(args[0]);

                IChatServiceCallback callback = new SimpleChatCallback();

                InstanceContext instanceContext = new InstanceContext(callback);
                ChatServiceClient serviceProxy = new ChatServiceClient(instanceContext);

                Console.WriteLine("Endpoint:");
                Console.WriteLine("***********************************************");
                Console.WriteLine(string.Format("Address = {0}", serviceProxy.Endpoint.Address));
                Console.WriteLine(string.Format("Binding = {0}", serviceProxy.Endpoint.Binding));
                Console.WriteLine(string.Format("Contract = {0}", serviceProxy.Endpoint.Contract.Name));

                Person[] people = serviceProxy.Join(user);

                Console.WriteLine("***********************************************");
                Console.WriteLine("Connected !");
                Console.WriteLine("Online users:");

                foreach (Person p in people)
                    Console.WriteLine(p.Name);

                string msg;
                while ((msg = Console.ReadLine()) != "exit")
                    serviceProxy.Say(msg);

                serviceProxy.Leave();

                if (serviceProxy.State != CommunicationState.Faulted)
                    serviceProxy.Close();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

Client callback:客户端回调:

public class SimpleChatCallback : IChatServiceCallback
{
    public void Receive(Person sender, string message)
    {
        Console.WriteLine("{0}: {1}", sender.Name, message);
    }

    public void ReceiveWhisper(Person sender, string message)
    {
        Console.WriteLine("{0}: {1}", sender.Name, message);
    }

    public void UserEnter(Person person)
    {
        Console.WriteLine("{0} has entered", person.Name);
    }

    public void UserLeave(Person person)
    {
        Console.WriteLine("{0} has left", person.Name);
    }
}

Host config:主机配置:

  <system.serviceModel>
    <services>
      <service behaviorConfiguration="mexBehavior" name="ChatLib.ChatService">
        <clear />
        <endpoint address="ChatService.svc" binding="netTcpBinding" bindingConfiguration=""
          name="netTcpEndpoint" bindingName="NonSecureTcpBinding" contract="Common.IChatService" />
        <endpoint binding="mexHttpBinding" bindingConfiguration="mexHttpBinding"
          name="mexHttpEndpoint" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:33334/chatservice" />
            <add baseAddress="net.tcp://localhost:33333/chatservice" />
          </baseAddresses>
          <timeouts openTimeout="00:10:00" />
        </host>
      </service>
    </services>
    <bindings>
      <netTcpBinding>
        <binding name="NonSecureTcpBinding">
          <security mode="None">
            <transport clientCredentialType="None" protectionLevel="None" />
            <message clientCredentialType="None" />
          </security>
        </binding>
      </netTcpBinding>
      <mexHttpBinding>
        <binding name="mexHttpBinding" />
      </mexHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="mexBehavior">
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <remove scheme="net.tcp" />
      <add scheme="net.tcp" binding="netTcpBinding" bindingConfiguration="NonSecureTcpBinding" />
      <add scheme="https" binding="basicHttpsBinding" />
    </protocolMapping>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>

Client config:客户端配置:

<system.serviceModel>
    <bindings>
        <netTcpBinding>
            <binding name="netTcpEndpoint" />
        </netTcpBinding>
    </bindings>
    <client>
        <endpoint address="net.tcp://localhost:33333/chatservice/ChatService.svc"
            binding="netTcpBinding" bindingConfiguration="netTcpEndpoint"
            contract="ServiceReference1.IChatService" name="netTcpEndpoint">
            <identity>
                <userPrincipalName value="ComputerName\UserName" />
            </identity>
        </endpoint>
    </client>
</system.serviceModel>

Host output:主机输出:

在此处输入图片说明

Client 1 output:客户端 1 输出:

在此处输入图片说明

Client 2 output:客户端 2 输出:

在此处输入图片说明

Notes:笔记:

ServiceReference1 is the default namespace assigned by Visual Studio to the generated proxy client ChatServiceClient . ServiceReference1是 Visual Studio 分配给生成的代理客户端ChatServiceClient的默认命名空间。

ChatLib is the locally assigned namespace to your ChatService implementation. ChatLib是本地分配给您的ChatService实现的命名空间。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM