Удаленная конечная точка запросила адрес для подтверждений, который не совпадает с адресом для сообщений приложения
Я нахожусь в процессе написания дуплексной службы 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
реализация.