在WCF发布/订阅设置中,我现在有一个Unsubscribe()方法,以便在客户端关闭或需要停止侦听时正常地断开客户端与WCF主机的连接;但是,这并不能处理客户端强行或异常中止的情况,例如计算机本身断电。如果客户端应用程序以这种方式死亡,则其通道仍然存在,并且下次尝试发送消息时,发布者会收到以下错误:
ExceptionDetail> was caught
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it has been Aborted.
客户端匿名订阅,发布者遵循多播结构(任何订阅的客户端/频道都应接收该消息)。虽然我能够捕获异常,但我不知道如何从代码中的这一点挑出故障通道以便处理它并允许其他客户端继续接收消息。我的发布代码类似于以下内容:
public static void Publish(DateTime sendTimeStamp, DataTable sendTable)
{
InstanceContext context = new InstanceContext(null, new PublishStatus());
MessagesClient publishingClient = new MessagesClient(context);
try {
publishingClient.PublishMessage(sendTimeStamp, sendTable);
if (publishingClient.State != CommunicationState.Faulted)
publishingClient.Close();
else
publishingClient.Abort();
}
catch (CommunicationException ex)
{
// This is where the error is caught
}
catch (TimeoutException ex)
{
publishingClient.Abort();
}
catch (Exception ex)
{
publishingClient.Abort();
throw ex;
}
}
是否有可能将故障通道与此点隔离(异常首先解决问题)并将其丢弃,以便发布服务本身可以继续发送消息?
答案 0 :(得分:0)
经过一些试验和错误以及异常研究后,我的WCF主机中的另一个try-catch块能够取消订阅错误中止的客户端,并防止错误返回到发布服务。在这里发布一个简单的版本,以防其他人偶然发现同一类型的问题:
public static event MessageEventHandler MessageEvent;
public delegate void MessageEventHandler(object sender, ServiceEventArgs e);
IClientContract callback = null;
MessageEventHandler messageHandler = null;
public void Subscribe()
{
callback = OperationContext.Current.GetCallbackChannel<IClientContract>();
messageHandler = new MessageEventHandler(Publish_NewMessageEvent);
MessageEvent += messageHandler;
}
public void Unsubscribe()
{
MessageEvent -= messageHandler;
}
public void PublishMessage(DateTime timeStamp, DataTable table)
{
ServiceEventArgs se = new ServiceEventArgs();
se.timeStamp = timeStamp;
se.table = table;
MessageEvent(this, se);
}
public void Publish_NewMessageEvent(object sender, ServiceEventArgs e)
{
try
{
// This callback was causing the error, as the client would no longer exist but the channel would still be open and trying to receive the message
callback.ReceiveMessage(e.timeStamp, e.table);
}
catch
{
// Unsubscribe the dead client.
Unsubscribe();
}
}