在.Net Core中实现一个WebSocket路由

640?wx_fmt=png

Net Core中使用WebSocket默认是没有路由系统的,只能通过Request.Path=="/xxx"来判断请求,例如:

 1 2 3 4 5 6 7 8 91011121314151617181920
app.Use(async (context, next) =>{    if (context.Request.Path == "/ws")    {        if (context.WebSockets.IsWebSocketRequest)        {            WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync();            await Echo(context, webSocket);        }        else        {            context.Response.StatusCode = 400;        }    }    else    {        await next();    }});

要使用类似[HttpGet("/xxx")]这种特性标签的路由方式,可以自己写一个简单的Attribute来实现。

这个Attribute类很简单,只接收一个定义的Path,用来和开头提到的Request.Path对应。

 1 2 3 4 5 6 7 8 910111213
/// <summary>    /// WebSockets执行函数特性标签    /// </summary>[AttributeUsage(AttributeTargets.Method,Inherited = false)] public class WebSocketsAttribute:Attribute {     // WebSocket请求的Path    public string Path;    public WebSocketsAttribute(string path)    {        Path = path;    }}

使用一个帮助类来根据请求的PATH分发给对应的函数。该类中有两个静态方法,实现同样的功能,区别是一个会缓存请求的Path和处理请求函数,另一个是实时反射解析的,实际使用中当然推荐带缓存的,因为反射对性能的损耗也是很大的。

2.1. 不带缓存的分发函数

 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536
/// <summary>/// 分发websockets请求(没有缓存)/// </summary>/// <param name="assemblyName">对应处理程序所在的程序集名称</param>/// <param name="context">HTTP上下文</param>/// <param name="task">TASK任务</param>/// <returns></returns>public static async Task DistributeAsync(string assemblyName, HttpContext context, Func<Task> task){    try    {        // 获取在WebSockets下面的websocket处理methods        var assembly = Assembly.Load(assemblyName);        // 根据特性标签获取对应的执行函数        var methods = assembly            .GetTypes()            .SelectMany(s => s.GetMethods())            .First(f => f                .GetCustomAttributes(typeof(WebSocketsAttribute), false)  // 这一步获取打了WebSocketsAttribute特性标签的函数                .Any(w => ((WebSocketsAttribute)w).Path == context.Request.Path)); // 过滤找出其中WebSocketsAttribute标签中Path参数对应的执行函数        if (context.WebSockets.IsWebSocketRequest)        {            var webSockt = await context.WebSockets.AcceptWebSocketAsync();            // 异步调用该方法            await (Task)methods.Invoke(null, new object[] { context, webSockt });        }        else        {            context.Response.StatusCode = 400;        }    }    catch (Exception)    {        await task();    }}

2.2. 带缓存的分发函数

 1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031323334353637383940414243444546
// 使用并发安全的字段类型缓存请求Path与处理请求函数private static ConcurrentDictionary<string, MethodInfo> ExecuteMethods;/// <summary>/// 分发websockets请求(缓存执行函数)/// </summary>/// <param name="assemblyName">对应处理程序所在的程序集名称</param>/// <param name="context">HTTP上下文</param>/// <param name="task">TASK任务</param>/// <returns></returns>public static async Task DistributeAsync(string assemblyName, HttpContext context, Func<Task> task){    if (!ExecuteMethods.Any())    {        // 获取在WebSockets下面的websocket处理methods        var assembly = Assembly.Load(assemblyName);        // 根据特性标签获取对应的执行函数        var methods = assembly            .GetTypes()            .SelectMany(s => s.GetMethods())            .Select(s => new            {                ((WebSocketsAttribute) s.GetCustomAttributes(typeof(WebSocketsAttribute), false).First()).Path,                s            }).ToArray();        ExecuteMethods =            new ConcurrentDictionary<string, MethodInfo>(methods.ToDictionary(t => t.Path, t => t.s));        await task();    }    else    {        if (context.WebSockets.IsWebSocketRequest)        {            var webSockt = await context.WebSockets.AcceptWebSocketAsync();            // 从键值对中获取对应的执行函数            ExecuteMethods.TryGetValue(context.Request.Path, out var method);            if (method != null)                // 异步调用该方法                await (Task) method.Invoke(null, new object[] {context, webSockt});        }        else        {            context.Response.StatusCode = 400;        }    }}

startup.cs中的Config函数中可以使用我们自己创建的分发函数,需要注意的是我们自定义的函数需要传入程序集的名称,也就是真实的请求处理函数所在的程序集。

由此可见,这种方式使用的一些限制,请求处理函数必须写在一个程序集里面,不过我想一般情况下,没有人会把同一类型的处理函数写在不同的程序集,所以这应该不算是问题。

 1 2 3 4 5 6 7 8 91011121314
