所以我使用WCF创建了一个聊天服务,到目前为止一切正常。我唯一的问题是当用户注销时。我使用ConcurrentDictionary
来保存所有连接的客户端,当使用TryRemove
时,它总是返回false。这是代码。
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
public class ComsService : IMessageComs
{
ConcurrentDictionary<Client, IMessageComsCallBack> clients = new ConcurrentDictionary<Client, IMessageComsCallBack>();
public IMessageComsCallBack CurrentCallback
{
get
{
return OperationContext.Current.
GetCallbackChannel<IMessageComsCallBack>();
}
}
public Client ClientConnect(string userName)
{
var exists = clients.Where(x => x.Key.UserName == userName);
if (exists.Count() == 0)
{
Client c = new Client();
c.UserName = userName;
if (this.clients.TryAdd(c, CurrentCallback))
{
// Let everyone know that a new user has connected
foreach (KeyValuePair<Client, IMessageComsCallBack> kvp in clients)
{
if(!kvp.Key.Equals(c))
kvp.Value.RefreshClients(clients.Keys.ToList());
}
return c;
}
else
return null;
}
else
return exists.First().Key;
}
public bool RemoveUser(Client user)
{
IMessageComsCallBack _callback;
if (clients.ContainsKey(user)) // this is pointless, I know but was just a test
{
if (clients.TryRemove(user, out _callback))
{
try
{
foreach (IMessageComsCallBack callback in clients.Values)
{
callback.UserLeft(user);
}
}
catch (Exception ex)
{
return false;
}
}
else
return false;
}
else
return false;
return true;
}
}
我知道这不是必需的,但我还添加了一个检查,看看客户端在删除之前是否已经在列表中。这也导致了错误。
有什么想法吗?
更新:
刚刚思考并修改了我的注销方法,它似乎有效。我只能假设client
值发生了某些事情,并且它不再与列表中的值相同。
public bool RemoveUser(Client user)
{
IMessageComsCallBack _callback;
var exists = clients.Where(x => x.Key.UserName == user.UserName);
if(exists.Count() > 0)
{
if (clients.TryRemove(exists.First().Key, out _callback))
{
try
{
foreach (IMessageComsCallBack callback in clients.Values)
{
callback.UserLeft(user);
}
}
catch (Exception ex)
{
return false;
}
}
else
return false;
}
}
答案 0 :(得分:2)
您使用客户端对象作为字典的键。当客户端发送他的注销消息时,他会向您发送一个刚刚被WCF反序列化的Client
对象。如果您没有覆盖Equals
和GetHashCode
,则ConcurrentDictionary
将无法认识到您的原始客户端代表的客户端与传入RemoveClient
的客户端相同。
考虑覆盖Equals
和GetHashCode
。
我能想到的一个案例是,如果有人杀死了客户端进程或者连接断开,那么就会阻止它工作。在这种情况下,客户端不会向RemoveClient
发送呼叫,并且该对象将在您的字典中停留。
如果您使用双工WCF服务,则可以在客户端断开连接时订阅事件,如果未连接,则允许您始终删除客户端。