Удаленная конечная точка запросила адрес для подтверждений, который не совпадает с адресом для сообщений приложения

Я нахожусь в процессе написания дуплексной службы WCF для приложения чата с клиентом WPF. Сервисный код ниже

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;
    }
}

Это размещено в консольном приложении с конечной точкой 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>

В моем клиенте 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
{
}

и 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
}

С конфигурацией клиента в основном приложении:

<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>

Хорошо, все хорошо, кроме того, по какой-то причине я получаю сообщение об ошибке при запуске следующего кода для подключения к службе, код:

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);
        }
    }

...
}

Я получаю ошибку:

Удаленная конечная точка запросила адрес для подтверждений, который не совпадает с адресом для сообщений приложения. Канал не может быть открыт, потому что это не поддерживается. Убедитесь, что адрес конечной точки, использованный для создания канала, идентичен тому, с которым была настроена удаленная конечная точка.

Мой вопрос (и извините за очень длинный пост, но я полностью застрял на этом) состоит в том, просто сталкивался ли кто-нибудь с этим и мог бы указать мне на ответ, пожалуйста?

РЕДАКТИРОВАТЬ: я обновил, чтобы включить полные разделы serviceModel от сервера и клиента, а также обновил ProxySingleton, чтобы показать, что он реализует интерфейс обратного вызова

1 ответ

Решение

Вот полностью функциональная настройка для вашего ChatService:

Ведущий:

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());
        }
    }
}

Клиент:

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);
        }
    }
}

Обратный звонок клиента:

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);
    }
}

Конфигурация хоста:

  <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>

Конфигурация клиента:

<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>

Выход хоста:

Выход клиента 1:

Выход клиента 2:

Заметки:

ServiceReference1 пространство имен по умолчанию, назначенное Visual Studio сгенерированному прокси-клиенту ChatServiceClient,

ChatLib это локально назначенное пространство имен для вашего ChatService реализация.

Другие вопросы по тегам