欢迎光临散文网 会员登陆 & 注册

实际项目讲解 Signalr(.net core3.x)使用

2020-02-06 16:22 作者:Tuple_元组  | 我要投稿

最近认真研究了一个微软最新的.net core3.x版本中的signalr技术,并使用之开发了一个相对稳定的聊天系统。在此作一些记录,希望能给需要的同志作一些有益的提醒。

一、系统功能界面


聊天系统界面,类似微信显示方式

、主要功能代码

1.用户登陆(Cookie方式)

   public IActionResult OnPost(string InputTime, string UserId, string Password)
        {

            if (!string.IsNullOrWhiteSpace(UserId) && !string.IsNullOrWhiteSpace(Password))

            {

                string tKey = (DateTime.Parse(InputTime).Second - 2).ToString();

                ModelUser user = GlobalVars.AllChatUsers.Where(e => e.Id.Equals(UserId) && (e.Pwd + tKey).Equals(Password)).FirstOrDefault();

                if (user != null)

                {

                    Claim[] Claims = new Claim[] { new Claim(ClaimTypes.Name, user.Id) };

                    ClaimsIdentity ClaimsIdentity = new ClaimsIdentity(Claims, CookieAuthenticationDefaults.AuthenticationScheme);

                    ClaimsPrincipal LoginUser = new ClaimsPrincipal(ClaimsIdentity);

                    Task.Run(async () =>

                    {

                        //登录用户

                        await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, LoginUser);

                     }).Wait();

                   return RedirectToPage("./WebChat", new { Login = "First" });

                }

            }

           //"用户名或密码错误!";

            return Page();
        }

2.客户端连接服务器代码

function ConnectionStart() {

    if (g_ReconnectCount >= g_ReconnectMaxTimes) {

        AddMsgToChatList.AddMessage('<span style="color:red">已重连 ' + g_ReconnectCount.toString() + ' 次,仍无法连接。请检查网络,然后刷新重试!</span>');

        return;

    }

    connection.start().then(function () {

        g_divInputMsg.setInputState(true);

        //connection.state === signalR.HubConnectionState.Connected

        //AddMsgToChatList.AddMessage('<span style="color:red">已连接到服务器</span>');

    }).catch(function (err) {

        //connection.state === signalR.HubConnectionState.Disconnecte

        //AddMsgToChatList.AddMessage('<span style="color:red">连接已断开,正在重连....(' + g_ReconnectCount.toString() + ')</span>');

        g_ReconnectCount++;

        setTimeout(() => ConnectionStart(), 500);

    });

}

//首次连接服务器。

ConnectionStart();

3. 客户端发信息到服务器(使用流传输方式,带发送进度显示,以防止客户端发大图时会卡死或无法发送的现象)

客户端主要代码(Javascript):

async function sendMsgToSever(willSendMsg, processElement) {

    processElement.innerHTML = '已发送(0/' + willSendMsg.length + ')';

    //客户端到服务器的流式处理

    var startIndex = 0;

    var stopIndex = 0;

    var fixLength = 1024 * 20; //此大小影响数据发送速度。4096=8407,5120=6714,10240=3348,20480=1882

    var msgSubStr = '';

    //var beginTime = (new Date()).valueOf();

    console.log(new Date().toDateString)

    const subject = new signalR.Subject();

    connection.send("UploadStream", subject);

    if (willSendMsg.length <= fixLength) { stopIndex = willSendMsg.length; } else { stopIndex = fixLength; }

    var sendFun = function () {

        if (stopIndex <= willSendMsg.length) {

            msgSubStr = willSendMsg.substring(startIndex, stopIndex);

            subject.next(msgSubStr);

            processElement.innerHTML = '已发送(' + stopIndex.toString() + '/' + willSendMsg.length + ')';   //显示发送到服务器的进度

            startIndex = stopIndex;

            if (willSendMsg.length === stopIndex) {

                subject.complete();

                //var endTime = (new Date()).valueOf();

                //console.log(fixLength.toString() + '=' + (endTime - beginTime).toString());

                return;

            }

            if (willSendMsg.length >= (stopIndex + fixLength)) { stopIndex += fixLength; } else { stopIndex += willSendMsg.length - stopIndex; }

        }

        window.setTimeout(sendFun, 0);

    }

    sendFun();

}

服务器端主要代码(C#):

 public async Task UploadStream(IAsyncEnumerable<string> stream)

        {

            //接收客户端发来的信息

            System.Text.StringBuilder ClientNewMsg = new System.Text.StringBuilder();

            await foreach (var item in stream)

            {

                ClientNewMsg.Append(item);

            }

            string message = ClientNewMsg.ToString();

            //发给指定用户

            await Clients.Client(RUser.ConnectionId).SendAsync("ReceiveMessage", message);

        }

4.服务器端推送信息到客户端

客户端主要代码(Javascript):

connection.on("ReceiveMessage", async function (MsgObj) {

    webChatLock.Reset();//锁屏重计时

    if (g_PlaySound) {

        var SoundFile = "/images/dingdong.wav";

        var SoundObj = new Audio(SoundFile);

        SoundObj.play();

    }

    var MsgJson = JSON.parse(MsgObj);

    //....进一步处理收到的数据

    }

服务器端主要代码(C#):

    await Clients.Client(RUser.ConnectionId).SendAsync("ReceiveMessage", message);

5.需要注意的几个问题。

  • js读取图片内容生成DataBase64 格式数据时,<input type="file"/> 控件不能仅使用声明,而必须添加到页面上,否则苹果手机 safari 浏览器可以打开文件选择框,但将无应响应控件的 change事件

  • 更新页面数据显示进度时,需使用 window.setTimeout(funname,0),在funname()中发送并更新页度进度值,否则将看不到数据变化。

  • 在处理用户的离线消息时,应使用线程安全的ConcurrentDictionary<string, ModeMsgServer> NeedSendMsgs 等类。

  • 在收到或发送信息时,让聊天窗口自动滚动到最底部,使用 DivChatElement.scrollIntoView(false);代码,需注意的是,信息中有图片,则上述代码应在图片的 load 事件中执行。

  • 采用客户端收到服务器信息后立即回调signalr的Hub中指定方法的方式,以便使服务器得以确认客户端已收到信息,从而将已发送的信息删除(否则将适时重新发送或在下次该用户登陆时再发送,以确保用户收到该信息)。

  • 关于客户端与服务器端“心跳”配置。(注意客户端与服务器端的时间要相匹配)

服务器端(Startup.cs):   

services.AddSignalR(options =>

            {

                //客户端发保持连接请求到服务端最长间隔,默认30秒

                options.ClientTimeoutInterval = TimeSpan.FromSeconds(30);

                //服务端发保持连接请求到客户端间隔,默认15秒

                options.KeepAliveInterval = TimeSpan.FromSeconds(15);

                options.EnableDetailedErrors = true;

                options.MaximumReceiveMessageSize = 1024 * 1024 * 1024;

            });

客户端(js):

//设置连接对象的相关属性

connection.serverTimeoutInMilliseconds = 30e3;  //等待服务器端发送过来的心跳包最长等待时间30s(如该时间段时收到不服务器发送的“心跳”数据,即认为链接丢失)

connection.keepAliveIntervalInMilliseconds = 15e3; //客户端向服务器发送心跳包频率15s



实际项目讲解 Signalr(.net core3.x)使用的评论 (共 条)

分享到微博请遵守国家法律