突然有个需求,需要使用普通的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请求头中加入了一个字段

Sec-WebSocket-Protocol: protocol1
这样没有返回这个协议头的服务器就无法连接的。
要求服务器在返回的时候也需要在头中返回对应协议头。
在startup.cs的Configure方法中配置Hub时加入子协议。   需要注意一下Chat.SignalR.Chat是命名空间+类名,也就是下边要写的Chat.cs
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的更多相关文章

  1. Client 客户端AspNetCore.SignalR 通讯服务器 Quartz 执行任务

    背景 需要Client跑服务在终端间隔执行任务,我的目标是运行在树莓派上 Client代码 如果未连接成功时隔3秒重新连接服务器 public static void Reconnect() { va ...

  2. 微信小程序与AspNetCore SignalR聊天实例

    微信小程序与aspnetcore signalr实例 本文不对小程序与signalr做任何介绍,默认读者已经掌握 aspnetcore Signalr文档 小程序文档 写在之前 SignalR没有提供 ...

  3. Deribit交易所 websocket API 连接范例

    Deribit websocket API 连接范例,使用JavaScript语言,策略运行在FMZ发明者量化平台. 源码地址:https://www.fmz.com/strategy/147765 ...

  4. Spring+Stomp+ActiveMq实现websocket长连接

    stomp.js+spring+sockjs+activemq实现websocket长连接,使用java配置. pom.xml(只列出除了spring基本依赖意外的依赖,spring-version为 ...

  5. 使用四种框架分别实现百万websocket常连接的服务器

    著名的 C10K 问题提出的时候, 正是 2001 年.这篇文章可以说是高性能服务器开发的一个标志性文档,它讨论的就是单机为1万个连接提供服务这个问题,当时因为硬件和软件的**,单机1万还是一个非常值 ...

  6. 在Signalr的Hub中写方法实现与安卓的数据交互

    简介: 实现数据实时刷新:SignalR 后台服务:.NET/WebAPI 为了减轻web的压力,将接口中接收数据的方法写到SignalR的Hub中 在此放一小段代码给自己加深下印象,博主有点健忘.. ...

  7. websocket 70K连接测试

    websocket 70K连接测试 最近使用socket.io做了一个实时应用,实时性.稳定性还是很让人满意的.如果拿socket.io来做小型应用,综合效率应该是最高的.但是网上少有socket.i ...

  8. ABP 找不到版本为 (>= 1.0.0-preview1-27891) 的包 Microsoft.AspNetCore.SignalR 错误

    错误描述: 下载ABP模板项目3.4.1的版本(当前最新版本),编译加载nuget包Microsoft.AspNetCore.SignalR时会报如下错误: 严重性     代码         说明 ...

  9. Asp.NetCore+Microsoft.AspNetCore.SignalR前后端分离

    1.新建WebApi 2.安装Microsoft.AspNetCore.SignalR 3.新建一个集线器和消息类 using Microsoft.AspNetCore.SignalR; using ...

随机推荐

  1. 【转】 Pro Android学习笔记(五五):调试和分析(3):adb命令、模拟器控制台和StrictMode

    目录(?)[-] adb命令 模拟器Console StrictMode adb命令 我们在学习SQLite的使用,介绍过部分adb命令的使用,见Pro Android学习笔记(五):了解Conten ...

  2. 通过设置swift中container的ACL提供匿名访问及用户授权读取服务

    在上层使用swift提供的云存储服务的过程中,提出了无需验证的使用需求. 在参考了:http://my.oschina.net/alanlqc/blog/160196(curl命令操作) 官方文档: ...

  3. Mybatis+Mysql 返回主键的值

    需求:使用MyBatis往MySQL数据库中插入一条记录后,需要返回该条记录的自增主键值. 方法:在mapper中指定keyProperty属性,示例如下: <insert id="i ...

  4. UE3优化

    转自:http://www.cnblogs.com/NEOCSL/p/3320510.html 优化问题有很多内容可讲,涉及林林总总.今天我总结一下优化注意的地方. 1.从AnimTree和Skele ...

  5. SpringBoot系列(1)

    简介:用来简化新Spring应用的初始搭建以及开发过程:该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置. 特点:1. 创建独立的Spring应用程序2. 嵌入的Tomcat, ...

  6. 通过 命令提示符(cmd.exe)连接 Oracle 数据库

    通过IP 连接数据库: sqlplus userName/userPassword@//IP:port/SID 例:sqlplus testuser/123456@//192.168.0.1:1521 ...

  7. tomcat jsp 数字串传值异常问题

    1.在下面的jsp内嵌java代码去除num之前,有某Controller已经有了操作:      request.getSession().setAttribute("num", ...

  8. SSDB VS redis

    现在有不少团队开始使用了一个新型高效的 NoSQL数据库 - SSDB,如 京东.唱吧 …… SSDB 官网的定义 一个高性能的支持丰富数据结构的 NoSQL 数据库,用于替代 Redis 官网 ht ...

  9. C++之queue模板类

    1.queue常用成员函数 ①size() 返回队列中元素的个数 ②empty() 如果队列空则返回真  ③back() 返回最后一个元素引用即队尾. ④front() 返回第一个元素引用即队首. ⑤ ...

  10. maven 打JAR包资源文件指定路径与文件读取

    1.配置RESOURCES节点 有时会遇到maven打包后找不到资源文件了,其实这时候路径发生了变化,为了确保打包后路径不发生变化,指定一个固定的路径,请看下面配置 <build> ... ...