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

计算机网络 --- 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);
        }
    }
}
  • 在上述代码中,我们首先在 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()
  • 在这个例子中,我们使用 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);
    }
}
  • 28
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值