参考地址
ASP.NET Core SignalR 入门 | Microsoft Learn
使用 ASP.NET Core SignalR 中的中心 | Microsoft Learn
十四、Net Core6 SignalR 入门(二)发送消息给指定用户_netcore6 想后台数据库有增量了就向前台推送消息如何实现-CSDN博客
1.添加 SignalR 客户端库
2.创建 SignalR 中心
中心是一个类,用作处理客户端 - 服务器通信的高级管道。
在 SignalRChat 项目文件夹中,创建 Hubs
文件夹。
在 Hubs
文件夹中,使用以下代码创建 ChatHub
类:
public interface IChatClient { Task ReceiveMessage(TransData data); } public class ChatHub : Hub<IChatClient> { private readonly static Dictionary<string, string> _connections = new(); private readonly string systemid = "system"; private readonly string systemname = "system"; /// <summary> /// /// </summary> /// <param name="Id">连接ID</param> /// <param name="User">用户名</param> /// <param name="Message">消息</param> public record TransData(string Id, string User, string Message); #region SignalR用户 /// <summary> /// 获取连接的唯一 ID(由 SignalR 分配)。 每个连接都有一个连接 ID /// </summary> /// <returns></returns> public string GetConnectionId() { var connectionId= Context.ConnectionId; return connectionId; } #endregion #region 发送消息 /// <summary> /// 以个人名义向所有客户端发送消息 /// </summary> /// <param name="message"></param> /// <returns></returns> public async Task SendToAll(string message) { string cid = GetConnectionId(); await Clients.All.ReceiveMessage(new(cid, _connections[cid], message)); } /// <summary> /// 以系统名义向所有客户端发送消息 /// </summary> /// <param name="message"></param> /// <returns></returns> public async Task SendSysToAll(string message) => await Clients.All.ReceiveMessage(new(systemid, systemname, message)); /// <summary> /// 发送消息给指定用户(个人) /// </summary> /// <param name="id"></param> /// <param name="message"></param> /// <returns></returns> public async Task SendToOne(string id, string message) { string cid = GetConnectionId(); await Clients.Client(id).ReceiveMessage(new(cid, _connections[cid], message)); } /// <summary> /// 发送消息给指定用户(系统) /// </summary> /// <param name="id"></param> /// <param name="message"></param> /// <returns></returns> public async Task SendSysToOne(string id, string message) => await Clients.Client(id).ReceiveMessage(new(systemid, systemname, message)); /// <summary> /// 发送群组消息(个人) /// </summary> /// <param name="group"></param> /// <param name="message"></param> /// <returns></returns> public async Task SendToGroup(string group, string message) { string cid = GetConnectionId(); await Clients.Group(group).ReceiveMessage(new(cid, _connections[cid], message)); } /// <summary> /// 发送群组消息(系统) /// </summary> /// <param name="group"></param> /// <param name="message"></param> /// <returns></returns> public async Task SendSysToGroup(string group, string message) => await Clients.Group(group).ReceiveMessage(new(systemid, systemname, message)); #endregion #region SignalR群组 /// <summary> /// 主动加入群组 /// </summary> /// <param name="group"></param> /// <returns></returns> public async Task AddToGroup(string group) { string cid = GetConnectionId(); await Groups.AddToGroupAsync(cid, group); await SendSysToGroup(group, $@"欢迎{_connections[cid]}加入"); } /// <summary> /// 被动加入群组 /// </summary> /// <param name="group"></param> /// <param name="id"></param> /// <returns></returns> public async Task AddToGrouped(string group, string id) { string cid = GetConnectionId(); await Groups.AddToGroupAsync(id, group); await SendSysToGroup(group, $@"欢迎{_connections[cid]}加入"); } #endregion #region 临时用户操作 /// <summary> /// 添加到在线用户集 /// </summary> /// <param name="user"></param> /// <returns></returns> public async Task AddUser(string name) { string cid = GetConnectionId(); if (!_connections.ContainsKey(cid)) { await Task.Run(() => _connections.Add(cid, name)); await SendSysToAll("relst"); } } /// <summary> /// 获取在线用户 /// </summary> /// <returns></returns> public object GetUser() { string cid = GetConnectionId(); return _connections.Where(t => !t.Key.Equals(cid)); } #endregion #region 重写连接断开钩子 /// <summary> /// 重写链接钩子 /// </summary> /// <returns></returns> public override async Task OnConnectedAsync() { await base.OnConnectedAsync(); } /// <summary> /// 重写断开链接钩子 /// </summary> /// <param name="exception"></param> /// <returns></returns> public override async Task OnDisconnectedAsync(Exception? exception) { string cid = GetConnectionId(); _connections.Remove(cid); await SendSysToAll("relst"); await base.OnDisconnectedAsync(exception); } #endregion }
3.配置 SignalR
4.添加 SignalR 客户端代码
"use strict"; var user = { id: '1', name: '111' }; var userlst = [];//用户列表 var msglst = [{ id: '', msg: [{}] }];//与用户的聊天记录 var nowuser = { id: 0, name: '123' };//当前聊天对象 var nowmsglst = [];//当前聊天记录 var nowmsg = ""; //1.创建 var connection = new signalR.HubConnectionBuilder().withUrl("/chatHub").build(); //2.启动连接 start(); //如果关闭重新启动 connection.onclose(async () => { await start(); }); //3.接收消息 connection.on("ReceiveMessage", function (res) { console.log(res); switch (res.message) { case 'relst': //中心通知有新用户加入 getUserLst(); break; default: //默认接收消息处理 //得到当前发送者的消息集位置 let inx = msglst.findIndex(t => { return t.id == res.id }); //如果不存在,则添加进消息集 //否则在指定用户消息及添加消息 if (inx == -1) { msglst.push({ id: res.id, msg: [{ id: res.id, msg: res.message }] }) } else { msglst[inx].msg.push({ id: res.id, msg: res.message }) } //消息加1 editState(res.id, true); //如果为当前聊天用户,赋值并自动滑到底部 if (nowuser.id == res.id) { nowmsglst.push({ id: res.id, msg: res.message }) editState(res.id, false); }; break; } var li = document.createElement("li"); document.getElementById("messagesList").appendChild(li); // We can assign user-supplied strings to an element's textContent because it // is not interpreted as markup. If you're assigning in any other way, you // should be aware of possible script injection concerns. //li.textContent = `${user} says ${message}`; }); document.getElementById("sendButton").addEventListener("click", function (event) { nowmsg = document.getElementById("messageInput").value; //4.发送消息 sendmsg(); event.preventDefault(); }); //连接方法,如果连接失败,5s后重新连接 async function start() { try { await connection.start(); user.name = getName(); //得到当前用户名 user.id = await getmyid(); //得到当前用户ID login(); //主动加入到用户列表 console.log("连接成功."); } catch (err) { console.log(err); setTimeout(start, 5000); } } //跳转 async function trun(d) { //if (d) { // this.nowuser = d; // this.editState(d.id, false); //} //this.showbox = !this.showbox; let res = await msglst.findIndex(t => { return t.id == nowuser.id }); if (res >= 0) { nowmsglst = msglst[res].msg; } else { nowmsglst = [{ id: '', msg: '' }]; } } function login() { connection.invoke("AddUser", user.name); } //获取在线用户列表 function getUserLst() { connection.invoke("GetUser").then(res => { createlst(res); }); } //获取个人ID function getmyid() { return new Promise(function (resolve, reject) { connection.invoke("GetConnectionId").then(res => { resolve(res); return res; }); }) } //发送消息 async function sendmsg() { nowuser = userlst[0]; connection.send("SendToOne", nowuser.id, nowmsg) var obj = { id: user.id, msg: nowmsg }; nowmsglst.push(obj); let res = await msglst.findIndex(t => { return t.id == nowuser.id }); if (res == -1) { msglst.push({ id: nowuser.id, msg: nowmsglst }) } else { msglst[res].msg = nowmsglst } //scrollTop(); nowmsg = ''; } //重构上线用户列表 function createlst(lst) { userlst = []; for (var d in lst) { userlst.push({ id: lst[d].key, name: lst[d].value, state: 0 }) } } //指定用户清空消息 async function editState(cid, isadd) { let inx = await userlst.findIndex(t => { return t.id == cid }); if (isadd) { userlst[inx].state = Number(userlst[inx].state) + 1; } else { userlst[inx].state = 0; } } function getName() { var familyNames = new Array( "赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", "褚", "卫", "蒋", "沈", "韩", "杨", "朱", "秦", "尤", "许", "何", "吕", "施", "张", "孔", "曹", "严", "华", "金", "魏", "陶", "姜", "戚", "谢", "邹", "喻", "柏", "水", "窦", "章", "云", "苏", "潘", "葛", "奚", "范", "彭", "郎", "鲁", "韦", "昌", "马", "苗", "凤", "花", "方", "俞", "任", "袁", "柳", "酆", "鲍", "史", "唐", "费", "廉", "岑", "薛", "雷", "贺", "倪", "汤", "滕", "殷", "罗", "毕", "郝", "邬", "安", "常", "乐", "于", "时", "傅", "皮", "卞", "齐", "康", "伍", "余", "元", "卜", "顾", "孟", "平", "黄", "和", "穆", "萧", "尹" ); var givenNames = new Array( "子璇", "淼", "国栋", "夫子", "瑞堂", "甜", "敏", "尚", "国贤", "贺祥", "晨涛", "昊轩", "易轩", "益辰", "益帆", "益冉", "瑾春", "瑾昆", "春齐", "杨", "文昊", "东东", "雄霖", "浩晨", "熙涵", "溶溶", "冰枫", "欣欣", "宜豪", "欣慧", "建政", "美欣", "淑慧", "文轩", "文杰", "欣源", "忠林", "榕润", "欣汝", "慧嘉", "新建", "建林", "亦菲", "林", "冰洁", "佳欣", "涵涵", "禹辰", "淳美", "泽惠", "伟洋", "涵越", "润丽", "翔", "淑华", "晶莹", "凌晶", "苒溪", "雨涵", "嘉怡", "佳毅", "子辰", "佳琪", "紫轩", "瑞辰", "昕蕊", "萌", "明远", "欣宜", "泽远", "欣怡", "佳怡", "佳惠", "晨茜", "晨璐", "运昊", "汝鑫", "淑君", "晶滢", "润莎", "榕汕", "佳钰", "佳玉", "晓庆", "一鸣", "语晨", "添池", "添昊", "雨泽", "雅晗", "雅涵", "清妍", "诗悦", "嘉乐", "晨涵", "天赫", "?傲", "佳昊", "天昊", "萌萌", "若萌" ); var i = parseInt(10 * Math.random()) * 10 + parseInt(10 * Math.random()); var familyName = familyNames[i]; var j = parseInt(10 * Math.random()) * 10 + parseInt(10 * Math.random()); var givenName = givenNames[i]; var name = familyName + givenName; return name; }