SignalR 持久链接 (该功能为手机设备与后台同个用户id进行实现的,仅用signalR学习参考)
using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Threading; using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Transports; using MY.BllModel; using Newtonsoft.Json; using Task = System.Threading.Tasks.Task; using MY.Logging; using MY.Utility; namespace SignalR.Persistent { /// <summary> /// 持久连接 /// </summary> public partial class CharPersistent : PersistentConnection { //log类声明为局部静态是为性能考虑 private static readonly LogHelper LogHelper = new LogHelper("SignalR.Persistent.CharPersistent"); protected static SyncList<DeviceOnlineModel> UserModelList = new SyncList<DeviceOnlineModel>(); /// <summary> /// 真实链接数量 /// </summary> protected static int ConnectionsCount = 0; /// <summary> /// 接受到消息 /// </summary> protected override async Task OnReceived(IRequest request, string connectionId, string data) { try { if (string.IsNullOrEmpty(data)) { throw new Exception("请求参数不能为空"); } var json = JsonConvert.DeserializeObject<Dictionary<string, object>>(data); if (!json.ContainsKey("type") || !json.ContainsKey("text")) { throw new Exception("参数{type,text}不能为空"); } switch (json["type"].ToString().ToLower()) { case "online": //设备、web上线 await Online(request, connectionId, json["text"].ToString()); break; case "ng": //设备、web指令接收 await MsgForwarding(connectionId, data); if (json["text"].ToString().ToLower() == "getall") { LogHelper.DebugAsync("设备返回getall时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")); } break; case "onlineline": //获得在线列表 Connection.SendToWeb(connectionId, new {count = ConnectionsCount, list = UserModelList}.ToJson()); break; case "sendmsg": //中转服务器发送消息 ServiceSendMsg(json["userid"].ToString(), json["text"].ToString()); break; case "appexceptionlog": //应用异常日志 AddException(json["text"].ToString()); break; case "recovered": //找回设备 case "notice": //通知回复 case "getbluetooth": //取的蓝牙数据 case "getappblacklist": //取的应用黑名单数据 case "getphonebook": //取的电话簿 case "removeapp": //删除app if (json["result"].ToString() == "1") { HandlePushEnd(json["text"].ToString()); LogHelper.DebugAsync(string.Format("特殊推送收到成功回复,回复类型:{0},json:{1}", json["type"].ToString().ToLower(), data)); } else { LogHelper.DebugAsync(string.Format("特殊推送收到失败回复,回复类型:{0},json:{1}", json["type"].ToString().ToLower(), data)); } break; default: LogHelper.DebugAsync(string.Format("服务器接收到消息【{0}】,消息内容为{1}", connectionId, data)); break; } } catch (Exception ex) { LogHelper.ErrorAsync("接收消息异常:" + (ex.InnerException != null ? ex.InnerException.Message : ex.Message)); //错误指令返回 Connection.SendErrMsg(connectionId, ex.Message); } } /// <summary> /// 用户发送消息转发处理 /// </summary> /// <param name="userid"></param> /// <param name="msg"></param> private void ServiceSendMsg(string userid, string msg) { if (string.IsNullOrEmpty(userid)) { return; } userid = Uri.EscapeDataString(userid); var entity = UserModelList.FirstOrDefaultV(q => q.UserId == userid && q.UserType == (int) UserType.AppUser); if (entity != null) { //指定用户发送消息 Connection.Send(entity.UserConnerctionId, msg); } LogHelper.DebugAsync(string.Format("服务推送消息给设备用户【{0}】,消息内容为{1}", userid, msg)); } /// <summary> /// 上线 /// </summary> /// <param name="request"></param> /// <param name="connectionId"></param> /// <param name="text"></param> private async Task Online(IRequest request, string connectionId, string text = "") { try { //获得用户信息 if (string.IsNullOrEmpty(text)) { text = request.QueryString["userid"]; if (string.IsNullOrEmpty(text)) { return; } } if (string.IsNullOrEmpty(text)) { throw new Exception("参数{text}不能为空"); } var texts = text.Split('|'); if (texts.Length < 2) { throw new Exception("参数{text}异常:{\"type\":\"online\",\"text\":\"imei|usertype|sign|timestamp\"}"); } var userid = texts[0]; //用户 var usertype = 0; if (!int.TryParse(texts[1], out usertype)) { throw new Exception( "参数{text}异常:{\"type\":\"online\",\"text\":\"imei|usertype|sign|timestamp\"},usertype参数异常"); } //存储用户 var model = UserModelList.FirstOrDefaultV(q => q.UserConnerctionId == connectionId); if (model == null) { userid = Uri.EscapeDataString(userid); UserModelList.Add(new DeviceOnlineModel() {UserConnerctionId = connectionId, UserId = userid, UserType = usertype}); } //web上线 if (usertype == (int) UserType.SysWebUser) { //验证请求地址是否合法 var hosts = ConfigurationManager.AppSettings["SignalRClientWebHost"]; if (!string.IsNullOrEmpty(hosts)) { var arrHost = hosts.Split(','); var origin = request.Headers["Origin"]; //非法地址直接断开 if (!arrHost.Contains(origin)) { LogHelper.ErrorAsync("非法连接,请求来源:" + request.Headers.ToJson() + ",连接数据:" + text); await ServiceDisconnect(connectionId); return; } } //获得要发送的链接id列表 var sendEntity = GetRrelatedSendList(connectionId).FirstOrDefault(); if (sendEntity != null) { //记录日志 LogHelper.DebugAsync("WEB用户getall时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss fff")); //推送获取设备信息指令 await Task.Run(() => Connection.SendToDevice(sendEntity.UserConnerctionId, JsonConvert.SerializeObject(new {type = "ng", text = "getall",}))); //告知web该设备在线 //await Task.Run(() => Connection.SendToWeb(connectionId, "Online")); } else { //告知web该设备不在线 await Task.Run(() => Connection.SendToWeb(connectionId, "NoOnline")); } //记录日志 LogHelper.DebugAsync("WEB用户【" + Uri.UnescapeDataString(userid) + "】上线了"); } //设备上线 else { //验证请求是否合法 if (texts.Length != 4) { throw new Exception( "参数{text}异常:{\"type\":\"online\",\"text\":\"imei|usertype|sign|timestamp\"}"); } //检测是否合法 var signalRKey = ConfigurationManager.AppSettings["SignalRKey"]; if (signalRKey != "") { var sign = texts[2]; var timestamp = texts[3]; var sign2 = MY.Utility.Encryption.EncryptMd5($"{userid}|{usertype}|{timestamp}|{signalRKey}"); if (!sign.Equals(sign2, StringComparison.OrdinalIgnoreCase)) { LogHelper.ErrorAsync("非法连接 ,连接数据:" + text); await ServiceDisconnect(connectionId); return; } } //存在网站用户登陆 bool onlineCnt = UserModelList.AnyV(o => o.UserId == userid && o.UserType == (int) UserType.SysWebUser); if (onlineCnt) { //推送获取设备信息指令 await Task.Run(() => Connection.SendToDevice(connectionId, JsonConvert.SerializeObject(new {type = "ng", text = "getall",}))); } //记录日志 LogHelper.DebugAsync("设备用户【" + Uri.UnescapeDataString(text) + "】上线了"); //同步设备在线情况 SyncDeviceOnlineSituation(); //写入设备上线记录 AddDeviceConnectLog(userid, connectionId, 1, "设备上线"); } } catch (Exception e) { LogHelper.ErrorAsync("连接上线异常【" + text + "】:" + (e.InnerException != null ? e.InnerException.Message : e.Message)); LogHelper.ErrorAsync("异常堆栈:" + e.ToJson()); //错误指令返回 Connection.SendErrMsg(connectionId, e.Message); ServiceDisconnect(connectionId, false); } } /// <summary> /// 连接断开 /// </summary> protected override async Task OnDisconnected(IRequest request, string connectionId, bool stopCalled) { Interlocked.Decrement(ref ConnectionsCount); try { DeviceOnlineModel model = UserModelList.FirstOrDefaultV(q => q.UserConnerctionId == connectionId); if (model != null) { //设备离线 if (model.UserType == (int) UserType.AppUser) { var sendEntitys = GetRrelatedSendList(connectionId); if (sendEntitys != null) { foreach (var sendEntity in sendEntitys) { List<DeviceOnlineModel> onlineList = UserModelList.WhereV(o => o.UserId == model.UserId && o.UserType == (int) UserType.AppUser).ToList(); if (onlineList.Count() == 1) { //推送设备离线 await Task.Run(() => Connection.SendToWeb(sendEntity.UserConnerctionId, "NoOnline")); } } } LogHelper.DebugAsync("设备用户【" + Uri.UnescapeDataString(model.UserId + "|" + model.UserType) + "】下线了"); #pragma warning disable 4014 //同步设备在线情况 SyncDeviceOnlineSituation(); //写入设备下线记录 AddDeviceConnectLog(model.UserId, connectionId, 2, "设备下线"); #pragma warning restore 4014 } else { LogHelper.DebugAsync("WEB用户【" + Uri.UnescapeDataString(model.UserId + "|" + model.UserType) + "】下线了"); } UserModelList.Remove(model); } } catch (Exception e) { LogHelper.ErrorAsync("连接断开异常【" + connectionId + "】:" + (e.InnerException != null ? e.InnerException.Message : e.Message)); LogHelper.ErrorAsync("异常堆栈:" + e.ToJson()); } //默认调用 await base.OnDisconnected(request, connectionId, stopCalled); } /// <summary> /// 连接创建 /// </summary> protected override async Task OnConnected(IRequest request, string connectionId) { Interlocked.Increment(ref ConnectionsCount);await Online(request, connectionId); await base.OnConnected(request, connectionId); } /// <summary> /// 重新连接 /// </summary> /// <param name="request"></param> /// <param name="connectionId"></param> /// <returns></returns> protected override async Task OnReconnected(IRequest request, string connectionId) { //ConnectionsCount++; await Online(request, connectionId); await base.OnReconnected(request, connectionId); } /// <summary> /// 消息转发,通过当前消息用户链接id找到对应的用户链接id /// </summary> /// <param name="userConnerctionId"></param> /// <param name="data"></param> private async Task MsgForwarding(string userConnerctionId, string data) { if (string.IsNullOrEmpty(userConnerctionId)) { return; } //获得要发送的链接id列表 var sendEntitys = GetRrelatedSendList(userConnerctionId); if (sendEntitys != null) { foreach (var model in sendEntitys) { if (model != null) { //指定用户发送消息 await Connection.Send(model.UserConnerctionId, data); LogHelper.DebugAsync($"服务器转发消息给用户:{model.UserId}|{model.UserType},内容为:{data}"); } } } //记录用户记录 DeviceOnlineModel entity = UserModelList.FirstOrDefaultV(o => o.UserConnerctionId == userConnerctionId); if (entity != null) { //指令发送成功后回复发送端发送成功 if (entity.UserType == (int) UserType.SysWebUser) { var dic = JsonConvert.DeserializeObject<Dictionary<string, object>>(data); if (dic["text"].Equals("restart") || dic["text"].Equals("shutdown") || dic["text"].Equals("resumedefault")) { await Connection.Send(entity.UserConnerctionId, "MainSendOK"); } } LogHelper.DebugAsync("服务器接收到【" + (entity.UserType == (int) UserType.SysWebUser ? "WEB" : "设备") + "】用户【" + entity.UserId + "】,消息内容为:" + data); } } /// <summary> /// 获得发送连接id列表 /// </summary> /// <param name="userConnerctionId"></param> /// <returns></returns> public List<DeviceOnlineModel> GetRrelatedSendList(string userConnerctionId) { //发送消息的用户 var entity = UserModelList.FirstOrDefaultV(q => q.UserConnerctionId == userConnerctionId); if (entity != null) { var usertype = entity.UserType == (int) UserType.AppUser ? (int) UserType.SysWebUser : (int) UserType.AppUser; //要推送消息的用户 var sendEntitys = UserModelList.WhereV(q => q.UserId == entity.UserId && q.UserType == usertype) .ToList(); return sendEntitys; } return null; } /// <summary> /// 服务器强制断开连接 /// </summary> /// <param name="connectionId"></param> /// <param name="isSendErrMsg"></param> private async Task ServiceDisconnect(string connectionId, bool isSendErrMsg = true) { await GlobalHost.DependencyResolver.Resolve<ITransportHeartbeat>().GetConnections() .First(o => o.ConnectionId == connectionId).Disconnect(); if (isSendErrMsg) { //错误指令返回 Connection.SendErrMsg(connectionId, "非法连接,强制断开"); } } } }