安装Microsoft.AspNet.SignalR(使用的版本2.4.1)
新建Hubs文件夹:Hub的接口和实现
namespace SingralRedis.Hubs { interface IChatHub { //服务器下发消息到各个客户端 void SendChat(string id, string name, string message); //用户上线通知 void SendLogin(string id, string name); //用户下线通知 void SendLogoff(string id, string name); //接收客户端发送的心跳包并处理 void TriggerHeartbeat(string id, string name); } }
提供五个事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect.
这里只 override OnConnected,看需要重载
using Microsoft.AspNet.SignalR; using System; using System.Collections.Generic; using System.Linq; using System.Timers; namespace SingralRedis.Hubs { public class ChatHub : Hub,IChatHub { public override System.Threading.Tasks.Task OnConnected() { string userName = Context.QueryString["userName"]; //获取客户端发送过来的用户名 string userId = Context.QueryString["userId"]; //获取客户端发送过来的用户名 string connectionId = Context.ConnectionId; if (!string.IsNullOrEmpty(userId)) { SendLogin(userId, userName, connectionId); } //HttpContext.Current.Application.Add(userName, connectionId); //存储关系 return base.OnConnected(); } public class UserChat { public UserChat() { Count = 0; if (Timer == null) Timer = new Timer(); Timer.Interval = 1000; //1s触发一次 Timer.Start(); Timer.Elapsed += (sender, args) => { Count++; if (Count >= 20) { action(); //该用户掉线了,抛出事件通知 Timer.Stop(); } }; } private readonly Timer Timer; public event Action action; //public event Action action1; public string UserId { get; set; } public string ID { get; set; } public string Name { get; set; } //内部计数器(每次递增1),如果服务端每5s能收到客户端的心跳包,那么count被重置为0; //如果服务端20s后仍未收到客户端心跳包,那么视为掉线 public int Count { get; set; } } public static class ChatUserCache { public static IList<UserChat> userList = new List<UserChat>(); } private IList<UserChat> userList = ChatUserCache.userList; /// <summary> /// 推送个单个用户 /// </summary> /// <param name="name">用户名</param> public void SendToUser(string name) { //根据username获取对应的ConnectionId var user = userList.FirstOrDefault(v => v.Name == name); if (user != null) { Clients.Client(user.UserId).addNewMessageToPage("新的信息"); } } /// <summary> /// 自发自收 /// </summary> public void ReturnId() { Clients.Caller.CheckId(Context.ConnectionId); } /// <summary> /// 获取全部用户 /// </summary> public void GetAllUserList() { Clients.All.loginUser(userList); } public void SendChat(string id, string name, string message) { Clients.Client(id).GetChatMsg("<====用户 " + name +" 发送消息 "+ message+" ======>"); } public void SendLogin(string id, string name, string connectionId) { connectionId = string.IsNullOrEmpty(connectionId) ? connectionId = Context.ConnectionId : connectionId; if (!userList.Any(v => v.ID == id)) { var userInfo = new UserChat() { UserId = connectionId, ID = id, Name = name }; userInfo.action += () => { //用户20s无心跳包应答,则视为掉线,会抛出事件,这里会接住,然后处理用户掉线动作。 SendLogoff(id, name); }; userList.Add(userInfo); } else { var user = userList.FirstOrDefault(v => v.ID == id); user.UserId = connectionId; user.Count = 0; } Clients.All.loginUser(userList); } public void SendLogoff(string id, string name) { var userInfo = userList.Where(x => x.ID.Equals(id) && x.Name.Equals(name)).FirstOrDefault(); if (userInfo != null) { var name1 = userInfo.Name; if (userList.Remove(userInfo)) { Clients.All.logoffUser(userList); SendChat(id, name, "<====用户 " + name + " 退出了讨论组====>"); } } } public void TriggerHeartbeat(string id, string name) { var userInfo = userList.Where(x => x.ID.Equals(id) && x.Name.Equals(name)).FirstOrDefault(); if (userInfo != null) { userInfo.Count = 0; //收到心跳,重置计数器 } } } }
新建Startup.cs文件
using Owin; namespace SingralRedis { public class Startup { public void Configuration(IAppBuilder app) { // Any connection or hub wire up and configuration should go here
//会默认指定一个路径 "/signalr"
app.MapSignalR(); } } }