0


后端 C# .net服务器 前端 原生网页、微信小程序、uni-app 集成 SignalR 做 即时通讯

一、 后端服务器

    后端集成SignalR比较简单,首先要在解决方案中依赖几个SignalR的库,SignalR的库就是做即时通讯的主要库,我们建立连接、收发信息都需要用这个库来进行。![](https://img-blog.csdnimg.cn/direct/1ac9313b666845ad81f155bf2558f705.png)

    除了这几个库以外,还要还有几个依赖库要一并依赖进来。

    Owin库的作用主要是为了在项目StartUp时将SignalR库的几个Hub文件注册起来,Hub文件就相当于是我们接口中的Controller文件。

    然后在项目下创建一个文件夹Hubs,Hubs下创建文件ChatHub

using Common.Log;
using Microsoft.Ajax.Utilities;
using Microsoft.AspNet.SignalR;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using WebGrease;
using WinexHK.Hubs.ConectionOperate;

namespace WinexHK.Hubs
{
///


/// ChatHub
///

public class ChatHub : PersistentConnection
{
private ConnectionManagement summaryTableCon = new ConnectionManagement();

    /// <summary>
     /// 连接成功后调用
     /// </summary>
     /// <param name="request"></param>
     /// <param name="connectionId"></param>
     /// <returns></returns>
     protected override Task OnConnected(IRequest request, string connectionId)
     {
         //获得SignalR的连接id
         var connid = connectionId;
         //获得用户id
         var userid = "123321";
         Console.Write($"【{connid}】:已建立连接!");

        //判断一下用户是不是已经链接了
         var checkUserConn = summaryTableCon.IsConn(connid, userid);
         if (!checkUserConn)
         {
             //添加一个新的连接
             summaryTableCon.AddConnInfo(new SignalRConn()
             {
                 UserId = userid,
                 ConnectionId = connid
             });
         }
         //更新连接
         else
         {
             summaryTableCon.UpdateConnInfo(userid, connid);
         }

        return Connection.Send(connectionId, $"【用户:{connectionId}】真正连接成功!");
         //return base.OnConnected(request, connectionId);
     }

    /// <summary>
     /// 接收到请求的时候调用
     /// </summary>
     /// <param name="request"></param>
     /// <param name="connectionId"></param>
     /// <param name="data"></param>
     /// <returns></returns>
     protected override async Task OnReceived(IRequest request, string connectionId, string data)
     {
         var keys = new Dictionary<string, string>
         {
             { "葡萄酒", "wine" },
             { "红酒", "wine" },
             { "白酒", "wine" },
             { "霞多丽", "grapery" },
             { "雷司令", "grapery" }
         };

        var values = new Dictionary<string, string>
         {
             { "wine", "这是一款葡萄酒" },
             { "grapery", "这是一串葡萄" }
         };

        //获得用户id
         var userid = "123321";
         await Task.Factory.StartNew(async () =>
         {
             var key = "";
             for (int i = 0; i < keys.Keys.Count; i++)
             {
                 if (data.Contains(keys.Keys.ToList()[i]))
                 {
                     key = keys.Values.ToList()[i];
                     break;
                 }
             }
             var value = "未找到匹配条目";
             if (values.ContainsKey(key))
             {
                 value = values[key];
             }

            await Connection.Send(connectionId, value);
         }, TaskCreationOptions.LongRunning);
     }

    /// <summary>
     /// 连接中断的时候调用
     /// </summary>
     /// <param name="request"></param>
     /// <param name="connectionId"></param>
     /// <param name="stopCalled"></param>
     /// <returns></returns>
     protected override Task OnDisconnected(IRequest request, string connectionId, bool stopCalled)
     {
         Console.Write($"【{connectionId}】:已断开连接!");
         //获得SignalR的连接id
         var connid = connectionId;
         //关闭连接
         summaryTableCon.DelConnInfo(connid);
         return base.OnDisconnected(request, connectionId, stopCalled);
     }

    /// <summary>
     /// 连接超时重新连接的时候调用
     /// </summary>
     /// <param name="request"></param>
     /// <param name="connectionId"></param>
     /// <returns></returns>
     protected override Task OnReconnected(IRequest request, string connectionId)
     {
         return base.OnReconnected(request, connectionId);
     }
 }

}

    文件里有几个回调方法,OnConnected用户连接时,OnReceived收到信息时、OnDisconnected用户断开连接,OnReconnected用户重连,也可以通过Connection.Send方法给连接用户发送消息。

    下面是Hub里用到的用到的ConnectionManagement的代码,用来保存并管理用户的连接状态。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WinexHK.Hubs.ConectionOperate
{
///


/// 连接管理
///

public class ConnectionManagement
{
///
/// 用户连接集合
///

public static List<SignalRConn> SignalRConns { get; set; } = new List<SignalRConn>();

    /// <summary>
     /// 添加连接
     /// </summary>
     /// <param name="conn"></param>
     public void AddConnInfo(SignalRConn conn)
     {
         SignalRConns.Add(conn);
     }

    /// <summary>
     /// 删除连接
     /// </summary>
     /// <param name="connid"></param>
     public void DelConnInfo(string connid)
     {
         var signalRConns = SignalRConns.FirstOrDefault(u => u.ConnectionId == connid);
         if (signalRConns != null)
         {
             SignalRConns.Remove(signalRConns);
         }
     }

    /// <summary>
     /// 更新链接(老的链接不起作用了)
     /// 场景:客户端重连了,userid没变,但是connid变了
     /// </summary>
     /// <param name="userId">用户id</param>
     /// <param name="newConnsId">新的链接id</param>
     public void UpdateConnInfo(string userId, string newConnsId)
     {
         var signalRConns = SignalRConns.FirstOrDefault(u => u.UserId.ToLower() == userId.ToLower());
         if (signalRConns != null)
         {
             signalRConns.ConnectionId = newConnsId;
         }
     }

    /// <summary>
     /// 判断用户是否已经链接
     /// </summary>
     /// <param name="connid">连接id</param>
     /// <param name="userid">用户id</param>
     /// <returns></returns>
     public bool IsConn(string connid, string userid)
     {
         var userConn = SignalRConns.FirstOrDefault(u => u.ConnectionId.ToLower() == connid.ToLower() && u.UserId.ToLower() == userid.ToLower());
         return userConn == null ? false : true;
     }
 }

}

    最后是用到的一个类,保存用户id及连接id

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace WinexHK.Hubs.ConectionOperate
{
///


/// 连接
///

public class SignalRConn
{
///
/// 系统用户id
///

public string UserId { get; set; }

    /// <summary>
     /// SignleR链接Id(每次链接SignalR都会分配一个id)
     /// </summary>
     public string ConnectionId { get; set; }
 }

}

二、 PC版原生网页

    这几个类定义好,基本上后端的代码就完成了,然后开始做前端部分。前端部分分两个版本,一个是PC版的网页,一个是uni-app版的网页,由于uni-app对于SignalR框架的适配性不好,我们暂时先不开发微信小程序及app版的SignalR,等后面技术成熟了在进行开发。

    PC版跟uni-app网页版比较类似,基本上依赖3个js文件就可以搞定了,页面自己看着改改。

    ![](https://img-blog.csdnimg.cn/direct/314528b9c6a5475c93df37dd82274ea8.png)

    一个是js的库,版本应该都ok,不挑。一个是signalR的库,还有一个是做聊天要用的调用方法的库。下面是chat_hub.js的代码

  

// 连接服务,xxxx里直接填后端的地址即可
var connection = $.connection("http://xxxx/chathub");

// 建立链接
connection.start(function () {
//连接成功
console.log("【" + new Date().toLocaleString() + "】连接成功!");
//发送消息
connection.send("给我一杯葡萄酒吧");
});

// 连接断开
connection.disconnected(function () {
console.log("【" + new Date().toLocaleString() + "】连接断开!");
});

// 接收服务器发来的消息
connection.received(function (data) {
console.log("【" + new Date().toLocaleString() + "】接收服务器发来的消息:");
console.log(data);
});

三、uni-app版web网页

    uni-app版的web端跟PC版有些不一样的,因为uni-app不是实时编译运行的,所以PC的模式对uni-app并不奏效,配进去后发现JQuery加载不出来,SignalR的js会报错。

    解决办法有两步,首先在main.js中就将JQuery直接依赖上,并配进window对象中

    其次要对SignalR中所有用到JQuery的地方进行修改,所有对$的使用都要从window对象中获取

好比说这个地方,改之前是上面这样的,改之后是下面这样的

    这么改的原因是因为SignalR的js对于uni-app的项目来说属于外部文件,你在uni-app项目内全局引入的JQuery在外部js中并不生效,但window对象是通用的,所以可以在uni-app项目初始化时把JQuery对象保存在window对象中,使用的时候调window中的$即可。

四、uni-app版微信小程序与App(微信小程序原生同理可用)

    **uni-app版的微信小程序与App版**更不一样了,因为这两个版本不支持jQuery,所以我们要放弃JQuery版本的signalR,改用**websocket来进行连接**。

    整体的逻辑代码没有太大的变动,只在SignalR的连接、发送、断开三处做平台适配即可。

    **首先通过negotiate接口获取到ConnectionToken**,这个token很关键,需要在一个会话周期内保存,后续发送信息、断开连接都跟这个token有关,后端的connectionId就保存在这里面。 ![](https://img-blog.csdnimg.cn/direct/6407cc27535f4893932d4abd17b8f109.png)

    获取到token后通过connect方法进行SignalR连接,需要用到**uni提供的connectSocket方法通过ws协议进行连接**,*****处直接写域名或ip地址即可。uni还提供了需要与websocket连接相关的回调,例如onSocketOpen,onSocketMessage等等,这些可以阅读uni提供的官方文档了解。![](https://img-blog.csdnimg.cn/direct/1386d7c9319043da9f7a78aa83d535bd.png)

    最重要的就是信息的发送与接收了,**发送信息通过uni提供的sendSocketMessage方法**进行处理,这边不像网页版可以直接传对象,需要把对象序列化一下。

    **接收信息就通过onSocketMessage回调来处理**。这边层级比较深,需要取M列表下的第一位。

    最后是断开连接的操作,尝试了一下uni官方提供的closeSocket方法,效果不是很好,后端接收不到OnDisconnected的回调,所以还是用请求的方式进行断开,没什么问题。

    到这基本就全通了,不通你再来问我。

    
标签: uni-app c# .net

本文转载自: https://blog.csdn.net/qq_35638140/article/details/136449493
版权归原作者 真男人不能说做不了 所有, 如有侵权,请联系我们删除。

“后端 C# .net服务器 前端 原生网页、微信小程序、uni-app 集成 SignalR 做 即时通讯”的评论:

还没有评论