WCF聊天服务中的ConcurrentDictionary,可以添加但不能删除

时间:2015-07-31 14:06:23

标签: c# wcf concurrentdictionary

所以我使用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;
        }
}

1 个答案:

答案 0 :(得分:2)

您使用客户端对象作为字典的键。当客户端发送他的注销消息时,他会向您发送一个刚刚被WCF反序列化的Client对象。如果您没有覆盖EqualsGetHashCode,则ConcurrentDictionary将无法认识到您的原始客户端代表的客户端与传入RemoveClient的客户端相同。

考虑覆盖EqualsGetHashCode

我能想到的一个案例是,如果有人杀死了客户端进程或者连接断开,那么就会阻止它工作。在这种情况下,客户端不会向RemoveClient发送呼叫,并且该对象将在您的字典中停留。 如果您使用双工WCF服务,则可以在客户端断开连接时订阅事件,如果未连接,则允许您始终删除客户端。