最新 .NET Core 中 SignalR的使用 以及 WebSocket的使用 同时在Asp.Net MVC 中 WebSocket的使用

ASP.NET MVC 中使用WebSocket 笔记

1.采用控制器的方法 这个只写建立链接的方法的核心方法

1.1 踩坑 网上都是直接 传个异步方法 直接接受链接 自己尝试了好多次链接是打开的,到时获取不到链接里面的内容!! (如果是我理解有问题的话,欢迎讨论,毕竟这个问题卡了我好久!)

1.2 自己建立链接的使用截图 方法

   

/// <summary>
        /// WebSocket链接 demo地址 ws://Localhost:8080/Get
        /// </summary>
        [HttpGet]
        public void Get()
        {
            if (System.Web.HttpContext.Current.IsWebSocketRequest)
            {
                //System.Web.HttpContext.Current.AcceptWebSocketRequest(AnalysisOptionChat);
                System.Web.HttpContext.Current.AcceptWebSocketRequest(async (context) => await AnalysisOptionChat(context));
            }
        }
 

1.3 判断是WebSocket 链接后 处理消息

1.4当数据量太大时,数据正在缓冲,所以还需要判断数据是否获取完整并且拼接

EndOfMessage = false 时的处理

/// <summary>
        /// 拓展方法
        /// </summary>
        /// <param name="ws">websocket</param>
        /// <param name="Capacity">容量</param>
        /// <returns></returns>
        public static async System.Threading.Tasks.Task<(ArraySegment<byte> Buffer, WebSocketReceiveResult Result,int bufferLength)> ReceiveMsgAsync(System.Net.WebSockets.WebSocket ws, long Capacity = 1024)
        {
            //容器,至多只能保存初始化大小的信息
            //Capacity必须大于0,否者代码在执行到这里,前端websocket会断开
            ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[Capacity]);
            //初始定义化消息结果类,主要是将result.EndOfMessage设为false
            WebSocketReceiveResult result = new WebSocketReceiveResult(0, WebSocketMessageType.Binary, false);
            //用于保存完整消息的所有片段
            List<byte[]> Message = new List<byte[]>();
            //消息未完全接收,用于接收完整的消息
            while (!result.EndOfMessage)
            {
                //接收消息
                result = await ws.ReceiveAsync(buffer, System.Threading.CancellationToken.None);
                Message.Add(buffer.Take(result.Count).ToArray());//保存消息片段
            }
            byte[] temp = new byte[0];//储存完整消息的容器
            Message.ForEach(u => temp = temp.Concat(u).ToArray());//将所有消息片段进行整合(提取实际数据)
            buffer = new ArraySegment<byte>(temp);//将完整消息保存到容器中(buffer)
            return (buffer, result, buffer.ToArray().Length);
        }
