ASP.NET SingalR 點對點聊天實現思路總結

丶Pz發表於2015-12-18

前一段時間寫了一個簡單的聊天室,是群聊的方式。部落格地址:http://www.cnblogs.com/panzi/p/4980346.html。還有一種需求就是常見的尤其是培訓機構的主頁面,經常會有1對1諮詢聊天視窗。那麼用singalR如何實現1對1聊天呢。

 

其實很簡單。我們先看看SingalR裡的IHubConnectionContext介面程式碼:

public interface IHubConnectionContext<T>
    {
     //所有連線伺服器的使用者 T All {
get; }      //除了一部分使用者 T AllExcept(params string[] excludeConnectionIds);
//這個就是我們要用的點對點,針對單個使用者傳送訊息 T Client(
string connectionId);
//群發訊息 T Clients(IList
<string> connectionIds);
//按組群發訊息 T Group(
string groupName, params string[] excludeConnectionIds);
//多組群發訊息 T Groups(IList
<string> groupNames, params string[] excludeConnectionIds);
//使用者 T User(
string userId); T Users(IList<string> userIds); }

 

這裡我們就用  T Client(string connectionId); 這個方法。呼叫方式為 Clients.Client("connectionId").clientFun(msg); //(clientFun為自定義客戶端接收訊息方法名)具體細節不在描述,這裡比較關鍵的就是,如何知道對方的ConnectionId,因為ConnectionId是自動生成的而且,每次重新整理頁面都會變,SingalR本身又不帶統計線上使用者的方法,所以,這個需要自己去實現。思路很清晰,這裡先用 靜態List做使用者線上列表資訊儲存。程式碼如下:

 /// <summary>
    /// 簡單使用者統計model 
    /// </summary>
    public class HubUser
    {
        /// <summary>
        /// 連線伺服器之後,自動生成的connectionId
        /// </summary>
        public string ConnectionId { get; set; }
        /// <summary>
        /// 客戶端使用者的主鍵ID
        /// 一般和業務相關的使用者ID
        /// </summary>
        public string ClientUserId { get; set; }
        /// <summary>
        /// 聊天所在組
        /// </summary>
        public string GroupId { get; set; }
    }
 public sealed class OnlineUserPool
    {
        private static Lazy<List<HubUser>> _onlineUser = new Lazy<List<HubUser>>();
        public static List<HubUser> OnlineUser { get { return _onlineUser.Value; } }

        /// <summary>
        /// 新增使用者,一般在使用者 連線伺服器或者使用者重新連線的時候
        /// </summary>
        /// <param name="user"></param>
        public static void AddUser(HubUser user)
        {
            DeleteUser(user);
            _onlineUser.Value.Add(user);
        }
        /// <summary>
        /// 刪除某個線上使用者
        /// </summary>
        /// <param name="clientUserId"></param>
        /// <param name="connectionId"></param>
        public static void DeleteUser(HubUser user, bool unConnected = true)
        {
            var onlineUser = IsOnline(user);
            if (onlineUser != null)
            {
                _onlineUser.Value.Remove(onlineUser);
            }
        }
        public static HubUser IsOnline(HubUser user)
        {
            if (user == null) { throw new ArgumentNullException(); }
            string clientUserId = user.ClientUserId;
            string connectionId = user.ConnectionId;
            if (!string.IsNullOrEmpty(clientUserId))
            {
                return _onlineUser.Value.FirstOrDefault(x => x.ClientUserId == clientUserId);
            }
            else
            {
                return _onlineUser.Value.FirstOrDefault(x => x.ConnectionId == connectionId);
            }
        }
        /// <summary>
        /// 獲取線上總數
        /// </summary>
        /// <returns></returns>
        public static long GetUserCount()
        {
            return _onlineUser.Value.Count;
        }
    }

可以看到 OnlineUserPool 類實現了往靜態列表新增使用者,刪除使用者等一系列操作。

新增使用者操作需要,在使用者接入到聊天室的時候執行:

  public Task Join(ZjMessage message)
        {
            message.connectionId = Context.ConnectionId;
            //就是使用者加入的時候
            OnlineUserPool.AddUser(new HubUser
            {
                ClientUserId = message.userid,
                ConnectionId = Context.ConnectionId
            });
            message.msg = "當前已經有:" + OnlineUserPool.GetUserCount() + " 人線上";
            return Clients.All.receiveMessage(new { type = "join", msg = message });
        }

刪除使用者操作就在重寫OnDisconnect方法裡執行,需要根據ConnectionId刪除

 public override Task OnDisconnected(bool stopCalled)
        {
            ZjMessage message = new ZjMessage(Context.ConnectionId);
            //使用者離開
            //使用者斷線,需要將該使用者從列表中刪除,(應該考慮短暫失去連線的可能性,不能直接從列表刪除。)
            OnlineUserPool.DeleteUser(new HubUser
            {
                ConnectionId = Context.ConnectionId
            });
            return Clients.All.receiveMessage(new { type = "left", msg = message });
        }

所以,當你想點對點傳送訊息的時候,將對方userId傳送到伺服器,然後伺服器從線上列表裡面查詢出相應的connectionID,然後將訊息推送到該connectionID的使用者,就實現了線上兩個人聊天了。當然,用靜態列表的方式也不是很好,如果使用者量龐大,會不會出什麼問題呢,我具體沒研究過。一般的方案,是放在專門的快取伺服器儲存,或者NOSQL資料庫儲存也可以吧,方案有很多,由於沒有具體做過也不敢多費口舌。這個思路是沒問題的,當然也會有更好的方法吧。

相關文章