十四、Net Core6 SignalR 入门(二)发送消息给指定用户

从上一篇文章我们知道了怎么配置中心和处理跨域的问题,这篇文章主要学习怎么向指定客户端发送消息,模拟了1对1聊天

如何配置中心服务,请移至上一篇

十三、Net Core6 SignalR入门(一)_XiaoGuaiSs的博客-CSDN博客npm将包内容安装在node_modules\@microsoft\signalr\dist\browser文件夹中。三、注册中心所需的SignalR服务和终结点,打开Program.cs,添加两处※※※SignalR※※※代码。和处理跨域,记住跨域不能默认*,需要指定客户端地址,否则跨域问题还是存在。了解如何使用ASP.NETCoreSignalR中的中心。二、新建ChatHub.cs类,并写好向所有客户端发送消息的方法。二、运行服务和客户端,开启两个客户端页面,测试成功。...https://blog.csdn.net/m0_37894611/article/details/125929054一、中心代码

using Microsoft.AspNetCore.SignalR;
using Newtonsoft.Json;

namespace Common.SignalR
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="Id">连接ID</param>
    /// <param name="User">用户名</param>
    /// <param name="Message">消息</param>
    public record TransData(string Id, string User, string Message);

    public interface IChatClient
    {
        Task ReceiveMessage(TransData data);
    }

    /// <summary>
    /// https://docs.microsoft.com/zh-cn/aspnet/core/signalr/hubs?view=aspnetcore-6.0
    /// </summary>
    public class CustomHub : Hub<IChatClient>
    {
        //用户集
        private readonly static Dictionary<string, string> _connections = new();

        private readonly string systemid = "system";
        private readonly string systemname = "system";

        #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>
        /// 获取连接的唯一 ID(由 SignalR 分配)。 每个连接都有一个连接 ID
        /// </summary>
        /// <returns></returns>
        public string GetConnectionId()
        {
            return Context.ConnectionId;
        }
        #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
    }
}