/// <summary>
        /// WebSocket链接操作
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private async Task AnalysisOptionChat(AspNetWebSocketContext context)
        {
            var webSocket = context.WebSocket;
            #region 测试demo
            try
            {
                ConsoleWrite.ConsoleInfo($"Websocket client add--> 自己需要实现 保存链接");
                WebSocketReceiveResult clientData = null;
                do
                {
                    var buffer = new ArraySegment<byte>(new byte[1024 * 1000]);
                    //if (clientData.MessageType == WebSocketMessageType.Text && !clientData.CloseStatus.HasValue)
                    if (webSocket.State == WebSocketState.Open && clientData.MessageType == WebSocketMessageType.Text)
                    {
                        string msgString = Encoding.UTF8.GetString(buffer.Array, 0, clientData.Count);
                        var sendData = string.Empty;
                        if ("rub".Equals(msgString))//心跳
                        {

                        }
                        else
                        {
                            int size = clientData.Count;

                            #region 通知
                            #endregion

                            #region DB持久化 操作
                            #endregion
                        }
                    }
                }
                //while (!clientData.CloseStatus.HasValue);
                while (webSocket.State == WebSocketState.Open);

                var state = webSocket.State;
                ConsoleWrite.ConsoleWarning($"Websocket client closed1-->{state.ToString()}");
                ConsoleWrite.ConsoleWarning($"Websocket client closed2-->{clientData.CloseStatus.Value}");
                //关闭事件
                await webSocket.CloseAsync(clientData.CloseStatus.Value, clientData.CloseStatusDescription, CancellationToken.None);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            #endregion
}

1.4 在Asp.Net MVC 中使用可能会遇到 跨域的问题 一下是解决方案

 1 
<system.webServer>
    <!--全局跨域-->
    <httpProtocol>
      <customHeaders>
        <add name="Access-Control-Allow-Origin" value="*" />
        <!--<add name="Access-Control-Allow-Headers" value="*" />
        <add name="Access-Control-Allow-Methods" value="GET,POST,PUT,DELETE,OPTIONS" />-->
      </customHeaders>
    </httpProtocol>
<system.webServer>

.NET Core 中使用WebSocket 笔记

1.在WepApi中使用法 这个只写建立链接的方法的核心方法

1.首先需要在Startup.cs 里面的 ConfigureServices里面配置跨域 如果不牵扯跨域的话 可以忽略 1,2 (里面的跨域) 跨域详细配置如下

services.AddCors(options =>
            {
                options.AddPolicy("WebSocketCors", builder =>
                {
                    builder.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
                });
            });

2.然后在 Configure方法 里面加入管道 

app.UseWebSockets(new Microsoft.AspNetCore.Builder.WebSocketOptions
            {
                //保持活动间隔
                KeepAliveInterval = TimeSpan.FromMinutes(5),
            });

            // 注意这个是重点!!!!
            app.UseMiddleware<WebsocketHandlerMiddleware>();

 //自定义跨域规则
            app.UseCors("WebSocketCors");

详细截图如下

 3. 请求中间件 WebsocketHandlerMiddleware.cs 详细介绍如下

public class WebsocketHandlerMiddleware
    {
        private readonly RequestDelegate _next;

        public WebsocketHandlerMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            if (context.Request.Path == "/ws")
            {
                //客户端与服务器成功建立连接后,服务器会循环异步接收客户端发送的消息,收到消息后就会执行Handle(WebsocketClient websocketClient)中的do{}while;直到客户端断开连接
                //不同的客户端向服务器发送消息后台执行do{}while;时,websocketClient实参是不同的,它与客户端一一对应
                //同一个客户端向服务器多次发送消息后台执行do{}while;时,websocketClient实参是相同的
                if (context.WebSockets.IsWebSocketRequest)
                {
                    var webSocket = await context.WebSockets.AcceptWebSocketAsync();

                }
                else
                {
                    context.Response.StatusCode = 404;
                }
            }
            else
            {
                await _next(context);
            }
        }
    }

里面处理 WebSocket 请求消息的内容 和 1.3  中方法是一样的 我这里就不重复了 两者的区别都只是 获取方式不一样而已。

.NET Core 中使用SignalR笔记

1.在WepApi中使用法 这个只写建立链接的方法的核心方法 以及相关方法介绍

所使用的Nuget 包 如下:

1.1 首先需要在Startup.cs 里面的 ConfigureServices方法 注入且配置跨域.

 1
#region SignalR

            services.AddCors(options =>
            {
                options.AddPolicy("SignalRCors", builder =>
                {
                    builder.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials();
                });
            });

            services.AddSignalR().AddHubOptions<SignalrHub>(options =>
            {
                options.EnableDetailedErrors = true;
            })
            // 支持MessagePack
            .AddMessagePackProtocol();

            #endregion

同时还需要 注入加入声明周期 管控 

//特殊的
services.AddSingleton<SignalrHub>();

1.2 在Configure方法里面 的相关使用

//自定义跨域规则
app.UseCors("SignalRCors");

//需要在 Map里面 指定路由
//app.UseHttpsRedirection();
 app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
                //可以设置SignalR相关参数,这里设置地址
                endpoints.MapHub<SignalrHub>("hubs/signalr", options =>
                {
            //配置 几种方式
                    options.Transports = HttpTransportType.WebSockets | HttpTransportType.LongPolling;
                });
            });

1.3 SignalrHub.cs 集线器类的使用介绍 里面有许多用不上的 自己按照需求自己过滤 主要就是 OnConnectedAsync() 建立链接 OnDisconnectedAsync()断开链接 方法的使用!

  1 
