• Asp-Net-Core学习笔记:3.使用SignalR实时通信框架开发聊天室


    SignalR牛刀小试

    在MVP杨老师的博客里看到这么个东西,我还以为是NetCore3才推出的新玩意,原来是已经有很多年的历史了,那看来还是比较成熟的一个技术了。

    简介#

    SignalR是一个.NET Core/.NET Framework的开源实时框架,SignalR的可使用Web Socket, Server Sent Events 和 Long Polling作为底层传输方式。

    SignalR基于这三种技术构建,抽象于它们之上,它让你更好的关注业务问题而不是底层传输技术问题。

    以上介绍来自博客,SignalR分为客户端和服务端,服务端运行在AspNetCore上,客户端支持前端、桌面端和移动端,我去查了一下,连Flutter也有相应的支持库。

    几个概念#

    回落机制#

    SignalR支持三种底层传输技术,根据客户端的兼容性可以自动协商传输类型。如图

    image

    Web Socket是最好的最有效的传输方式,如果浏览器或Web服务器不支持它的话,就会降级使用SSE,实在不行就用Long Polling。

    RPC#

    不用解释太多,Remote Procedure Call,SignalR采用RPC范式来进行服务端和客户端之间的通信。

    Hub#

    Hub是SignalR的一个组件,运行在服务端,它是一个通信用的组件, Hub使用RPC接受从客户端发来的消息,也能把消息发送给客户端 。

    image

    关于横向扩展#

    SignalR针对多种底层通信方式有了 Sticky Sessions (粘性会话) 这种解决方案,可以保证一个客户端在一次会话中的请求都分配给同一个服务器,具体以后需要的时候再研究。

    开始使用#

    我要做的是一个聊天室,所以只需要写一个简单的服务就可以了。

    首先要创建AspNetCore项目,注册SignalR服务:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddSignalR();
    }
    

    创建ChatService类:

    public class ChatService
    {
        private readonly List _messages;
        private readonly IHubContext _context;
        public List Messages { get => _messages; }
        public ChatService(IHubContext context)
        {
            _context = context;
            _messages = new List();
        }
    }
    

    消息实体类:

    public class ChatMessage
    {
        public string UserName { get; set; }
        public string Content { get; set; }
        public DateTime SendedTime { get; set; }
        public string ClientName { get; set; }
    }
    

    关键的来了,编写Hub:

    使用SendAsync可以进行远程调用

    public class ChatHub : Hub {
        private readonly ChatService _chatService;
        public ChatHub(ChatService chatService) {
            _chatService = chatService;
        }
        public async Task GetMessages(string connectionId) {
            var data = _chatService.Messages;
            await Clients.Client(connectionId).
                SendAsync("GetMessages", data);
        }
        public async Task SendMessage(string userName, string content, string clientName) {
            var msg = new Models.ChatMessage {
                UserName = userName,
                Content = content,
                SendedTime = DateTime.Now,
                ClientName = clientName
            };
            _chatService.Messages.Add(msg);
            await Clients.All.SendAsync("SendMessage", msg);
        }
        public override Task OnConnectedAsync() {
            GetMessages(Context.ConnectionId);
            return base.OnConnectedAsync();
        }
    }
    

    注册依赖注入和中间件#

    依赖注入

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddSignalR();
        services.AddSingleton();
        services.AddSingleton();
    }
    

    中间件

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseStaticFiles();
        app.UseRouting();
        app.UseEndpoints(endpoints =>{
            endpoints.MapControllers();
            endpoints.MapHub("/chat");
        });
    }
    

    客户端#

    为了方便我就用网页来做。

    首先写个简单的聊天界面布局,这里就不贴代码了没啥技术含量,效果大概这样:

    image

    首先要安装signalr.js,通过npm安装即可。

    然后需要编写js:

    function setupConnection() {
        connection = new signalR.HubConnectionBuilder()
            .withUrl("/chat")
            .build();
    
        connection.on("SendMessage", message => {
            console.log(message);
            addMsg(message.userName, message.sendedTimeStr, message.content, message.clientName);
        });
    
        connection.on("GetMessages", data => {
            console.log(data);
            for (var i = 0; i < data.length; i++) {
                let message = data[i];
                addMsg(message.userName, message.sendedTimeStr, message.content, message.clientName);
            }
        });
    
        connection.on("Finished", () => {
            connection.stop();
            console.log("finished.")
        });
    
        connection.start()
            .catch(err => console.error(err.toString()));
    }
    

    通过以下代码可以远程调用服务器的方法:

    connection.invoke("SendMessage", username, content, '网页客户端');
    

    然后就可以打开多个浏览器测试了。
    这是我在手机上的截图

    PS:为了把这个部署到服务器,我还买了个新的阿里云服务器。。

    参考资料#

    欢迎交流#

    交流问题请在微信公众号后台留言,每一条信息我都会回复哈~

  • 相关阅读:
    CockroachDB-备份与恢复(6)RESTORE命令
    C++ 【2】
    数学术语之源——平凡(trivial)与非平凡(nontrivial)
    蓝桥杯双周赛算法心得——串门(双链表数组+双dfs)
    OLED根据数据手册显示一条直线
    C语言基础篇 —— 4.1 管理内存:栈(stack)、堆(heap)、数据区(.data)
    全网最全Django面试题整理(二)
    Shell脚本数组简介及运用
    RESTful 风格是指什么
    Docker+nginx部署Springboot+vue前后端分离项目(Linux版)
  • 原文地址:https://www.cnblogs.com/deali/p/18011837