- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
1、什么是SignalR 。
ASP.NET SignalR 是一个面向 ASP.NET 开发人员的库,可简化向应用程序添加实时 Web 功能的过程。 实时 Web 功能是让服务器代码在可用时立即将内容推送到连接的客户端,而不是让服务器等待客户端请求新数据.
SignalR使用的三种底层传输技术分别是Web Socket, Server Sent Events 和 Long Polling, 它让你更好的关注业务问题而不是底层传输技术问题.
WebSocket是最好的最有效的传输方式 , 如果浏览器或Web服务器不支持它的话(IE10之前不支持Web Socket), 就会降级使用SSE, 实在不行就用Long Polling.
(现在也很难找到不支持WebSocket的浏览器了,所以我们一般定义必须使用WebSocket) 。
。
2、我们做一个聊天室,实现一下SignalR前后端通讯 。
由简入深,先简单实现一下 。
2.1 服务端Net5 。
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using System; using System.Threading.Tasks; namespace ServerSignalR.Models { public class ChatRoomHub:Hub { public override Task OnConnectedAsync() // 连接成功触发 { return base .OnConnectedAsync(); } public Task SendPublicMsg( string fromUserName, string msg) // 给所有client发送消息 { string connId = this .Context.ConnectionId; string str = $ " [{DateTime.Now}]{connId}\r\n{fromUserName}:{msg} " ; return this .Clients.All.SendAsync( " ReceivePublicMsg " ,str); // 发送给ReceivePublicMsg方法,这个方法由SignalR机制自动创建 } } }
Startup添加 。
static string _myAllowSpecificOrigins = "MyAllowSpecificOrigins"; public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc( " v1 " , new OpenApiInfo { Title = " ServerSignalR " , Version = " v1 " }); }); services.AddSignalR(); services.AddCors(options => { options.AddPolicy(_myAllowSpecificOrigins, policy => { policy.WithOrigins("http://localhost:4200" ) .AllowAnyHeader().AllowAnyMethod().AllowCredentials(); }); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint( " /swagger/v1/swagger.json " , " ServerSignalR v1 " )); } app.UseCors(_myAllowSpecificOrigins); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapHub <ChatRoomHub>("/Hubs/ChatRoomHub" ); }); }
2.2 前端Angular 。
引入包 。
npm i --save @microsoft/signalr
ts:
import { Component, OnInit } from ' @angular/core ' ; import * as signalR from ' @microsoft/signalr ' ; import { CookieService } from ' ngx-cookie-service ' ; @Component({ selector: ' app-home ' , templateUrl: ' ./home.component.html ' , styleUrls: [ ' ./home.component.scss ' ] }) export class HomeComponent implements OnInit { msg = '' ; userName = ' kxy ' public messages: string [] = []; public hubConnection: signalR.HubConnection; constructor( private cookie: CookieService ) { this .hubConnection= new signalR.HubConnectionBuilder() .withUrl( ' https://localhost:44313/Hubs/ChatRoomHub ' , { skipNegotiation: true , // 跳过三个协议协商 transport:signalR.HttpTransportType.WebSockets, // 定义使用WebSocket协议通讯 } ) .withAutomaticReconnect() .build(); this .hubConnection.on( ' ReceivePublicMsg ' ,msg=> { this .messages.push(msg); console.log(msg); }); } ngOnInit(): void { } JoinChatRoom(){ this .hubConnection.start() . catch (res=> { this .messages.push( ' 连接失败 ' ); throw res; }).then(x => { this .messages.push( ' 连接成功 ' ); }); } SendMsg(){ if (! this .msg){ return ; } this .hubConnection.invoke( ' SendPublicMsg ' , this .userName, this .msg); } }
这样就简单实现了SignalR通讯!!! 。
有一点值得记录一下 。
问题:强制启用WebSocket协议,有时候发生错误会被屏蔽,只是提示找不到/连接不成功 。
解决:可以先不跳过协商,调试完成后再跳过 。
3、引入Jwt进行权限验证 。
安装Nuget包:Microsoft.AspNetCore.Authentication.JwtBearer
Net5的,注意包版本选择5.x,有对应关系 。
Startup定义如下 。
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; using ServerSignalR.Models; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using JwtHelperCore; namespace ServerSignalR { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get ; } // This method gets called by the runtime. Use this method to add services to the container. static string _myAllowSpecificOrigins = " MyAllowSpecificOrigins " ; public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc( " v1 " , new OpenApiInfo { Title = " ServerSignalR " , Version = " v1 " }); }); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.RequireHttpsMetadata = false ; // 是否需要https options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false , // 是否验证Issuer ValidateAudience = false , // 是否验证Audience ValidateLifetime = true , // 是否验证失效时间 ValidateIssuerSigningKey = true , // 是否验证SecurityKey IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes( " VertivSecurityKey001 " )), // 拿到SecurityKey }; options.Events = new JwtBearerEvents()//从url获取token { OnMessageReceived = context => { if (context.HttpContext.Request.Path.StartsWithSegments("/Hubs/ChatRoomHub"))//判断访问路径 { var accessToken = context.Request.Query["access_token"];//从请求路径获取token if (!string.IsNullOrEmpty(accessToken)) context.Token = accessToken;//将token写入上下文给Jwt中间件验证 } return Task.CompletedTask; } }; } ); services.AddSignalR(); services.AddCors(options => { options.AddPolicy(_myAllowSpecificOrigins, policy => { policy.WithOrigins( " http://localhost:4200 " ) .AllowAnyHeader().AllowAnyMethod().AllowCredentials(); }); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint( " /swagger/v1/swagger.json " , " ServerSignalR v1 " )); } app.UseCors(_myAllowSpecificOrigins); app.UseHttpsRedirection(); app.UseRouting(); // Token 授权、认证 app.UseErrorHandling(); // 自定义的处理错误信息中间件 app.UseAuthentication(); // 判断是否登录成功 app.UseAuthorization(); // 判断是否有访问目标资源的权限 app.UseEndpoints(endpoints => { endpoints.MapHub <ChatRoomHub>( " /Hubs/ChatRoomHub " ); endpoints.MapControllers(); }); } } }
红色部分为主要关注代码!!! 。
因为WebSocket无法自定义header,token信息只能通过url传输,由后端获取并写入到上下文 。
认证特性使用方式和http请求一致:
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using System; using System.Linq; using System.Threading.Tasks; namespace ServerSignalR.Models { [Authorize] // jwt认证 public class ChatRoomHub:Hub { public override Task OnConnectedAsync() // 连接成功触发 { return base .OnConnectedAsync(); } public Task SendPublicMsg( string msg) // 给所有client发送消息 { var roles = this .Context.User.Claims.Where(x => x.Type.Contains( " identity/claims/role " )).Select(x => x.Value).ToList(); // 获取角色 var fromUserName = this .Context.User.Identity.Name; // 从token获取登录人,而不是传入(前端ts方法的传入参数也需要去掉) string connId = this .Context.ConnectionId; string str = $ " [{DateTime.Now}]{connId}\r\n{fromUserName}:{msg} " ; return this .Clients.All.SendAsync( " ReceivePublicMsg " ,str); // 发送给ReceivePublicMsg方法,这个方法由SignalR机制自动创建 } } }
然后ts添加 。
constructor( private cookie: CookieService ) { var token = this .cookie. get ( ' spm_token ' ); this .hubConnection= new signalR.HubConnectionBuilder() .withUrl( ' https://localhost:44313/Hubs/ChatRoomHub ' , { skipNegotiation: true , // 跳过三个协议协商 transport:signalR.HttpTransportType.WebSockets, // 定义使用WebSocket协议通讯 accessTokenFactory:()=> token.slice(7,token.length)//会自动添加Bearer头部,我这里已经有Bearer了,所以需要截掉 } ) .withAutomaticReconnect() .build(); this .hubConnection.on( ' ReceivePublicMsg ' ,msg=> { this .messages.push(msg); console.log(msg); }); }
4、私聊 。
Hub 。
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using System; using System.Collections.Generic; using System.Threading.Tasks; namespace ServerSignalR.Models { [Authorize] // jwt认证 public class ChatRoomHub:Hub { private static List<UserModel> _users = new List<UserModel> (); public override Task OnConnectedAsync() // 连接成功触发 { var userName = this .Context.User.Identity.Name; // 从token获取登录人 _users.Add( new UserModel(userName, this .Context.ConnectionId)); return base .OnConnectedAsync(); } public override Task OnDisconnectedAsync(Exception exception) { var userName = this .Context.User.Identity.Name; // 从token获取登录人 _users.RemoveRange(_users.FindIndex(x => x.UserName == userName), 1 ); return base .OnDisconnectedAsync(exception); } public Task SendPublicMsg( string msg) // 给所有client发送消息 { var fromUserName = this .Context.User.Identity.Name; // var ss = this.Context.User!.FindFirst(ClaimTypes.Name)!.Value; string str = $ " [{DateTime.Now}]\r\n{fromUserName}:{msg} " ; return this .Clients.All.SendAsync( " ReceivePublicMsg " ,str); // 发送给ReceivePublicMsg方法,这个方法由SignalR机制自动创建 } public Task SendPrivateMsg( string destUserName, string msg) { var fromUser = _users.Find(x=>x.UserName== this .Context.User.Identity.Name); var toUser = _users.Find(x=>x.UserName== destUserName); string str = $ "" ; if (toUser == null ) { msg = $ " 用户{destUserName}不在线 " ; str = $ " [{DateTime.Now}]\r\n系统提示:{msg} " ; return this .Clients.Clients(fromUser.WebScoketConnId).SendAsync( " ReceivePrivateMsg " , str); } str = $ " [{DateTime.Now}]\r\n{fromUser.UserName}-{destUserName}:{msg} " ; return this .Clients.Clients(fromUser.WebScoketConnId,toUser.WebScoketConnId).SendAsync( " ReceivePrivateMsg " , str); } } }
TS:
// 加一个监听 this .hubConnection.on( ' ReceivePublicMsg ' , msg => { this .messages.push( ' 公屏 ' + msg); console.log(msg); }); this .hubConnection.on( ' ReceivePrivateMsg ' ,msg=> { this .messages.push( ' 私聊 ' + msg); console.log(msg); }); // 加一个发送 if ( this .talkType == 1 ) this .hubConnection.invoke( ' SendPublicMsg ' , this .msg); if ( this .talkType == 3 ){ console.log( ' 11111111111111 ' ); this .hubConnection.invoke( ' SendPrivateMsg ' , this .toUserName, this .msg); }
5、在控制器中使用Hub上下文 。
Hub链接默认30s超时,正常情况下Hub只会进行通讯,而不再Hub里进行复杂业务运算 。
如果涉及复杂业务计算后发送通讯,可以将Hub上下文注入外部控制器,如 。
namespace ServerSignalR.Controllers { // [Authorize] public class HomeController : Controller { private IHubContext<ChatRoomHub> _hubContext; public HomeController(IHubContext<ChatRoomHub> hubContext) { _hubContext = hubContext; } [HttpGet( " Welcome " )] public async Task<ResultDataModel< bool >> Welcome() { await _hubContext.Clients.All.SendAsync( " ReceivePublicMsg " , " 欢迎 " ); return new ResultDataModel< bool >( true ); } } }
。
至此,感谢关注!! 。
。
最后此篇关于SignalRWebSocket通讯机制的文章就讲到这里了,如果你想了解更多关于SignalRWebSocket通讯机制的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
Closed. This question does not meet Stack Overflow guidelines。它当前不接受答案。 想要改善这个问题吗?更新问题,以便将其作为on-topi
有没有办法只允许 https 而不是 http 与 Neo4j 服务器通信?另外,Neo4j Shell 的通信使用哪个 channel ,http 还是 https? 最佳答案 这来自 Neo4j
您好,我有新问题 :) 我正在构建带有面板的简单时事通讯,以向注册用户和时事通讯邮件地址发送邮件。 我有此代码,但时事通讯仅发送给注册用户。谁能告诉我为什么? $zapytanie = mys
第一次发帖,所以可能会有比必要的更多的信息,但我想彻底: 我们的 C 练习之一是创建发送器和接收器程序,通过 RS232 串行通信与零调制解调器交换数据。我们使用了虚拟端口程序(如果你想测试的话,我使
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我有一台通过 RS485 连接到另一台设备的单板计算机。计算机应向设备发送请求并接收响应(使用设备相关协议(protocol))。我可以毫无问题地发送消息并且设备接收它们(例如,我可以更改设备的参数)
我目前正在尝试在我的 Visual Basic 6 应用程序中引用 .NET COM 库。我已经使用 Regasm 注册了它,并且在我的类(class)中将 ComVisible 设置为 true。但
Closed. This question needs to be more focused 。它目前不接受答案。 想改善这个问题吗?更新问题,使其仅通过 editing this post 关注一个
我尝试通过 https 协议(protocol)在 Archiva 和 Jenkins 之间建立通信,但我收到以下错误: [WARNING] Could not transfer metadata .
我完成了一个运行良好的客户端/服务器套接字通信程序。现在我想弄清楚如何做到这一点,以便我可以同时拥有到服务器的多个客户端连接。我环顾四周,似乎有不止几种不同的方法可以做到这一点。所以我来这里是想向你们
我正在 mailchimp 中制作时事通讯,我在使用 outlook 时遇到了这个问题,它一直在干扰我的两个专栏,如图所示: 这是这部分的代码:
我正在创建一份时事通讯,经过大量努力,它在除 android 的 gmail 应用程序之外的任何地方都有效。问题是它似乎有最小字体大小,这会导致我的表格损坏。 有没有办法克服最小字体大小而不是媒体查询
C++ 作为Client端 view plaincopy to clipboardprint? 复制代码 代码如下: // Client.cpp : Defines the entry poi
我创建了 ECM NewsLetter,其中包含一些网站链接(另一个项目)。在在线版本的 NewsLetter 链接中工作正常,但是当我将此 NewsLetter 发送到我的电子邮件并尝试单击我的邮件
无论出于何种原因,我的文本大小调整在 iPhone 上无法正常工作,但在 Android 和其他电子邮件格式中工作正常。似乎看不出这两个文件之间有任何区别。图片也调整了大小,似乎只是文本的问题。
我正在开发一个网站,我需要将 HTML 新闻稿发送到邮件列表。 我构建了一个 html 框架,其中包含“在浏览器中查看”和“取消订阅”链接(原因会有所不同)。在管理模块中,我发布 html 并为 ht
我正在尝试在 B-L072Z-LRWAN(Master) 和 Arduino(Slave) 之间进行 I2C 通信。 我使用以下代码成功将数据从主站发送到从站: B-L072Z-LRWAN 代码: #
我有 ECM NewsLetter,其中包含指向站点(另一个项目)的一些链接。当我通过单击发送按钮将此时事通讯发送到我的电子邮件时 - 当我从电子邮件收件箱中单击它们时,链接不起作用。它显示 404
我是一名优秀的程序员,十分优秀!