WebSocket Client连接AspNetCore SignalR Json Hub
突然有个需求,需要使用普通的websocket客户端去连接SignalR服务器。
因为使用的是.net core 版的signalr,目前对于使用非signalr客户端连接的中文文档几乎为0,在gayhub折腾几天总算折腾出来了。
首先,在startup.cs的ConfigureServices方法中添加signalr配置
services.AddSignalR(options =>
{
// Faster pings for testing
options.KeepAliveInterval = TimeSpan.FromSeconds(5);//心跳包间隔时间,单位 秒,可以稍微调大一点儿
}).AddJsonProtocol(options =>
{
//options.PayloadSerializerSettings.Converters.Add(JsonConver);
//the next settings are important in order to serialize and deserialize date times as is and not convert time zones
options.PayloadSerializerSettings.Converters.Add(new IsoDateTimeConverter());
options.PayloadSerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Unspecified;
options.PayloadSerializerSettings.DateParseHandling = DateParseHandling.DateTimeOffset;
});
在使用微信小程序的websocket的时候,可以在websocket请求头中加入了一个字段
app.UseSignalR(routes =>
{
routes.MapHub<AbpCommonHub>("/signalr", options => options.WebSockets.SubProtocolSelector = requestedProtocols =>
{
return requestedProtocols.Count > 0 ? requestedProtocols[0] : null;
});
routes.MapHub<Chat.SignalR.Chat>("/chat", options => options.WebSockets.SubProtocolSelector = requestedProtocols =>
{
return requestedProtocols.Count > 0 ? requestedProtocols[0] : null;
});
然后是Chat.cs. 因为我用的是Abp.AspNetCore.SignalR,所以在写法上会略微和aspnetcore.signalr有区别
using Abp.Dependency;
using Abp.Runtime.Session;
using Castle.Core.Logging;
using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;
public class Chat : Hub, ITransientDependency
{
public IAbpSession AbpSession { get; set; }
public ILogger Logger { get; set; } public Chat()
{
AbpSession = NullAbpSession.Instance;
Logger = NullLogger.Instance;
} public async Task SendMessage(string message)
{
await Clients.All.SendAsync("getMessage", string.Format("User {0}: {1}", AbpSession.UserId, message));
} public override async Task OnConnectedAsync()
{
await base.OnConnectedAsync();
Logger.Debug("A client connected to MyChatHub: " + Context.ConnectionId);
} public override async Task OnDisconnectedAsync(Exception exception)
{
await base.OnDisconnectedAsync(exception);
Logger.Debug("A client disconnected from MyChatHub: " + Context.ConnectionId);
} public void log(string arg)
{
Logger.Info("client send:" + arg);
}
这样在浏览器输入http://myhost/chat (myhost换成自己的域名 或者是ip:端口)
出现 Connection ID required 就说明signalr启动成功了。
对于websocket客户端来说,服务器连接地址就是把http改为ws就可以了。如http://192.168.1.100:21012/chat,则对应的连接地址就是ws://192.168.1.100:21012/chat
然后是客户端代码。这里我使用的是ClientWebSocket
ClientWebSocket client = new ClientWebSocket();
client.Options.AddSubProtocol("protocol1");
wait client.ConnectAsync(new Uri(BaseUrl), CancellationToken.None);
Console.WriteLine("Connect success"); await client.SendAsync(new ArraySegment<byte>(AddSeparator(Encoding.UTF8.GetBytes(@"{""protocol"":""json"", ""version"":1}")))
, WebSocketMessageType.Text, true, CancellationToken.None);//发送握手包
Console.WriteLine("Send success");
var bytes = Encoding.UTF8.GetBytes(@"{
""type"": 1,
""invocationId"":""123"",
""target"": ""log"",
""arguments"": [
""Test Message""
]
}"")");//发送远程调用 log方法
await client.SendAsync(new ArraySegment<byte>(AddSeparator(bytes)), WebSocketMessageType.Text, true, CancellationToken.None);
var buffer = new ArraySegment<byte>(new byte[]);
while (true)
{
await client.ReceiveAsync(buffer, CancellationToken.None);
Console.WriteLine(Encoding.UTF8.GetString(RemoveSeparator(buffer.ToArray())));
}
添加和删除分隔符方法
private static byte[] AddSeparator(byte[] data)
{
List<byte> t = new List<byte>(data) { 0x1e };//0x1e record separator
return t.ToArray();
}
private static byte[] RemoveSeparator(byte[] data)
{
List<byte> t = new List<byte>(data);
t.Remove(0x1e);
return t.ToArray();
}
然后就能在服务器日志中查看到log方法调用后的结果
然后是微信小程序的连接代码
在api.js中定义要连接的url,这里有个小技巧,http的自动替换为ws,https自动替换为wss,这样本地调试和服务器运行都只用修改serverRoot这个url就可以了。
var _serverRoot = 'https://myhost.com/';
var websocket = (_serverRoot.startsWith("https") ?
_serverRoot.replace('https', 'wss') : _serverRoot.replace('http', 'ws')) + 'signalr';
然后定义调用的连接方法。token是在登录的时候调用abp登录方法返回并setStorage,然后就可以在任意页面获取到token。
这样连接的时候传入token,在signalr的远程调用的时候,abpSession中就能获取到用户信息,同时也避免了无关客户端连接远程调用。
还需要稍微修改一下ABP的验证部分代码,Host项目中的AuthConfigurer.cs的QueryStringTokenResolver方法,从HttpContext.Request的QueryString中获取accesstoken。
private static Task QueryStringTokenResolver(MessageReceivedContext context)
{
if (!context.HttpContext.Request.Path.HasValue ||
!context.HttpContext.Request.Path.Value.StartsWith("/signalr"))
{
//We are just looking for signalr clients
return Task.CompletedTask;
}
var qsAuthToken = context.HttpContext.Request.Query["accesstoken"].FirstOrDefault();
if (qsAuthToken == null)
{
//Cookie value does not matches to querystring value
return Task.CompletedTask;
}
//Set auth token from cookie
context.Token = qsAuthToken;//SimpleStringCipher.Instance.Decrypt(qsAuthToken, AppConsts.DefaultPassPhrase);
return Task.CompletedTask;
}
function WsConnect(){
var token = wx.getStorageSync('token');
var url = {
url: api.websocket + '?accesstoken=' + token,
header: {
'Abp.TenantId': 2,
'Content-Type': 'application/json'
}
};
wx.connectSocket(url);
} function wsSend(msg){
console.log('send:'+msg); msg += String.fromCharCode(0x1e);
wx.sendSocketMessage({
data: msg,
});
}
发送的时候需要在后面添加一个分隔符0x1e。所以稍微封装了一下。
然后就是接收处理和断开重连处理。需要注意一下,signalr连接成功后要发送握手包指定协议。{"protocol":"json","version":1}就表示是使用的json
wx.onSocketClose(function () {
console.log("链接关闭 ");
setTimeout(() => util.WsConnect(), 5000);//5s后自动重连
}) wx.onSocketError(function (res) {
console.log('WebSocket连接打开失败,请检查!');
console.log(res);
setTimeout(() => util.WsConnect(), 5000);//5s后自动重连
}); wx.onSocketOpen(res => {//websocket打开连接成功回调
util.wsSend('{"protocol":"json","version":1}');//发送握手包
wx.onSocketMessage(function (res) {//接收消息回调
var data = res.data.replace(String.fromCharCode(0x1e), "");//返回时去掉分隔符
console.log("recv:"+data);
}
}
贴一下能运行查看的小程序代码片段 wechatide://minicode/3YTuJZmP7BYZ
粘贴这个到微信web开发者工具--导入代码片段中
修改连接地址
然后在控制台接收到{“type”:6} 服务器发送的心跳包,就说明signalr连接成功了
后续将会讲解一下signalr core的Hub协议和远程调用方式。未完待续
WebSocket Client连接AspNetCore SignalR Json Hub的更多相关文章
- Client 客户端AspNetCore.SignalR 通讯服务器 Quartz 执行任务
背景 需要Client跑服务在终端间隔执行任务,我的目标是运行在树莓派上 Client代码 如果未连接成功时隔3秒重新连接服务器 public static void Reconnect() { va ...
- 微信小程序与AspNetCore SignalR聊天实例
微信小程序与aspnetcore signalr实例 本文不对小程序与signalr做任何介绍,默认读者已经掌握 aspnetcore Signalr文档 小程序文档 写在之前 SignalR没有提供 ...
- Deribit交易所 websocket API 连接范例
Deribit websocket API 连接范例,使用JavaScript语言,策略运行在FMZ发明者量化平台. 源码地址:https://www.fmz.com/strategy/147765 ...
- Spring+Stomp+ActiveMq实现websocket长连接
stomp.js+spring+sockjs+activemq实现websocket长连接,使用java配置. pom.xml(只列出除了spring基本依赖意外的依赖,spring-version为 ...
- 使用四种框架分别实现百万websocket常连接的服务器
著名的 C10K 问题提出的时候, 正是 2001 年.这篇文章可以说是高性能服务器开发的一个标志性文档,它讨论的就是单机为1万个连接提供服务这个问题,当时因为硬件和软件的**,单机1万还是一个非常值 ...
- 在Signalr的Hub中写方法实现与安卓的数据交互
简介: 实现数据实时刷新:SignalR 后台服务:.NET/WebAPI 为了减轻web的压力,将接口中接收数据的方法写到SignalR的Hub中 在此放一小段代码给自己加深下印象,博主有点健忘.. ...
- websocket 70K连接测试
websocket 70K连接测试 最近使用socket.io做了一个实时应用,实时性.稳定性还是很让人满意的.如果拿socket.io来做小型应用,综合效率应该是最高的.但是网上少有socket.i ...
- ABP 找不到版本为 (>= 1.0.0-preview1-27891) 的包 Microsoft.AspNetCore.SignalR 错误
错误描述: 下载ABP模板项目3.4.1的版本(当前最新版本),编译加载nuget包Microsoft.AspNetCore.SignalR时会报如下错误: 严重性 代码 说明 ...
- Asp.NetCore+Microsoft.AspNetCore.SignalR前后端分离
1.新建WebApi 2.安装Microsoft.AspNetCore.SignalR 3.新建一个集线器和消息类 using Microsoft.AspNetCore.SignalR; using ...
随机推荐
- MySQL on Azure高可用性设计 DRBD - Corosync - Pacemaker - CRM (一)
MySQL迁移到Azure上后,由于云的特性,在自建数据中心的MySQL的HA的方法在云上很多都不能部署. 这主要是因为,目前Public Cloud不支持:1. 共享存储:2. Multicast: ...
- 【转】Pro Android学习笔记(十三):用户界面和控制(1):UI开发
目录(?)[-] UI开发 方式一通过XML文件 方式二通过代码 方式三XML代码 UI开发 先理清一些UI概念: view.widget.control:这三个名词其实没有什么区别,都是一个UI元素 ...
- String/ StringBuilder/ StringBuffer
1. 首先String不属于8种基本数据类型,String是一个对象. 因为对象的默认值是null,所以String的默认值也是null:但它又是一种特殊的对象,有其它对象没有的一些特性. 2. ne ...
- CSS3新增的伪类
Element1 ~ element2:选择前面有element1的所有element2元素 [attr ^= val] 属性值以val开头的元素 [attr $= val] 属性值以val结尾的元素 ...
- Spring5.0的第一次尝鲜
对于这次尝鲜,说白了和Spring5.0的新特性基本没有多大的关系,如果说您不小心进来了,却发发现文章的内容和标题似乎不太匹配,那么我将是非常的抱歉,因为这浪费了您宝贵的时间.但是我还是要说:因为这确 ...
- oracle上课 学习2 oracle 游标 存储过程 有用
1.1. 训练描述 使用游标,打印emp中20号部门的所有员工的信息 操作步骤答案 declare cursor c_emp is select * from emp where deptno=10 ...
- 树莓派 Learning 002 必备的操作 --- 08 实现PC端 远程登入 树莓派 --- 法1 远程登入树莓派的命令行状态
树莓派 必备的操作 - 实现PC端 远程登入 树莓派 - 法1 远程登入树莓派的命令行状态 我的树莓派型号:Raspberry Pi 2 Model B V1.1 装机系统:NOOBS v1.9.2 ...
- vue.js2.0实战(1):搭建开发环境及构建项目
Vue.js学习系列: vue.js2.0实战(1):搭建开发环境及构建项目 https://my.oschina.net/brillantzhao/blog/1541638 vue.js2.0实战( ...
- 【Qt官方例程学习笔记】Address Book Example(代理模型)
地址簿示例展示了如何使用代理模型在单个模型的数据上显示不同的视图. 本例提供了一个地址簿,允许按字母顺序将联系人分组为9组:ABC.DEF.GHI.…,VW,…XYZ.这是通过在同一个模型上使用多个视 ...
- HTTP协议格式及基础
HTTP请求数据: HTTP请求信息由3部分组成: ① 请求方法 URI 协议/版本 ② 请求头(Request Header) ③ 请求正文 HTTP 请求 数据 例子举例: GET/sam ...