.NET4.x下使用SignalR做登录、推送
.NET Core平台下就记录过一篇文章:https://www.cnblogs.com/shousiji/p/12737925.html
已经不想在.net framework下折腾了,奈何老项目也需要SignalR,故写下这篇文章。
官方示例:MVC方式、前后端分离方式-后端、前后端分离方式-前端
前后端方式复杂些,本文就是记录总结这个方式。项目结构如图:
一、创建服务端
1. 在vs中创建一个空的“web应用程序”,命令为“SignalRServer”
2. NuGet搜索并安装“Microsoft.AspNet.SignalR”
3. 会自动下载Scripts文件夹,待会把它剪切到“SignalRClient”项目中
4. NuGet搜索并安装“Microsoft.Owin.Cors”跨域组件
5. 新建OwinStartup类,命名为“Startup.cs”
代码如下:
using Microsoft.AspNet.SignalR; using Microsoft.Owin; using Microsoft.Owin.Cors; using Owin; [assembly: OwinStartup(typeof(SignalRServer.Startup))] namespace SignalRServer { public class Startup { public void Configuration(IAppBuilder app) { // Branch the pipeline here for requests that start with "/signalr" app.Map("/signalr", map => { // Setup the CORS middleware to run before SignalR. // By default this will allow all origins. You can // configure the set of origins and/or http verbs by // providing a cors options with a different policy. map.UseCors(CorsOptions.AllowAll); var hubConfiguration = new HubConfiguration { // You can enable JSONP by uncommenting line below. // JSONP requests are insecure but some older browsers (and some // versions of IE) require JSONP to work cross domain // EnableJSONP = true }; // Run the SignalR pipeline. We're not using MapSignalR // since this branch already runs under the "/signalr" // path. map.RunSignalR(hubConfiguration); }); } } }
6. 新建类,命名为“DemoChatHub.cs”,代码如下:
using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Newtonsoft.Json; using System.Collections.Generic; using System.Threading.Tasks; namespace SignalRServer { [HubName("demoChatHub")] public class DemoChatHub : Hub { /// <summary> /// 已登录的用户信息 /// </summary> public static List<UserModel> OnlineUser { get; set; } = new List<UserModel>(); /// <summary> /// 模拟存放在数据库里的用户信息 /// </summary> private static readonly List<UserModel> _dbuser = new List<UserModel> { new UserModel{ UserName = "test1", Password = "111", GroupName = "Group1" }, new UserModel{ UserName = "test2", Password = "111", GroupName = "Group2" }, new UserModel{ UserName = "test3", Password = "111", GroupName = "Group2" }, new UserModel{ UserName = "test4", Password = "111", GroupName = "Group1" }, new UserModel{ UserName = "test5", Password = "111", GroupName = "Group3" }, }; /// <summary> /// 登录验证 /// </summary> [HubMethodName("login")] public async Task Login(string username, string password) { string connid = Context.ConnectionId; ResultModel result = new ResultModel { Status = 0, Message = "登录成功!" }; if (!OnlineUser.Exists(u => u.ID == connid)) { var model = _dbuser.Find(u => u.UserName == username && u.Password == password); if (model != null) { model.ID = connid; OnlineUser.Add(model); //给当前的连接分组 await Groups.Add(connid, model.GroupName); } else { result.Status = 1; result.Message = "账号或密码错误!"; } } //给当前连接返回消息 await Clients.Client(connid).LoginResponse(result); } /// <summary> /// 获取所在组的在线用户 /// </summary> [HubMethodName("getUsers")] public async Task GetUsers() { var model = OnlineUser.Find(u => u.ID == Context.ConnectionId); ResultModel result = new ResultModel(); if (model == null) { result.Status = 1; result.Message = "请先登录!"; } else { result.Status = 0; result.OnlineUser = OnlineUser.FindAll(u => u.GroupName == model.GroupName); } //给所在组返回消息 await Clients.Group(model.GroupName).GetUsersResponse(result); } /// <summary> /// 推送消息 /// </summary> [HubMethodName("sendMessage")] public async Task SendMessage(string user, string message) { ResultModel result = new ResultModel(); var model = OnlineUser.Find(u => u.ID == Context.ConnectionId); if (model == null) { result.Status = 1; result.Message = "请先登录!"; } else { result.Status = 0; result.Message = $"“{user}”发送的消息:{message}"; } await Clients.Group(model.GroupName).SendMessageResponse(result); } /// <summary> /// 当连接成功时的处理 /// </summary> public override Task OnConnected() { return base.OnConnected(); } /// <summary> /// 当连接断开时的处理 /// </summary> public override Task OnDisconnected(bool stopCalled) { string connid = Context.ConnectionId; var model = OnlineUser.Find(u => u.ID == connid); int count = OnlineUser.RemoveAll(u => u.ID == connid); if (model != null) { ResultModel result = new ResultModel() { Status = 0, OnlineUser = OnlineUser.FindAll(u => u.GroupName == model.GroupName) }; Clients.Group(model.GroupName).GetUsersResponse(result); } return base.OnDisconnected(stopCalled); } } public class UserModel { [JsonProperty("id")] public string ID { get; set; } [JsonProperty("userName")] public string UserName { get; set; } [JsonProperty("password")] public string Password { get; set; } [JsonProperty("groupName")] public string GroupName { get; set; } } public class ResultModel { [JsonProperty("status")] public int Status { get; set; } [JsonProperty("message")] public string Message { get; set; } [JsonProperty("onlineUser")] public List<UserModel> OnlineUser { get; set; } } }
7. 运行服务端,记住地址,配置到下面步骤的客户端中
二、创建客户端
1. 在vs中创建一个空的“web应用程序”,命名为“SignalRClient”
2. 把服务端的Scripts文件夹剪切过来
3. 新建HTML页,命名为“index.html”,代码如下:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>client - demo</title> </head> <body> <div> <div> <input id="txtUserName" type="text" placeholder="账号" /> <input id="txtPasswod" type="password" placeholder="密码" /> <input id="btnLogin" type="button" value="登录" /> <br /> <br /> <input id="txtMessage" type="text" placeholder="消息" /> <input id="btnSend" type="button" value="发送" /> </div> <ul id="usersList"> </ul> <ul id="messagesList"> </ul> </div> <script src="Scripts/jquery-1.6.4.min.js"></script> <script src="Scripts/jquery.signalR-2.4.2.min.js"></script> <script type="text/javascript"> $(function () { var connection = $.hubConnection("http://localhost:52157/signalr", { useDefaultPath: false }); //注意修改为你的服务端地址 var contosoChatHubProxy = connection.createHubProxy('demoChatHub'); connection.start() .done(function () { console.log('Now connected, connection ID=' + connection.id); }) .fail(function () { console.log('Could not connect'); }); //-----登录----- document.getElementById("btnLogin").addEventListener("click", function (event) { var username = $("#txtUserName").val(); var password = $("#txtPasswod").val(); contosoChatHubProxy.invoke('login', username, password).done(function () { console.log('Invocation of login succeeded'); }).fail(function (error) { console.log('Invocation of login failed. Error: ' + error); }); event.preventDefault(); }); contosoChatHubProxy.on('LoginResponse', function (res) { if (res && res.status == 0) { alert(res.message); getUsers(contosoChatHubProxy); } else { alert('登录失败!'); } }); //-----登录----- //-----消息----- document.getElementById("btnSend").addEventListener("click", function (event) { var username = $("#txtUserName").val(); var message = $("#txtMessage").val(); contosoChatHubProxy.invoke('sendMessage', username, message).done(function () { console.log('Invocation of sendMessage succeeded'); }).fail(function (error) { console.log('Invocation of sendMessage failed. Error: ' + error); }); event.preventDefault(); }); contosoChatHubProxy.on('SendMessageResponse', function (res) { if (res && res.status == 0) { var li = document.createElement("li"); li.textContent = res.message; document.getElementById("messagesList").appendChild(li); } else { alert(res.message); } }); //-----消息----- //获取在线用户 function getUsers(contosoChatHubProxy) { contosoChatHubProxy.invoke('getUsers').done(function () { console.log('Invocation of getUsers succeeded'); }).fail(function (error) { console.log('Invocation of getUsers failed. Error: ' + error); }); contosoChatHubProxy.on('GetUsersResponse', function (res) { if (res && res.status == 0) { var _html = '<li>在线用户:</li>'; for (var i = 0; i < res.onlineUser.length; i++) { _html += `<li>${res.onlineUser[i].userName}</li>`; } document.getElementById("usersList").innerHTML = _html; } }); } }); </script> </body> </html>
4. 浏览器中打开页面,交互效果如下:
(注意:test1和test4是一组的,注意它们的变化;test5的组只有它一个,所以它收不到其它用户的消息)
本文代码:https://files.cnblogs.com/files/shousiji/SignalRDemo_net4.rar