突然有个需求,需要使用普通的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. 1.4 isAlive()方法

    方法isAlive()是判断当前线程是否处于活动状态. 线程代码: public class TestThread extends Thread{ @Override public void run( ...

  2. 计算MySQL的内存峰值公式 (转)

    -- 计算MySQL的内存峰值公式,计算所有的连接满了的情况下: select (@@key_buffer_size + @@query_cache_size + @@tmp_table_size   ...

  3. sys.argv用法

    argv是在脚本内部使用,旨在接受命令传参 比如,一个脚本argv.py,代码里面有,sys.argv[1],,sys.argv[2],那么运行这个脚本时,必须在后面跟两个参数,用空格隔开,如:pyt ...

  4. HDU1026(延时迷宫:BFS+优先队列)

    Ignatius and the Princess I Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (J ...

  5. JVM体系结构之六:堆Heap之2:新生代及新生代里的两个Survivor区(下一轮S0与S1交换角色,如此循环往复)、常见调优参数

    一.为什么会有年轻代 我们先来屡屡,为什么需要把堆分代?不分代不能完成他所做的事情么?其实不分代完全可以,分代的唯一理由就是优化GC性能.你先想想,如果没有分代,那我们所有的对象都在一块,GC的时候我 ...

  6. FATFS 文件系统

    转载请注明出处:http://blog.csdn.net/qq_26093511/article/details/51706228 1.文件系统是什么? 负责管理和存储文件信息的软件机构称为文件管理系 ...

  7. Tomcat 服务器详解

    工具/原料 1.JDK:版本为jdk-7-windows-i586.exe  下载地址   http://www.oracle.com/technetwork/java/javase/download ...

  8. source in sight 删除工程

    用十六进制编辑器打开  "我的文档/Source Insight/Projects/PROJECTS.DB3" 文件 ,找到你要删除的项目路径及名称字符串,用0替换相关位置的数据.

  9. 图像滤波与OpenCV中的图像平滑处理

    .About图像滤波 频率:可以这样理解图像频率,图像中灰度的分布构成一幅图像的纹理.图像的不同本质上是灰度分布规律的不同.但是诸如"蓝色天空"样的图像有着大面积近似的灰度强度,而 ...

  10. keras安装windows版

    按照官网成功了.下面没有成功,貌似是 Anacode的问题 http://blog.csdn.net/hweiyi/article/details/70018317 http://blog.csdn. ...