后端 C# .net服务器 前端 原生网页、微信小程序、uni-app 集成 SignalR 做 即时通讯
创始人
2025-01-17 21:33:02
0

一、 后端服务器

        后端集成SignalR比较简单,首先要在解决方案中依赖几个SignalR的库,SignalR的库就是做即时通讯的主要库,我们建立连接、收发信息都需要用这个库来进行。

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

        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();

        ///


        /// 连接成功后调用
        ///

        ///
        ///
        ///
        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);
        }

        ///


        /// 接收到请求的时候调用
        ///

        ///
        ///
        ///
        ///
        protected override async Task OnReceived(IRequest request, string connectionId, string data)
        {
            var keys = new Dictionary
            {
                { "葡萄酒", "wine" },
                { "红酒", "wine" },
                { "白酒", "wine" },
                { "霞多丽", "grapery" },
                { "雷司令", "grapery" }
            };

            var values = new Dictionary
            {
                { "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);
        }

        ///


        /// 连接中断的时候调用
        ///

        ///
        ///
        ///
        ///
        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);
        }

        ///


        /// 连接超时重新连接的时候调用
        ///

        ///
        ///
        ///
        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 SignalRConns { get; set; } = new List();

        ///


        /// 添加连接
        ///

        ///
        public void AddConnInfo(SignalRConn conn)
        {
            SignalRConns.Add(conn);
        }

        ///


        /// 删除连接
        ///

        ///
        public void DelConnInfo(string connid)
        {
            var signalRConns = SignalRConns.FirstOrDefault(u => u.ConnectionId == connid);
            if (signalRConns != null)
            {
                SignalRConns.Remove(signalRConns);
            }
        }

        ///


        /// 更新链接(老的链接不起作用了)
        /// 场景:客户端重连了,userid没变,但是connid变了
        ///

        /// 用户id
        /// 新的链接id
        public void UpdateConnInfo(string userId, string newConnsId)
        {
            var signalRConns = SignalRConns.FirstOrDefault(u => u.UserId.ToLower() == userId.ToLower());
            if (signalRConns != null)
            {
                signalRConns.ConnectionId = newConnsId;
            }
        }

        ///


        /// 判断用户是否已经链接
        ///

        /// 连接id
        /// 用户id
        ///
        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; }

        ///


        /// SignleR链接Id(每次链接SignalR都会分配一个id)
        ///

        public string ConnectionId { get; set; }
    }
}

二、 PC版原生网页

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

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

        

        一个是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就保存在这里面。 

        获取到token后通过connect方法进行SignalR连接,需要用到uni提供的connectSocket方法通过ws协议进行连接,*****处直接写域名或ip地址即可。uni还提供了需要与websocket连接相关的回调,例如onSocketOpen,onSocketMessage等等,这些可以阅读uni提供的官方文档了解。

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

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

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

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

        

相关内容

热门资讯

专业讨论!德扑之星真破解套路(... 专业讨论!德扑之星真破解套路(辅助挂)软件透明挂(有挂了解)-哔哩哔哩;人气非常高,ai更新快且高清...
每日必看!智星德州菠萝外挂检测... 每日必看!智星德州菠萝外挂检测(辅助挂)软件透明挂(有挂教学)-哔哩哔哩1、玩家可以在智星德州菠萝外...
透视透明挂!轰趴十三水有后台(... 轰趴十三水有后台赢率提升策略‌;透视透明挂!轰趴十三水有后台(辅助挂)软件透明挂(有挂详情)-哔哩哔...
发现玩家!德扑ai助手软件(辅... 发现玩家!德扑ai助手软件(辅助挂)透视辅助(有挂教学)-哔哩哔哩;玩家在德扑ai助手软件中需先进行...
一分钟了解!x-poker辅助... 一分钟了解!x-poker辅助软件(辅助挂)辅助透视(有挂攻略)-哔哩哔哩1、每一步都需要思考,不同...
一分钟揭秘!德州最新辅助器(辅... 一分钟揭秘!德州最新辅助器(辅助挂)透视辅助(有挂攻略)-哔哩哔哩;德州最新辅助器最新版本免费下载安...
玩家攻略推荐!德州辅助(辅助挂... 玩家攻略推荐!德州辅助(辅助挂)辅助透视(有挂了解)-哔哩哔哩是由北京得德州辅助黑科技有限公司精心研...
揭秘真相!pokernow德州... 《揭秘真相!pokernow德州(辅助挂)辅助透视(有挂介绍)-哔哩哔哩》 pokernow德州软件...
五分钟了解!德州之星辅助器(辅... 五分钟了解!德州之星辅助器(辅助挂)辅助透视(有挂透明)-哔哩哔哩1、很好的工具软件,可以解锁游戏的...
推荐一款!pokermaste... 1、推荐一款!pokermaster有外挂(辅助挂)透视辅助(有挂教学)-哔哩哔哩;详细教程。2、p...