• 计算机网络 --- WebSocket协议 和 Signalr


    什么是WebSocket

    • HTTP是基于TCP协议的,同一时间里,客户端和服务器只能有一方主动发数据,是半双工通信。
    • 通常,打开某个网页,我们每点击一次网页上的某个选项,前端就会发送一次HTTP请求,网站返回一次HTTP响应。这种由客户端主动请求,服务器响应的方式满足大部分网页的功能场景。但这种情况下,服务器不会主动给客户端发消息。而类似网页游戏这样的场景,是需要客户端和服务器之间互相主动发大量数据的。
    • 因此,我们需要一个基于TCP的新协议,即新的应用层协议WebSocket。
    • 它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种

    在这里插入图片描述

    • 其他特点包括:
    • 建立在 TCP 协议之上,服务器端的实现比较容易。
    • 与 HTTP 协议有着良好的兼容性。默认端口也是80和
    • 并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
    • 数据格式比较轻量,性能开销小,通信高效。
    • 可以发送文本,也可以发送二进制数据。
    • 没有同源限制,客户端可以与任意服务器通信。
    • 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL
      在这里插入图片描述
    • 使用WebSocket注意事项
    • 资源消耗:WebSocket 连接是持久的,这意味着服务器端需要分配和管理每个连接的资源。如果同时有大量连接存在,可能会对服务器的性能和资源消耗造成压力
    • 安全性考虑:由于 WebSocket 允许服务器端向客户端发送实时数据,因此存在安全风险。如果不正确地实施安全措施,可能会导致跨站点脚本攻击(XSS)和其他安全漏洞
    • 连接状态维护:与传统的 HTTP 请求不同,WebSocket 建立了一个持久的连接,需要维护连接状态。这意味着服务器端需要消耗额外的资源来管理连接、保持连接状态和处理连接断开等情况
    • 防火墙和代理限制:某些网络环境中的防火墙或代理服务器可能会阻止 WebSocket 连接。这可能导致在特定网络配置下,无法建立或保持 WebSocket 连接,从而影响实时通信功能的可用性

    什么是Signalr

    • 因为WebSocket是无状态的,如果因为网络问题中断的话, 则重连之后, 服务器会丢失所有客户端的信息. 所以使用原生WebSocket的话, 需要自己在服务器端实现状态管理.
    • SignalR 是一个开源的 Microsoft .NET 库,用于实现实时双向通信功能。它简化了在 Web 应用程序中实现实时功能的开发过程,允许服务器端代码能够主动推送数据到客户端,并支持客户端与服务器之间的实时双向通信。
    • SignalR 支持多种传输协议
    • WebSocket
    • Server-Sent Events(SSE)
    • 长轮询
    • Forever Frame
    • Signalr对于每个链接提供了一个connectionId, 这样可以服务器端可以通过connectionId识别出来客户端, 实现状态管理

    SignalR 的主要特点包括:

    • 实时双向通信:SignalR 允许服务器端向客户端实时推送数据,同时也支持客户端向服务器端发送消息,实现双向通信。
    • 广播和组管理:SignalR 提供了广播功能,可以将消息同时发送给多个客户端。它还支持将客户端分组,可以根据需要将消息发送给特定的客户端组。
    • 自动重连和状态管理:SignalR 具有自动重连功能,如果连接中断,它会尝试重新建立连接。同时,它还能够管理客户端和服务器之间的连接状态,包括连接建立、连接断开和错误处理等。
    • 扩展性和可定制性:SignalR 可以在不同的扩展点进行定制,开发人员可以根据需要修改消息传输、身份验证和授权等方面的行为。
    • 总体而言,SignalR 提供了一种简单且强大的方式来实现实时双向通信功能,并且能够与 ASP.NET、ASP.NET Core 和其他 .NET 平台无缝集成。

    Signalr Example – SimpleChat

    服务器端

    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace SignalRServer
    {
        public class Startup
        {
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddSignalR();
            }
    
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseRouting();
    //将Hub和Http uri进行绑定
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapHub<ChatHub>("/chathub");
                });
            }
        }
    
        public class ChatHub : Microsoft.AspNetCore.SignalR.Hub
        {
            public async Task Broadcast(string message)
            {
            //"ReceiveMessage" 是客户端注册的接收消息的回调函数的名字
                await Clients.All.SendAsync("ReceiveMessage", message);
            }
    		public async Task SendMessage(string message)
            {
                var connectionId = Context.ConnectionId;
                await Clients.All.SendAsync("ReceiveMessage", connectionId, message);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 在上述代码中,我们首先在 ConfigureServices 方法中添加了 SignalR 服务。然后,在 Configure 方法中注册了 ChatHub,并将其映射到 /chathub 路由。
    • ChatHub 是一个继承自 Microsoft.AspNetCore.SignalR.Hub 的类,它包含了一个名为 SendMessage 的方法,用于接收客户端发送的消息,并将消息通过 Clients.All.SendAsync 发送给所有连接的客户端。

    客户端

    from signalrcore.hub_connection_builder import HubConnectionBuilder
    
    # 创建 SignalR 连接
    connection = HubConnectionBuilder() \
        .with_url("http://example.com/chathub") \
        .build()
    
    # 定义收到消息时的回调函数
    def on_message(message):
        print("Received message:", message)
    
    # 注册消息回调函数
    connection.on("ReceiveMessage", on_message)
    
    # 启动连接
    connection.start()
    
    # 发送消息, SendMessage 是服务器端Hub里定义的接受消息的方法名
    while True:
        message = input("Enter a message to send: ")
        connection.send("SendMessage", message)
    
    # 断开连接
    connection.stop()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 在这个例子中,我们使用 signalrcore 库创建了一个 SignalR 连接,并指定了服务器的 URL(http://example.com/chathub)。然后,我们定义了一个回调函数 on_message,用于处理接收到的消息。接下来,我们通过调用 connection.on() 注册回调函数,以便在收到消息时触发回调函数。
    • 然后,我们通过调用 connection.start() 启动连接。在连接启动后,我们进入一个循环,等待用户输入要发送的消息,并通过调用 connection.send() 发送消息到服务器.

    注意

    • Hub 类是Transient的, 也就是用完一次就会被销毁
    • 不要将状态存储在 Hub 类的属性中。每次调用 Hub 方法时,都会在一个新的 Hub 实例上执行。
    • 不要直接通过依赖注入实例化一个 Hub。可以使用 IHubContext 来从应用程序的其他位置向客户端发送消息。
    • 在调用依赖于 Hub 保持活动状态的异步方法时,请使用 await。例如,如果在没有使用 await 的情况下调用类似 Clients.All.SendAsync(…) 的方法,并且在 SendAsync 完成之前 Hub 方法已经完成,那么该方法可能会失败.

    使用 IHubContext 从应用程序的其他位置向客户端发送消息

    public class NotificationService
    {
        private readonly IHubContext<ChatHub> _hubContext;
    
        public NotificationService(IHubContext<ChatHub> hubContext)
        {
            _hubContext = hubContext;
        }
    
        public async Task SendNotificationToAllClients(string message)
        {
            await _hubContext.Clients.All.SendAsync("ReceiveNotification", message);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
  • 相关阅读:
    MyBatis的Mapper文件的foreach标签详解
    一零二四、pyspark在jupyter中的完美运行
    启动 Tomcat 日志乱码问题
    《视觉 SLAM 十四讲》V2 第 9 讲 后端优化1 【扩展卡尔曼滤波器 EKF && BA+非线性优化(Ceres、g2o)】
    Oralce笔记-解决Oracle18c中ORA-28001: 口令已经失效
    成功解决 java.lang.NumberFormatException
    【教3妹学编程-算法题】高访问员工
    【测试】最火的测试开发学习路线内容再次大更新,助力通关大厂测开
    数据湖浅析(以hudi为例)
    邮箱发送验证码(nodemailer)
  • 原文地址:https://blog.csdn.net/weixin_38803409/article/details/138095571