今天要來個小型的實作一個連線名單,連線名單可以讓你知道目前在線的人有誰(廢話),之前的範例都是在進入時傳送訊息給所有人,如果是中途進來的人,是無從得知目前有哪些人在線上 ,如果要做一個聊天室沒這基本的功能也太爛了吧!XD

一樣使用Day3的範例來做,沒有的點這裡

製作一個名單容器和容器Service

我們先要製作一個容器,把目前上線的人資訊都存下來,至於要存哪些呢?我覺得使用者名稱連接ID是一定需要的,再來就是上線的時間點,大概就這些,先建立一個ConUserModel.cs, 並建立一個Class當容器。

using System;
using System.Collections.Generic;

namespace CoreWeb.Models
{
    public class ConUserModel
    {
        public string connectID { get; set; }
        public string username { get; set; }
        public DateTime onlineTime { get; set; }
    }
}

再來建立ConUserService,這邊我建立在同一個檔案內,在內部建立一個List泛型內部裝剛剛建立的ConUserModel,建立新增及移除List的方法

public class ConUserService
{
    private List<ConUserModel> _list;
    public ConUserService()
    {
        _list = new List<ConUserModel>();
    }
    public List<ConUserModel> AddList(ConUserModel user)
    {
        _list.Add(user);
        return _list;
    }
    public List<ConUserModel> RemoveList(string connectID)
    {
        for (int i = 0; i < _list.Count; i++)
        {
            if (_list[i].connectID == connectID)
                _list.Remove(_list[i]);
        }
        return _list;
    }
}

再到Starp.cs註冊Service

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<ConUserService>();
    services.AddSignalR();
}

再來到我們建立一個Hub裡面引用Core.Models

using CoreWeb.Models;

Hub內宣告List<ConUserMode>,建構子裡面接收從Servie傳過來的List<ConUserMode>,在載入到Hub內的List變數

namespace CoreWeb.Hubs
{
    public class ChatHub : Hub
    {
        private ConUserService conUserList;
        public ChatHub(ConUserService conUserService)
        {
            conUserList = conUserService;
        }
    }
}

後端連線斷線的處理

後端每當有人連線或斷線時,都要重新傳送一份名單給所有使用者,所以得要在OnConnectedAsyncOnDisconnectedAsync處理,但是OnConnectedAsync裡面不能接參數,所以我們改用 連線後,再加入的群組的方式,首先寫加入群組的方法,群組假設叫TestGroup

public async void AddConUserList(string user)
{
    var _user = new ConUserModel();
    _user.username = user;
    _user.connectID = Context.ConnectionId;
    _user.onlineTime = DateTime.Now;

    await Groups.AddToGroupAsync(Context.ConnectionId, "TestGroup");
    await Clients.Group("TestGroup").SendAsync("GetConList", conUserList.AddList(_user));
}

程式碼大概就是建立一個ConUserModel相關資料塞進去,加入群組後再加入上線名單。 然後切斷連線時,要通知群組所有人更新連線名單

public override async Task OnDisconnectedAsync(Exception exception)
{
    await base.OnDisconnectedAsync(exception);
    await Clients.Group("TestGroup").SendAsync("GetConList", conUserList.RemoveList(Context.ConnectionId));
}

前端表現處理

我們先建立一個連線名單的select html element,記得加入multiple屬性讓它展開

<select id="ConList" multiple></select>

在連線的方法成功之後加入群組

document.getElementById('conOpenBtn').onclick = function () {
    connection.start().then(function() {
        console.log('start');

        var user = document.getElementById("name").value;
        // 加入群組
        connection.invoke("AddConUserList", user).catch(function (err) {
            return console.error(err.toString());
        });

    }).catch(function (err) {
        return console.error(err.toString());
    });
}

更新名單的方法每次都要清空List,再重新填入Server傳回來的名單

connection.on("GetConList", function (ConList) {
    console.log(ConList);
    _ConList.innerHTML = '';

    for (var i = 0; i < ConList.length; i++) {
        var user = document.createElement("option");
        user.text = ConList[i].username;
        _ConList.appendChild(user);
    }
})

DEMO

今天大概就這些,雖然它只是一個小組件,但是之後的實作都能套用到它,明天會會來個比較難一點的。

範例下載