//创建SingalR中心跨域
    [EnableCors("SignalRCors")]
    public  class SignalrHub : Hub
    {
        //ReceiveMessage 为客户端监听的 方法名称
        private static string clientSendMethodName = "ReceiveMessage";

        //Redis 存储信息Key
        private static string signalrRedisKey = "SignalRConnections";

        public SignalrHub()
        {

        }


        #region Send

        /// <summary>
        /// 发送消息-指定用户集合发送消息
        /// </summary>
        /// <param name="userIds">通知用户集合</param>
        /// <param name="sendObject">通知OBject</param>
        /// <returns></returns>
        public  async Task SendUserMessage(List<int> userIds,object sendObject)
        {
            //从redis 获取 userIds 对应的 ConnectionId 进行推送
            var connectionUserList = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue);
            if(connectionUserList.Count()<=0) throw new Exception("无连接用户,无法进行消息推送。");
            var sentUserClient = connectionUserList.Where(x => userIds.Contains(x.UserId)).ToList();
            var sendMeaaageToJson = JsonHelper.ObjectToJsonCamelCase(sendObject);

            var connectionIds = sentUserClient.Select(y => y.ConnectionId);

            //全部
            //await Clients.All.SendAsync(clientSendMethodName, new { data = sendMeaaageToJson });

            //指定
            await Clients.Clients(connectionIds).SendAsync(clientSendMethodName, new { data = sendMeaaageToJson });
        }


        //发送消息--发送给所有连接的客户端
        public async Task SendMessage(string msg)
        {
            //await Clients.All.SendAsync(clientSendMethodName, msg);
            Console.WriteLine($"保持心跳链接-->接收参数:{msg}");
        }



        /// <summary>
        /// 除 connectionId 之外的所有发送message
        /// </summary>
        /// <param name="connectionId"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public Task SendAllExceptMe(string connectionId, string message)
        {

            return Clients.AllExcept(connectionId).SendAsync(clientSendMethodName, $"{Context.ConnectionId}: {message}");
        }

        #endregion



        #region Group分组的话 依据职位 或者 角色来业务循环获取



        #endregion


        #region overrides

        /// <summary>
        /// 当新的客户端连接建立时执行的操作
        /// </summary>
        /// <returns></returns>
        public override async Task OnConnectedAsync()
         {
            //建立者用户Id
            var clientUserId = AuthHelper.GetUserId(Context.User);
            //建立者用户Name
            var clientUserName = Context.User.Identity.Name;
            //建立ConnectionId
            var connectionId = Context.ConnectionId;

            if (clientUserId <= 0 || string.IsNullOrWhiteSpace(clientUserName))
            {
                throw new Exception("建立连接异常,无法获取用户信息。");
            }

            Console.WriteLine($"OnConnectedAsync建立链接----userId:{clientUserId},userName:{clientUserName},connectionId:{ Context.ConnectionId}");

            var userConnections = new List<UserConnection>();
            userConnections.Add(new UserConnection() { UserId = clientUserId, UserName = clientUserName, ConnectionId = connectionId, CreateTime = DateTime.Now });

            //redis存储连接用户信息
            RedisService.SetLPushValue(signalrRedisKey, false, userConnections);

            //获取所有用户的链接信息
            //var aaa = RedisService.GetLPushData<UserConnection>(signalrRedisKey,0,10000000);

            await base.OnConnectedAsync();
        }


        /// <summary>
        /// 当客户端断开连接时执行的操作
        /// </summary>
        /// <param name="exception"></param>
        /// <returns></returns>
        public override async Task OnDisconnectedAsync(Exception exception)
        {
            //建立者用户Id
            var clientUserId = AuthHelper.GetUserId(Context.User);
            //建立者用户Name
            var clientUserName = Context.User.Identity.Name;
            //建立ConnectionId
            var connectionId = Context.ConnectionId;
            //NLogHelper.ActionWarn();

            Console.WriteLine($"OnDisconnectedAsync断开链接----userId:{clientUserId},clientUserName:{clientUserName},connectionId:{ connectionId}");

            var connectionUserList = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue);

            //在redis里面依据 Value 移除
            var removeItem = connectionUserList.Where(x => x.ConnectionId == connectionId).FirstOrDefault();
            if (removeItem != null)
            {
                var Drow = await RedisService.LRemAsync(signalrRedisKey, removeItem);
                //var connectionUserLists = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue);
            }
            await base.OnDisconnectedAsync(exception);
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
        }

        #endregion


        #region ClearOverTime 清除超时的垃圾数据

        public async Task ClearOverTimeUserRedis(int houre=24)
        {
            var connectionUserList = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue);
            var thisTime = DateTime.Now;
            var delConnection = connectionUserList.Where(x=>(thisTime-x.CreateTime).Duration().TotalHours >= houre).ToList();
            if (delConnection != null)
            {
                foreach (var delItem in delConnection)
                {
                    var Drow = await RedisService.LRemAsync(signalrRedisKey, delItem);
                    //var connectionUserLists = RedisService.GetLPushData<UserConnection>(signalrRedisKey, 0, int.MaxValue);
                }
            }
        }

        #endregion
    }

1.4 SignalrHub.cs 里面所用到的用户链接 UserConnection.cs 类

[Serializable]
    public class UserConnection
    {
        /// <summary>
        /// 用户Id
        /// </summary>
        public int UserId { set; get; }

        /// <summary>
        /// 用户名称
        /// </summary>
        public string UserName { set; get; }

        /// <summary>
        /// 连接Id
        /// </summary>
        public string ConnectionId { set; get; }

        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateTime { set; get; }
    }

作者声明:

1.文章转载请注明出处 !!! 

2.文章 如有不正确之处欢迎大家讨论,交流, 如果感觉写的还行,或者帮助了您,请点个赞哈,再次谢过~

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值