客户端代码

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<style>
			html {
				width: 100%;
				height: 100%;
				display: flex;
				justify-content: center;
				align-items: center;
			}

			.box {
				margin: 0 auto;
				height: 600px;
				width: 256px;
				background-color: aqua;
			}

			.box>div {
				background-color: antiquewhite;
				padding: 10px;
				border: 1px solid #aaaaff;
			}

			.box>div:hover {
				background-color: beige;
			}

			.msg {
				margin: 0 auto;
				height: 400px;
				width: 500px;
				background-color: aqua;
				overflow: hidden;
				left: -20px;
			}

			.t {
				max-height: 70%;
				height: 70%;
				width: 100%;
				background-color: #ffaa00;
				overflow-y: scroll;
			}

			.t>div {
				margin: 6px;
				font-size: 14px;
			}

			.b {
				margin-top: 1px;
				max-height: 30%;
				height: 29%;
				width: 100%;
				background-color: #55aaff;
			}

			textarea {
				width: 96%;
				height: 94%;
			}

			.self {
				text-align: right;
			}

			.name {
				color: #1a38ff;
			}

			.hide {
				display: none;
			}

			.myname {

				margin: 0 auto;
			}

			.dian {
				position: absolute;
				color: white;
				font-size: 17px;
				background-color: red;
				/*height: 24px;改前*/
				min-height: 15px;
				/*改后新增的代码*/
				min-width: 15px;
				/*改后新增的代码*/
				line-height: 15px;
				right: 40%;
				text-align: center;
				-webkit-border-radius: 15px;
				border-radius: 15px;
				padding: 2px;
			}
		</style>
	</head>
	<body>
		<div id="app">
			<div class="myname">{{user.name}}</div>
			<div class="box" v-show="!showbox">
				<div v-for="item in userlst" @dblclick="trun(item)">
					<div v-if="item.state" class="dian"> {{item.state}} </div>
					{{item.name}}
				</div>
			</div>
			<div class="msg" v-show="showbox">
				<div>
					<button title="返回" @click="trun()">
						< </button>
							{{nowuser.name}}
				</div>
				<div class="t" id="t">
					<div v-for="item in nowmsglst" :class="{'self':item.id==user.id}">
						<div class="name" v-if="item.id==user.id">{{user.name}}:</div>
						<div class="name" v-else>{{nowuser.name}}</div>
						<div class="conent">{{item.msg}}</div>
					</div>
				</div>
				<div class="b">
					<textarea id="smg" @keyup.enter="sendmsg()" v-model="nowmsg"></textarea>
				</div>
			</div>
		</div>
		</div>
		<script src="vue.js"></script>
		<script src="signalr.min.js"></script>
		<script src="common.js"></script>
		<script>
			var app = new Vue({
				el: '#app',
				data() {
					return {
						//链接
						connection: null,
						showbox: false,
						nowmsg: '',
						user: {
							id: '1',
							name: '111'
						},
						//用户列表
						userlst: [],
						//与用户的聊天记录
						msglst: [{
							id: '',
							msg: [{

							}]
						}],
						//当前聊天对象
						nowuser: {
							id: 0,
							name: '123'
						},
						//当前聊天记录
						nowmsglst: []
					}
				},
				created: function() {
					this.connection = new signalR.HubConnectionBuilder()
						.withUrl("http://192.168.5.73:6123/chatHub")
						.build();

					//如果连接断开,尝试重新连接
					this.connection.onclose(async () => {
						await this.start();
					});

					// 开始连接.
					this.start();
					//ReceiveMessage 与后端对应 ,接收中心发来的消息
					this.connection.on("ReceiveMessage", (res) => {
						console.log(res, "收到消息");
						switch (res.message) {
							case 'relst': //中心通知有新用户加入
								console.log("更新在线列表");
								this.getUserLst();
								break;
							default: //默认接收消息处理

								//得到当前发送者的消息集位置
								let inx = this.msglst.findIndex(t => {
									return t.id == res.id
								});
								//如果不存在,则添加进消息集
								//否则在指定用户消息及添加消息
								if (inx == -1) {
									this.msglst.push({
										id: res.id,
										msg: [{
											id: res.id,
											msg: res.message
										}]
									})
								} else {
									this.msglst[inx].msg.push({
										id: res.id,
										msg: res.message
									})
								}
								//消息加1
								this.editState(res.id, true);
								//如果为当前聊天用户,赋值并自动滑到底部
								if (this.nowuser.id == res.id) {
									nowmsglst.push({
										id: res.id,
										msg: res.message
									})
									this.editState(res.id, false);
									this.scrollTop();
								};

								break;
						}
					});
				},
				methods: {
					//连接方法,如果连接失败,5s后重新连接
					start: async function() {
						try {
							await this.connection.start();
							this.user.name =getName(); //得到当前用户名
							this.user.id = this.getmyid(); //得到当前用户ID
							this.login(); //主动加入到用户列表
							console.log("连接成功.");
						} catch (err) {
							console.log(err);
							setTimeout(this.start, 5000);
						}
					},
					//跳转
					trun: async function(d) {
						if (d) {
							this.nowuser = d;
							this.editState(d.id, false);
						}
						this.showbox = !this.showbox;
						let res = await this.msglst.findIndex(t => {
							return t.id == this.nowuser.id
						});
						if (res >= 0) {
							this.nowmsglst = this.msglst[res].msg;
						} else {
							this.nowmsglst = [{
								id: '',
								msg: ''
							}];
						}

					},
					//登入并获取用户列表
					login: function() {
						this.connection.invoke("AddUser", this.user.name);
					},
					//获取在线用户列表
					getUserLst: function() {
						this.connection.invoke("GetUser").then(res => {
							console.log(res, "getuser");
							this.createlst(res);
						});
					},
					//获取个人ID
					getmyid: function() {
						this.connection.invoke("GetConnectionId").then(res => {
							return res;
						});
					},
					//发送消息
					sendmsg: async function() {
						this.connection.send("SendToOne", this.nowuser.id, this.nowmsg)
						var obj = {
							id: this.user.id,
							msg: this.nowmsg
						};
						this.nowmsglst.push(obj);
						let res = await this.msglst.findIndex(t => {
							return t.id == this.nowuser.id
						});
						if (res == -1) {
							this.msglst.push({
								id: this.nowuser.id,
								msg: this.nowmsglst
							})
						} else {
							this.msglst[res].msg = this.nowmsglst
						}
						this.scrollTop();
						this.nowmsg = '';
					},
					//重构上线用户列表
					createlst: function(lst) {
						this.userlst = [];
						for (d in lst) {
							this.userlst.push({
								id: lst[d].key,
								name: lst[d].value,
								state: 0
							})
						}
					},
					//指定用户清空消息
					editState: async function(cid, isadd) {
						let inx = await this.userlst.findIndex(t => {
							return t.id == cid
						});
						console.log(inx, 123)
						if (isadd) {
							this.userlst[inx].state = Number(this.userlst[inx].state) + 1;
						} else {
							this.userlst[inx].state = 0;
						}
					},
					//重置滚动条
					scrollTop: function() {
						let t = document.getElementById("t");
						t.scrollTop = t.scrollHeight;
					}
				}
			});
		</script>
	</body>
