突然有个需求,需要使用普通的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. 制作spark镜像

    构建镜像 添加jdk引用(可以使用yum进行安装): 安装SSH 碰到一个问题,执行systemctl的时候发生了异常: Failed to get D-Bus connection 解决这个问题的方 ...

  2. 【Android学习笔记】 点击穿透(Click Through)

    问题:开发一个App,主界面用了Activity,子页面用了Fragment.从Activity跳转到Fragment后Fragment透明,并且点击击穿到Axtivity. 分析:刚开始没有注意到点 ...

  3. POJ1001(C++处理大数)

    Exponentiation Time Limit: 500MS   Memory Limit: 10000K Total Submissions: 158025   Accepted: 38470 ...

  4. Project Web Server PSI 接口一些常用操作

    对Project Web Server进行二次开发,每天都把自己折腾到12点以后才休息,到处都是坑,研究那些烦人的PSI,国内根本查不到PSI相关的资料,对照API文档一点点谷歌资料,全部英文资料,开 ...

  5. TCP/IP的3次握手和4次握手

    在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接,如图1所示. (1) 第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等 ...

  6. jprofiler9.1.1 安装与配置

    一.安装部分 安装包: 1.jprofiler_linux_9_1_1.rpm 2.jprofiler_windows-x64_9_1_1.exe 需要注意的是,Linux 和 Windows 安装的 ...

  7. Hive Joins 用法与操作

    Hive表连接的语法支持如下: join_table: table_reference JOIN table_factor [join_condition] | table_reference {LE ...

  8. Luogu 4556 雨天的尾巴

    主席树+线段树合并. 首先我们想一想如果只有一个结点的话,我们弄一个权值线段树就可以随便维护了. 那么我们可以运用差分的思想,把一个询问拆成四个操作,对于一个询问$(x, y, v)$,我们在$x$的 ...

  9. Entity Framework Code-First(9.6):DataAnnotations - StringLength Attribute

    DataAnnotations - StringLength Attribute: StringLength attribute can be applied to a string type pro ...

  10. HDU 5980 Find Small A (水题)

    题意:众所周知,字符 'a' 的ASCII码为97.现在,找出给定数组中出现了多少次 'a' .注意,此处的数字为计算机中的32位整数.这表示, 1个数字由四个字符组成(一个字符由8位二进制数组成). ...