/// <summary>/// startup.cs/// </summary>public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory){    ...    // 增加websocket    app.Use(async (context, next) =>    {        // 将原本的Echo方法换成我们自定义的分发函数        await WebSocketsHandler.DistributeAsync("NetCoreFrame.WebSockets", context, next);    });    ...}

使用方式很简单,就是一个正常的WebSocket处理函数,只不过标注了我们自己的“路由特性标签”。

 1 2 3 4 5 6 7 8 91011121314151617181920
public class Test{    [WebSockets("/ws")]    public static async Task Echo(HttpContext context, WebSocket webSocket)    {        var buffer = new byte[1024 * 4];        var result =            await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);        while (!result.CloseStatus.HasValue)        {            await webSocket.SendAsync(                    new ArraySegment<byte>(buffer, 0, result.Count),                    result.MessageType, result.EndOfMessage,                    CancellationToken.None                );            result = await webSocket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);        }        await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None);    }}

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/websockets?view=aspnetcore-2.2

原文地址:

http://haijd.tech/csharp/%E5%9C%A8.net-core%E4%B8%AD%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AAwebsocket%E8%B7%AF%E7%94%B1/


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/315191.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【CF1189D】Add on a Tree【结论】【构造】

传送门 题意&#xff1a;给一棵NNN个结点的树&#xff0c;构造或判断无法构造若干次操作&#xff0c;每次操作选择两个叶节点将路径上的边加上一定整数权值&#xff0c;使得每条边等于给定权值。权值互不相同且为偶数。 1≤N≤10001 \leq N \leq10001≤N≤1000 结论&#xff…

Codeforces Round #628 (Div. 2) F. Ehab‘s Last Theorem dfs树

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 给你个nnn个点mmm条边的图&#xff0c;可以选择完成以下两个任务中的一个&#xff1a; (1)(1)(1)找出大小恰好为n\sqrt nn​的一个独立集。 (2)(2)(2)找出一个长度≥n\ge \sqrt n≥n​的一个环。 n≤1e5,m≤…

Dapper介绍--Micro-ORM

一&#xff0e;概述目前对于.net的数据访问ORM工具很多&#xff0c;EF和EF Core是一个重量级的框架。最近在搭建新的项目架构&#xff0c;来学习一下轻量级的数据访问ORM工具Dapper。Dapper支持SQL Server&#xff0c;MySQL&#xff0c;Sqlite&#xff0c;SqlCE&#xff0c;Fir…

剑指 Offer 31. 栈的压入、弹出序列

思路&#xff1a;模拟就完事 class Solution { public:bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {stack<int> c;int idx0;for(int v:pushed){c.push(v);while(c.size()&&c.top()popped[idx]){c.pop();idx;}…

【CF1189F】Array Beauty【dp】【复杂度玄学优化】

传送门 题意&#xff1a;给一个长度为NNN的序列aaa,从中选出kkk个&#xff0c;定义一个序列的美丽度为最接近的两个数的差的绝对值&#xff0c;求所有方案的美丽度之和模998244353998244353998244353。 N≤1000,ai≤100000N \leq 1000,a_i \leq100000N≤1000,ai​≤100000 显…

Educational Codeforces Round 84 (Rated for Div. 2) D. Infinite Path 构建环 + 思维

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 懒得写了&#xff0c;直接贴图了。 思路&#xff1a; 遇事不决画成图&#xff0c;考虑将iii向p[i]p[i]p[i]连一个边&#xff0c;可以发现每个点入度为111&#xff0c;出度为111&#xff0c;所以画出来是若…

WSL+VSCODE体验UBUNTU环境下的开发

首先安装 WSL&#xff0c;我这里选择的是 ubuntu18.04 这个应用。切换 WSL 的默认用户为 root 用户切换成 root 用户主要是避免后续开发中遇到权限问题比较麻烦&#xff0c;直接默认 root 解决问题。找到ubuntu安装目录&#xff0c;一般在C:\Program Files\WindowsApps\Canonic…

程序员修仙之路--优雅快速的统计千万级别uv

菜菜&#xff0c;咱们网站现在有多少PV和UV了&#xff1f;Y总&#xff0c;咱们没有统计pv和uv的系统&#xff0c;预估大约有一千万uv吧写一个统计uv和pv的系统吧网上有现成的&#xff0c;直接接入一个不行吗&#xff1f;别人的不太放心&#xff0c;毕竟自己写的&#xff0c;自己…

【CF1230E】Kamil and Making a Stream【GCD性质】【暴力vector】

