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, "非法连接,强制断开");
            }
        }

    }

}

 

继SignalR 持久链接 Web客户端

posted @ 2020-04-21 18:15  拾梦小侠ด้้้  阅读(405)  评论(0编辑  收藏  举报