WCF双工通道:检查回调通道是否仍然可用
我有以下问题。我在写聊天软件。客户机/服务器机制基于WCF的DualHttpBinding。这意味着,如果用户发送消息,服务器将通知发送消息的房间中的所有客户端 我想确保,如果客户机的应用程序崩溃(无论是什么原因),客户机对象将从文件室列表中删除 在调用回调操作之前,是否可以检查回调通道的状态?问题是,如果我在不再连接的客户机上调用操作(由于意外崩溃),服务将挂起WCF双工通道:检查回调通道是否仍然可用,wcf,client,duplex-channel,Wcf,Client,Duplex Channel,我有以下问题。我在写聊天软件。客户机/服务器机制基于WCF的DualHttpBinding。这意味着,如果用户发送消息,服务器将通知发送消息的房间中的所有客户端 我想确保,如果客户机的应用程序崩溃(无论是什么原因),客户机对象将从文件室列表中删除 在调用回调操作之前,是否可以检查回调通道的状态?问题是,如果我在不再连接的客户机上调用操作(由于意外崩溃),服务将挂起 public YagzResult SendMessage(Message message) { fore
public YagzResult SendMessage(Message message)
{
foreach (ChatNodeAddress chatNodeAddress in message.Destination)
{
ChatNode chatNode = chatProvider.FindChatNode(chatNodeAddress);
if (chatNode != null)
{
User currentUser = CurrentUser;
foreach (User user in chatNode)
{
//Don't notify the current client. Deadlock!
if (!user.Equals(currentUser))
{
//Get the callback channel here
IYagzClient client = GetClientByUser(user);
if (client != null)
{
//--> If the client here called is not any more available,
//the service will hang <---
client.OnChatMessageReceived(message);
}
}
}
}
else
{
return YagzResult.ChatNodeNotFound;
}
}
return YagzResult.Ok;
}
公共YagzResult发送消息(消息消息)
{
foreach(message.Destination中的chatnodeddress chatnodeddress)
{
ChatNode ChatNode=chatProvider.FindChatNode(chatnodeddress);
if(chatNode!=null)
{
用户当前用户=当前用户;
foreach(chatNode中的用户)
{
//不要通知当前客户端。死锁!
如果(!user.Equals(currentUser))
{
//在这里获取回调通道
IYagzClient=GetClientByUser(用户);
如果(客户端!=null)
{
//-->如果这里调用的客户端不再可用,
//该服务将挂起已关闭和出现故障的通信对象(即回调通道)上存在事件。您可能希望为这些事件添加处理程序,并跟踪哪些客户端仍有可用的有效通道
您还可以查看IChannelInitializer类,以实现对客户端的跟踪。主要问题是,除了a之外,我没有收到任何异常。我的服务被阻止了1分钟(我设置的超时),直到触发异常 我通过以下解决方法解决了这个问题。我没有在服务的当前工作线程上调用客户端回调操作,而是创建了一个新线程,该线程调用客户端回调操作并等待TimeoutException。如果发生超时,用户只需从他所属的聊天室列表中删除 这是一段代码片段,展示了我是如何做到这一点的: 首先,我创建了一个类,表示对客户端的单个调用:
class YagzClientAsyncCall<T>
{
/// <summary> Gets or sets the parameter of the client callback. </summary>
/// <value> The parameter. </value>
T Param { get; set; }
/// <summary> Gets or sets the client. </summary>
/// <value> The client. </value>
IYagzClient Client { get; set; }
/// <summary> Gets or sets the service. </summary>
/// <value> The service. </value>
YagzService Service { get; set; }
/// <summary> Constructor. </summary>
/// <remarks> Simon, 30.12.2009. </remarks>
/// <param name="service"> The service. </param>
/// <param name="client"> The client. </param>
/// <param name="param"> The parameter. </param>
public YagzClientAsyncCall(YagzService service, IYagzClient client, T param)
{
Param = param;
Client = client;
}
/// <summary>
/// Invokes the client callback. If a timeout exception occurs,
/// the client will be removed from clients' list.
/// </summary>
/// <remarks> Simon, 30.12.2009. </remarks>
/// <param name="clientCallback"> The client callback. </param>
protected void Invoke(Action<T> clientCallback)
{
try
{
if (clientCallback != null)
{
clientCallback(Param);
}
}
catch (TimeoutException)
{
// Remove the client and the user
Service.RemoveClient(Client);
}
}
protected void Invoke(object objCallback)
{
Invoke(objCallback as Action<T>);
}
public void CallOperationAsync(Action<T> clientCallback)
{
ParameterizedThreadStart ts = new ParameterizedThreadStart(this.Invoke);
Thread t = new Thread(ts);
t.Start(clientCallback);
}
}
类YagzClientAsyncCall
{
///获取或设置客户端回调的参数。
///参数。
T参数{get;set;}
///获取或设置客户端。
///客户。
IYagzClient客户机{get;set;}
///获取或设置服务。
///服务。
YagzService服务{get;set;}
///构造器。
///西蒙,30.12.2009。
///服务。
///客户。
///参数。
公共YAGZClient同步调用(YagzService服务、IYagzClient客户端、T参数)
{
Param=Param;
客户=客户;
}
///
///调用客户端回调。如果发生超时异常,
///客户端将从客户端列表中删除。
///
///西蒙,30.12.2009。
///客户端回调。
受保护的无效调用(操作clientCallback)
{
尝试
{
if(clientCallback!=null)
{
clientCallback(Param);
}
}
捕获(超时异常)
{
//删除客户端和用户
服务。RemoveClient(客户);
}
}
受保护的void调用(对象objCallback)
{
调用(objCallback作为操作);
}
public void CallOperationAsync(Action clientCallback)
{
ParameterizedThreadStart ts=新的ParameterizedThreadStart(this.Invoke);
螺纹t=新螺纹(ts);
t、 启动(clientCallback);
}
}
假设以下代码是通知聊天室客户端已写入新消息的方法的一部分:
foreach (User user in chatNode)
{
// Don't notify the current client. Deadlock!
if (!user.Equals(currentUser))
{
IYagzClient client = GetClientByUser(user);
if (client != null)
{
var asyncCall = new YagzClientAsyncCall<Message>(this, client, message);
asyncCall.CallOperationAsync(client.OnChatMessageReceived);
}
}
}
foreach(chatNode中的用户)
{
//不要通知当前客户端。死锁!
如果(!user.Equals(currentUser))
{
IYagzClient=GetClientByUser(用户);
如果(客户端!=null)
{
var asyncCall=new YagzClientAsyncCall(此,客户端,消息);
asyncCall.CallOperationAsync(client.OnChatMessageReceived);
}
}
}
我只是创建了一个新的YagzClientAsyncCall对象,并允许在一个新线程上调用该操作。您可以将回调协定强制转换为,然后检查通道状态。虽然这可能会起作用,但按需创建这样的线程通常是不好的做法。您可能最终会有1000个不需要的线程。最好使用bocking collection从中读取的固定线程数较少。