传送门 题意:给一棵带点权的有根树&#xff0c;求所有满足uuu是vvv的祖先的路径(u,v)(u,v)(u,v)的路径上所有点权的gcdgcdgcd的和模1e971e971e97。 N≤100000N \leq 100000N≤100000 看到连续gcdgcdgcd多半是根据单调性维护链表之类的 对于每个点&#xff0c;记录所有祖先到…

P3391 【模板】文艺平衡树 fhq-treap 模板

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 这是学splaysplaysplay的时候的一个模板题&#xff0c;之前学过fhq−treapfhq-treapfhq−treap&#xff0c;但是没怎么用他做过题&#xff0c;今天一做还发现不少问题&#xff0c;真是眼高…

123. 买卖股票的最3佳时机 III

思路&#xff1a;动态规划&#xff0c; dp1是当前第一次买入的最大值 dp2是当前第一次卖出的最大值 dp3是当前第二次买入的最大值 dp4是当前第二次卖出的最大值 转移看代码&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {int dp1-…

Matrix Decompressing UVA - 11082 最大流 + 输出方案

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 给出前iii行的元素和A[i]A[i]A[i]&#xff0c;前jjj列的元素B[j]B[j]B[j]&#xff0c;让你构造一个矩阵使得其满足前iii行的元素和是A[i]A[i]A[i]&#xff0c;前jjj列的元素和是B[j]B[j]B[j]。 r,c≤20r,c\l…

微软正式开源WSL 2的内核源码

微软在今年5月举办的 Build 2019 上宣布了第二代 Windows 的 Linux 子系统 —— WSL 2。与第一代相比&#xff0c;WSL 2 重新设计了架构&#xff0c;使用真正的 Linux 内核&#xff0c;支持在 Windows 上运行 ELF64 Linux 二进制文件。按照计划&#xff0c;WSL 2 的初始版本已于…

【NOI2016】优秀的拆分【后缀数组】【ST表】【关键点】【调和级数复杂度】【差分】

传送门 题意&#xff1a;如果一个字符串可以拆分为AABB的形式&#xff0c;其中A和B是任意非空字符串&#xff0c;则我们这种拆分是优秀的。求给定串的所有子串的拆分方案数之和。 N≤30000N \leq30000N≤30000 本来是个神仙题 但明明一个O(nlogn)O(nlogn)O(nlogn)的题为啥只…

剑指 Offer 43. 1~n 整数中 1 出现的次数(数位dp)

思路&#xff1a;就是数位dp&#xff0c;dp[idx][sum][limit]代表&#xff0c;到idx位&#xff0c;前面有sum个0&#xff0c;有没有limit限制&#xff1b; class Solution { public:int dp[20][50][2];int len; int pos[20];int countDigitOne(int n) {for(int i0;i<15;i)f…

Windows新终端中玩转ASCII和Emoji游戏的正确姿势

前一段时间&#xff0c;我搬运了几个Windows Terminal中玩游戏的视频.Windows Terminal - 动图GIF作背景图Windows Terminal - 母牛说HiWindows Terminal - 字符水族箱今天我来给大家展示一下具体的玩法~Emoji版双人碰碰球目前有个现成的 .NET core 项目可以直接用&#xff0c;…

剑指 Offer 38. 字符串的排列(有重复元素的排列)

思路&#xff1a;dfs 暴搜 class Solution { public:vector<string> permutation(string s) {vector<string> a;function<bool(char,int,int)> check[&](char x,int st,int idx){if(st>idx) return false;for(int ist;i<idx;i) if(s[i]x) return …

Pool construction UVA - 1515 最小割模型

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 给你一个n∗mn*mn∗m的矩阵&#xff0c;包含字符#和.&#xff0c;将#变成.的代价是ddd&#xff0c;将.变成#的代价是fff&#xff0c;让后将#和.隔开的代价是bbb&#xff0c;现在让你用最小的代价将#和.隔开&…

【洛谷P4707】重返现世【扩展Min-Max容斥】【dp】

传送门 题意&#xff1a;NNN种物品&#xff0c;每次第iii种产生概率为piM\frac{p_i}{M}Mpi​​,∑piM\sum p_iM∑pi​M。求生成KKK种不同物品的期望时间 模998244353998244353998244353 N≤1000,M≤10000,N−K≤10N \leq1000,M \leq 10000,N-K\leq10N≤1000,M≤10000,N−K≤10…

nowcoder Forsaken的数列 fhq-treap

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 很明显的数据结构了&#xff0c;splaysplaysplay当然能写&#xff0c;但是fhq−treapfhq-treapfhq−treap更加简洁易懂。 考虑第一个操作&#xff0c;无非就是分裂出[1,pos−1][1,pos-1][1…