Я нахожусь в процессе написания службы WCF Duplex для приложения чата с клиентом 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>
Ok, все хорошо, за исключением какой-то причине я получаю сообщение об ошибке при выполнении следующего кода для подключения к услуге, код:
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);
}
}
...
}
Ошибка, которую я получаю:
Удаленная конечная точка запросила адрес для подтверждений, который не совпадает с адресом для сообщений приложения. Не удалось открыть канал, потому что он не поддерживается. Убедитесь, что адрес конечной точки, используемый для создания канала, идентичен адресу, который был настроен для удаленной конечной точки.
Мой вопрос (и извините за очень длинный пост, но я полностью застрял на этом), просто ли кто-нибудь еще встретил это и мог бы указать мне на ответ, пожалуйста?
EDIT: Я обновлен, чтобы включить полные разделы ServiceModel с сервера и клиента, а также обновил ProxySingleton, чтобы показать, что он реализует интерфейс обратного вызова
Прежде всего, было бы намного лучше, если бы вы разместили весь раздел '' вашего конфигурационного файла. В вашем 'ProxySingleton' вы передаете' this' в качестве параметра 'InstanceContext'. Использует ли 'ProxySingleton'' IChatServiceCallback'? Кроме того, по какой-либо причине, по-видимому, вы не выполнили стандартную процедуру ** Add-> Service Reference ... ** и вручную создаете свой прокси-клиент службы? –
jsanalytics
Сообщения об ошибках WCF могут оказаться сложными для выяснения. Включение трассировки может быть очень полезно: [MSDN - настройка трассировки] (https://msdn.microsoft.com/en-us/library/ms733025%28v=vs.110%29.aspx) – jsanalytics
Создание клиента прокси-сервера службы самостоятельно это смелая задача. Ты сделал это? Если бы вы это сделали, я бы также предложил создать служебный прокси по стандартной процедуре (Add -> Service Reference) от вашего клиента. И в '' для узла приложения App.config вы можете добавить базовый адрес http, чтобы помочь создать прокси для вас из этого порта: ''. Обратите внимание на дополнительный ' ' –
Ian