话不多说,上代码
1、首先nuget引用Microsoft.AspNetCore.SignalR包
2、在Startup文件ConfigureServices加入services.AddSignalR();和跨域的代码,如下:
public IServiceProvider ConfigureServices(IServiceCollection services){ //消息推送 services.AddSignalR(); //跨域 发布正式环境需要设置访问主机限制 services.AddCors(options =>{ options.AddPolicy("CorsPolicy", builder =>{ builder.SetIsOriginAllowed(origin => true).AllowAnyHeader().AllowAnyMethod().AllowCredentials(); }); }); ......... }
3、Configure里面(一个跨域和一个路由):
public void Configure(IApplicationBuilder app, IWebHostEnvironment env){ if (env.IsDevelopment()){ app.UseDeveloperExceptionPage(); } //跨域 app.UseCors("CorsPolicy"); //添加SignalR路由配置代码,路由指向自定义类MessageHub app.UseSignalR(routes => { routes.MapHub<MessageHub>("/MessageHub");//直接路由到下面消息处理类 }); 。。。。。 }
4、建立一个消息处理类
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using CriticalMass.C2C.Model; using Microsoft.AspNetCore.SignalR; namespace CriticalMass.C2C.API.Extend { [AllowCrossSiteJson] public class MessageHub:Hub { public static List<ConnectionSession> UserList = new List<ConnectionSession> { }; //发送消息--发送给所有连接的客户端 public Task SendMessage(string msg){ return Clients.All.SendAsync("ReceiveMessage", msg); } //发送消息--发送给指定用户 public Task SendPrivateMessage(string connectionId, string message){ return Clients.Client(connectionId).SendAsync("ReceiveMessage", message); } /// <summary> /// 客户端登录到服务器 /// </summary> /// <param name="uid"></param> public Task SendLogin(int uid) { AjaxResult result = new AjaxResult(); try{ ConnectionSession cs = UserList.Find(a => a.Uid == uid); if (cs != null){ UserList.Remove(cs); } UserList.Add(new ConnectionSession{ Uid = uid, ConnectionId = Context.ConnectionId }); result.Msg = "ok"; } catch (Exception ex) { result.Msg = ex.Message; } return Clients.Client(UserList.Find(a=>a.Uid==uid).ConnectionId).SendAsync("ReceiveLogin", result.ToJson()); } } public class ConnectionSession { public int Uid { get; set; } public string ConnectionId { get; set; } } }
5、前端代码
<ul class="form-group" id="messagesListUl" style="margin-bottom:20px"></ul> <ul class="form-group" id="messagesListUl2" style="margin-bottom:20px"></ul> <div class="form-group"> <label for="username">姓名:</label> <input type="text" class="form-control" id="username" name="username"> </div> <div class="form-group"> <label for="msgcontent">内容:</label> <textarea rows="5" cols="20" id="msgcontent" name="msgcontent" class="form-control"></textarea> </div> <input type="button" οnclick="btnSendMsg()" value="发送"> <script src="jquery.js"></script> <script src="signalr.js"></script> <script type="text/javascript"> //创建连接对象connection const signalr_connection = new signalR.HubConnectionBuilder() .withUrl("http://localhost:65423/MessageHub")//这里是我们前面建的那个类的名字 .configureLogging(signalR.LogLevel.Information) .build(); //启动connection signalr_connection.start() .then(function(){ console.log("连接成功"); //这里与服务器连接成功之后,就要执行登录 signalr_connection.invoke("SendLogin", 18/*我这里传入的是用户ID,登录服务器之后需要保存用户信息的*/).catch(err => console.error("发送失败:"+err.toString())); }).catch(function(ex){ console.log("连接失败"+ex); //SignalR JavaScript 客户端不会自动重新连接,必须编写代码将手动重新连接你的客户端 //setTimeout(() => start(), 5000); }); async function start() { try { await signalr_connection.start(); console.log("connected"); } catch (err) { console.log(err); setTimeout(() => start(), 5000); } }; signalr_connection.onclose(async () => { start(); }); signalr_connection.on("ReceiveChartMessage", function(data) { var msgObj = JSON.parse(data);//json串转对象 console.log(JSON.stringify(msgObj)); const li = document.createElement("li"); li.textContent=JSON.stringify(msgObj); document.getElementById("messagesListUl2").appendChild(li); }); //绑定事件("ReceiveMessage"和服务器端的SendMessage方法中的第一个参数一致) signalr_connection.on("ReceiveMessage", function(data) { var msgObj = JSON.parse(data);//json串转对象 const li = document.createElement("li"); li.textContent = msgObj.name + " : " + msgObj.message; document.getElementById("messagesListUl").appendChild(li); }); //接受登录成功的消息 signalr_connection.on("ReceiveLogin", function(data) { var msgObj = JSON.parse(data);//json串转对象 console.log(JSON.stringify(msgObj)); if(msgObj.Msg=="ok"){ alert("登录成功"); } else{ alert(msgObj.Msg); } }); //发送消息 function btnSendMsg(){ var username = $.trim($("#username").val()); var msgcontent = $.trim($("#msgcontent").val()); var msgObj={}; msgObj.name=username; msgObj.message=msgcontent; var jsonstr=JSON.stringify(msgObj);//对象转json串 console.log(jsonstr); signalr_connection.invoke("SendMessage", jsonstr).catch(err => console.error("发送失败:"+err.toString())); } </script>
说两个方法:
signalr_connection.invoke调用后台服务 signalr_connection.on 前端页面监听后台方法调用 这个前端跟后台之间的相互调用有点蛋疼,耐着性子多看几遍就会明白,他娘的就跟RPC一样 6、后台消息推送实现 using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using CriticalMass.C2C.Model; using CriticalMass.C2C.IRepository; using CriticalMass.C2C.Utility; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Caching.Memory; using CriticalMass.C2C.API.Extend; using Microsoft.AspNetCore.SignalR; namespace CriticalMass.C2C.API.Controllers { [TokenCheck] public class AdmChartController : BaseController{ private readonly IHubContext<MessageHub> myHub; //构造函数注入MessageHub public AdmChartController(IHubContext<MessageHub> _myHub) { myHub = _myHub; } public string AuditChart([FromBody] dynamic parameModel) { AjaxResult result = new AjaxResult(); try{ string Ids = parameModel.ids; var ChartSrv = HttpContext.RequestServices.GetService<Itproduct_chart_detailsRepository>(); var SessionSrv = HttpContext.RequestServices.GetService<Itproduct_chat_sessionRepository>(); List<Model.tproduct_chart_details> lt = ChartSrv.GetList($"id in({Ids})"); List<int> lt_status = lt.Map(a => (int)a.status).Distinct().ToList<int>(); if (lt_status.Count > 1 || lt_status[0] != 0){ throw new Exception("状态不正确."); } ChartSrv.Update("status=1", $"id in({Ids})"); List<dynamic> lt_msg = new List<dynamic>(); Ids.Split(',').Each(a=> { int uid = 0; int to_uid = 0; Model.tproduct_chart_details d = ChartSrv.GetModel(a.ToInt32()); Model.tproduct_chat_session s = SessionSrv.GetModel((int)d.session_id); uid = (int)d.uid; if (uid == s.session_call){ to_uid = (int)s.session_listen; } else { to_uid = (int)s.session_call; } var obj = new { session_id=s.id, uid= uid, to_uid= to_uid, msg=d.msg }; lt_msg.Add(obj); }); List<dynamic> list=lt_msg.Map(a => a.to_uid).Distinct().Map(b=> { return new { to_uid = b, sessions = lt_msg.Filter(c=>c.to_uid == b).Map(f=>f.session_id).Distinct().Map(g=> { return new { session_id=g, msg_lt= lt_msg.Filter(h=>h.to_uid==b && h.session_id==g).Map(j=> { return new { uid_send=j.uid, msg=j.msg }; }) }; }) }; }).ToList<dynamic>(); SendMsg(list); result.Msg = "ok"; } catch (Exception ex) { result.Msg = ex.Message; } return result.ToJson(); } /// <summary> /// 向客户端推送及时消息 /// </summary> /// <param name="uid"></param> /// <param name="to_uid"></param> /// <param name="json"></param> public async Task SendMsg(List<dynamic> lt) { var task = Task.Run(()=> { lt.Each(a => { int to_uid = a.to_uid; object obj = a.sessions; try{ ConnectionSession cs = MessageHub.UserList.Find(a => a.Uid == to_uid); //在线 if (cs != null){ myHub.Clients.Client(cs.ConnectionId).SendAsync("ReceiveChartMessage", obj.ToJson()); } } catch { } }); }); await task; } } }
注意
ConnectionSession cs = MessageHub.UserList.Find(a => a.Uid == to_uid);这一句
在MessageHub类定义了一个UserList用户信息全局静态数组变量,用户登陆后就会把用户信息更新到这个数据组
MessageHub.UserList.Find(a => a.Uid == to_uid)
这里我是通过用户ID在数组找到用户信息的,然后就可以拿到ConnectionId,拿到ConnectionId之后就可以给客户端发送通知了,发送pong到客户端ReceiveChartMessage这个方法(前面js中客户端已经对这个方法监听了),并带上参数即可
7、在线聊天原理差不多,服务器充当转发器即可