</html>

common.js为随机用户名,内容如下

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;
        }

最终效果,多开几个客户端测试

 

 

去对应的 看是否收到消息

 

 

测试成功 ,有疑问可以留言询问

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
.NET Core 6中使用SignalR向前端发送消息是可行的。SignalR是一个实时通信库,可以通过WebSocket、Server-Sent Events(服务器推送事件)以及长轮询等方式,提供实时双向通信能力。 首先,你需要安装并配置SignalR。可以通过NuGet包管理器或在项目文件中手动添加相关依赖项来安装SignalR及其所需组件。 接下来,在后台代码中,你需要创建一个SignalR的Hub类,它将用于与前端进行通信。你可以在Hub类中定义服务器端的方法,以便在后台代码中触发并发送消息给前端实例。例如: ```csharp using Microsoft.AspNetCore.SignalR; public class MyHub : Hub { public void SendMessage(string message) { Clients.All.SendAsync("ReceiveMessage", message); } } ``` 在这个例子中,我们定义了一个名为`SendMessage`的服务器端方法。当调用这个方法时,它会将接收到的消息通过`ReceiveMessage`事件发送到所有连接的前端实例上。 然后,在前端代码中,你需要通过JavaScript或其他前端框架来连接到SignalR Hub,并订阅服务器端发送的事件,以便接收消息。例如: ```javascript const connection = new signalR.HubConnectionBuilder() .withUrl("/myHubUrl") .build(); connection.on("ReceiveMessage", (message) => { // 处理接收到的消息 console.log(message); }); connection.start() .then(() => { // 连接成功,在此处可以调用后台方法发送消息(如button click事件等) }) .catch((error) => { // 处理连接错误 console.error(error); }); ``` 在这个例子中,我们创建了一个SignalR连接,并在连接成功后订阅了`ReceiveMessage`事件,以便在接收到服务器端消息时进行处理。你可以在连接成功后的回调函数中调用后台方法,发送消息前台实例。 最后,你可以通过调用后台方法来发送消息到前端实例,例如: ```csharp public class HomeController : Controller { private readonly IHubContext<MyHub> _hubContext; public HomeController(IHubContext<MyHub> hubContext) { _hubContext = hubContext; } public async Task<IActionResult> SendMessageToClients(string message) { await _hubContext.Clients.All.SendAsync("ReceiveMessage", message); return Ok(); } } ``` 在这个例子中,我们在控制器中注入了`IHubContext<MyHub>`,它允许我们调用与`MyHub`关联的方法,并向所有连接的前端实例发送消息。 综上所述,你可以通过SignalR.NET Core 6中实现后台前台实例发送消息的功能。具体实现细节可能会根据你的项目需求而有所不同,以上只是一个简单的示例。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值