我还没有找到明确的答案。所以,如果已经有一个关于这个的问题,我的不好。
我有一个WCF服务,它通过回调方法将数据推送到连接的客户端。这种回调方法是单向的。因此,每当有新数据时,我都会遍历连接的用户并推送数据。 我现在遇到的问题是当客户端断开连接时会抛出错误并且通道出现故障。 我一直认为,如果消息到达目的地,他们并不关心。所以,如果没有客户,那么运气不好。但也不例外。 但是有一个例外,该异常会使频道出现故障。
现在我已经读过某个地方,如果你启用了可靠的会话,那个例外就不会对频道造成错误。这是真的? 当单向呼叫发生异常时,如何防止信道进入故障状态?
答案 0 :(得分:1)
可以存储在某些资源(如List)中的已注册和可用客户端列表。
创建另一个公开Connect / Disconnect方法的接口。应用程序启动时将调用Connect,并将方法客户端添加到列表中。当应用程序关闭以便摆脱列表的客户端时,将调用断开连接。 OnStartup / OnClosing事件或其等价物,取决于应用程序客户端的类型,请参考应用程序启动和关闭的时刻。这样的解决方案可确保资源仅存储可以访问的用户。
[ServiceContract]
interface IConnection
{
[OperationContract(IsOneWay = true)]
void Connect();
[OperationContract(IsOneWay = true)]
void Disconnect();
}
[ServiceContract]
interface IServiceCallback
{
[OperationContract(IsOneWay = true)]
void CallbackMethod();
}
[ServiceContract(CallbackContract = typeof(IServiceCallback))]
interface IService
{
[OperationContract]
void DoSth();
}
class YourService : IConnection, IService
{
private static readonly List<IServiceCallback> Clients = new List<IServiceCallback>();
public void Connect()
{
var newClient = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
if (Clients.All(client => client != newClient))
Clients.Add(newClient);
}
public void Disconnect()
{
var client = OperationContext.Current.GetCallbackChannel<IServiceCallback>();
if (Clients.Any(cl => cl == client))
Clients.Remove(client);
}
public void DoSth()
{
foreach(var client in Clients)
client.CallbackMethod();
}
}
最后使用IConnection公开另一个端点,以便客户端可以创建仅用于连接/断开连接的代理。
编辑:
我知道已经有一段时间了,因为我发布了一件衣服,但我没有找到以便准备一个例子。解决方法是让服务的接口派生IConnection,然后仅将服务作为端点公开。我附上了WCF和WPF应用程序的简单示例作为客户端。客户端的应用程序违反了MVVM模式,但在这种情况下它是无关紧要的。下载here。
答案 1 :(得分:0)
添加Maximus所说的内容。
我已经在一个类中实现了这种模式,客户端可以订阅它来获取系统内部状态的更新,因此监视客户端可以显示图形,而其他客户端可以执行其他操作,例如,如果某些状态处于活动状态,则启用/禁用按钮。 它在失败时从列表中删除故障通道。当客户端连接时,也会发送所有当前状态。
这是代码,希望它有所帮助!
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class Publish : IPublish
{
private struct SystemState
{
public string State;
public string ExtraInfo;
}
private static Dictionary<Key<string>, IPublishCallback> mCallbacks = new Dictionary<Key<string>, IPublishCallback>();
private static Dictionary<string, SystemState> mStates = new Dictionary<string, SystemState>();
public void RegisterClient(string name, string system)
{
lock (mCallbacks)
{
IPublishCallback callback = OperationContext.Current.GetCallbackChannel<IPublishCallback>();
Key<string> key = new Key<string>(name, system);
if (!mCallbacks.ContainsKey(key))
{
mCallbacks.Add(key, callback);
}
else
{
mCallbacks[key] = callback;
}
foreach (KeyValuePair<string, SystemState> s in mStates)
{
mCallbacks[key].ServiceCallback(s.Key, s.Value.State, s.Value.ExtraInfo);
}
}
}
public void UnregisterClient(string name)
{
lock (mCallbacks)
{
outer: foreach (var key in mCallbacks.Keys)
{
if (key.Key1 == name)
{
mCallbacks.Remove(key);
goto outer;
}
}
}
}
public void SetState(string system, string state, string extraInfo)
{
lock (mCallbacks)
{
List<Key<string>> toRemove = new List<Key<string>>();
SystemState s = new SystemState() { State = state, ExtraInfo = extraInfo };
SystemState systemState;
if (!mStates.TryGetValue(system, out systemState))
mStates.Add(system, s);
else
mStates[system] = s;
foreach (KeyValuePair<Key<string>, IPublishCallback> callback in mCallbacks)
{
try
{
callback.Value.ServiceCallback(system, state, extraInfo);
}
catch (CommunicationException ex)
{
toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
}
catch
{
toRemove.Add(new Key<string>(callback.Key.Key1, callback.Key.Key2));
}
}
foreach (Key<string> key in toRemove)
mCallbacks.Remove(key);
}
}
}