博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WebSocket Client连接AspNetCore SignalR Json Hub
阅读量:6189 次
发布时间:2019-06-21

本文共 7177 字,大约阅读时间需要 23 分钟。

突然有个需求,需要使用普通的websocket客户端去连接SignalR服务器。

因为使用的是.net core 版的signalr,目前对于使用非signalr客户端连接的中文文档几乎为0,在gayhub折腾几天总算折腾出来了。

 

首先,在startup.cs的ConfigureServices方法中添加signalr配置

1
2
3
4
5
6
7
8
9
10
11
12
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
("/signalr", options => options.WebSockets.SubProtocolSelector = requestedProtocols =>                 {
                    return requestedProtocols.Count > 0 ? requestedProtocols[0] : null;                 }); routes.MapHub
("/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

1 ClientWebSocket client = new ClientWebSocket(); 2 client.Options.AddSubProtocol("protocol1"); 3 wait client.ConnectAsync(new Uri(BaseUrl), CancellationToken.None); 4 Console.WriteLine("Connect success"); 5  6 await client.SendAsync(new ArraySegment
(AddSeparator(Encoding.UTF8.GetBytes(@"{""protocol"":""json"", ""version"":1}"))) 7 , WebSocketMessageType.Text, true, CancellationToken.None);//发送握手包 8 Console.WriteLine("Send success"); 9 var bytes = Encoding.UTF8.GetBytes(@"{10 ""type"": 1,11   ""invocationId"":""123"",12 ""target"": ""log"",13 ""arguments"": [14 ""Test Message""15 ]16 }"")");//发送远程调用 log方法17 await client.SendAsync(new ArraySegment
(AddSeparator(bytes)), WebSocketMessageType.Text, true, CancellationToken.None);18 var buffer = new ArraySegment
(new byte[1024]);19 while (true)20 {21 await client.ReceiveAsync(buffer, CancellationToken.None);22 Console.WriteLine(Encoding.UTF8.GetString(RemoveSeparator(buffer.ToArray())));23 }

添加和删除分隔符方法

private static byte[] AddSeparator(byte[] data){    List
t = new List
(data) { 0x1e };//0x1e record separator return t.ToArray();}private static byte[] RemoveSeparator(byte[] data){ List
t = new List
(data); t.Remove(0x1e); return t.ToArray();}

然后就能在服务器日志中查看到log方法调用后的结果

 

然后是微信小程序的连接代码

在api.js中定义要连接的url,这里有个小技巧,http的自动替换为ws,https自动替换为wss,这样本地调试和服务器运行都只用修改serverRoot这个url就可以了。

1
2
3
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;        }

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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协议和远程调用方式。未完待续

 

转载于:https://www.cnblogs.com/webenh/p/11014860.html

你可能感兴趣的文章
11.11 开课二个月零七天(ajax和bootstrp做弹窗)
查看>>
自定义控件之圆形的image
查看>>
《机电传动大作业》笔记一
查看>>
TypeScript 素描-变量声明
查看>>
查看字体列表,查看字体名称
查看>>
oracle 定时器调用存储过程
查看>>
centos6.8中zabbix3.0.X的部署
查看>>
$.getjson遇到的几个问题-json返回数据中带有html标签的输出
查看>>
关于对话框DoModal()函数调用失败的原因分析
查看>>
Sublime Text 2快捷键大全
查看>>
C++强制类型转换
查看>>
hdu 1787 GCD Again (欧拉函数)
查看>>
工作中如何不累,保持好心态;遇到问题,尽全力解决,不能解决也不能以死谢罪,饭还要吃,觉还是要睡...
查看>>
泰国佛历的换算问题
查看>>
资源记录页面
查看>>
Boostrap(2)
查看>>
linux 输入java 出现中文乱码
查看>>
Linux文件夹文件创建、删除
查看>>
hadoop三种运行模式
查看>>
打开IE8总是提示欢迎使用?怎样使它不提示?
查看>>