忽然有个需求,须要使用普通的websocket客户端去链接SignalR服务器。javascript
由于使用的是.net core 版的signalr,目前对于使用非signalr客户端链接的中文文档几乎为0,在gayhub折腾几天总算折腾出来了。java
首先,在startup.cs的ConfigureServices方法中添加signalr配置web
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请求头中加入了一个字段json
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启动成功了。api
对于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<byte>(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<byte>(AddSeparator(bytes)), WebSocketMessageType.Text, true, CancellationToken.None); 18 var buffer = new ArraySegment<byte>(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<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就能够了。
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协议和远程调用方式。未完待续