• SignalR:基本使用


    SignalR的介绍

    WebSocket

    在传统的HTTP中,只能客户端主动向服务器端发起请求,服务器端无法主动向客户端发送消息。有的业务场景下,我们需要服务器端主动向客户端发送消息,比如Web聊天室、OA系统、站内消息等。

    为了实现服务器端向客户端推送消息,在2008年诞生了WebSocket协议,并且该协议在2011年成为国际标准。目前所有的主流浏览器都已经支持WebSocket协议。WebSocket基于TCP(transmission control protocol,传输控制协议),支持二进制通信,因此通信效率非常高,它可以让服务器处理大量的并发WebSocket连接;WebSocket是双工通信,因此服务器可以高效地向客户端推送消息。

    SignalR

    ASP.NET Core SignalR(以下简称SignalR)是.NET Core平台中对WebSocket的封装,从而让开发人员可以更简单地进行WebSocket开发。

    虽然WebSocket是独立于HTTP的,但是我们一般仍然把WebSocket服务器端部署到Web服务器上,因为我们需要借助HTTP完成初始的握手,并且共享HTTP服务器的端口,这样就可以避免为WebSocket单独打开新的服务器端口。因此,SignalR的服务器端一般运行在ASP.NET Core项目中。

    SignalR中一个重要的组件是集线器(hub),它用于在WebSocket服务器端和所有客户端之间进行数据交换,所有连接到同一个集线器上的程序都可以互相通信。我们既可以通过集线器来完成服务器端向客户端的消息推送,也可以完成客户端之间的消息推送,当然WebSocket也允许客户端向服务器端发送消息。
    image

    使用SignalR的简单聊天室

    下面通过开发一个简单的聊天室来了解SignalR的基本使用。

    第1步:
    创建一个ASP.NET Core Web API项目,安装Nuget包Microsoft.AspNetCore.SignalR.StackExchangeRedis,并且在项目中创建一个继承自Hub类的ChatRoomHub类,所有的客户端和服务器端都通过这个集线器进行通信。

    public class ChatRoomHub : Hub
    {
        public Task SendPublicMessage(string message)
        {
            string connId = this.Context.ConnectionId;
            string msg = $"{connId}{DateTime.Now}:{message}";
            return Clients.All.SendAsync("ReceivePublicMessage", msg);
        }
    }
    

    ChatRoomHub类中定义的方法可以被客户端调用,也就是客户端可以向服务器端发送请求,方法的参数就是客户端向服务器端传送的消息,参数的个数原则上来讲不受限制,而且参数的类型支持string、bool、int等常用的数据类型。
    在ChatRoomHub类中,我们定义了一个方法SendPublicMessage,方法的参数message为客户端传递过来的消息。在第5行代码中,我们获得了当前发送消息的客户端连接的唯一标识ConnectionId;在第6行代码中拼接出一个包含连接ID、当前时间、客户端消息的字符串;随后我们把msg字符串以名字为“ReceivePublicMessage”的消息发送到所有连接到集线器的客户端上。

    第2步:
    编辑Program.cs,在builder.Build之前调用builder.Services.AddSignalR注册所有SignalR的服务,在app.MapControllers之前调用app.MapHub<ChatRoomHub>("/Hubs/ChatRoomHub")启用SignalR中间件,并且设置当客户端通过SignalR请求“/Hubs/ChatRoomHub”这个路径的时候,由ChatRoomHub进行处理。

    builder.Services.AddSignalR(); //注册所有SignalR的服务
    string[] urls = new[] { "http://localhost:3000" };
    builder.Services.AddCors(options =>
        options.AddDefaultPolicy(builder => 
            builder.WithOrigins(urls).AllowAnyMethod()
                .AllowAnyHeader().AllowCredentials())
    );
    var app = builder.Build();
    
    // Configure the HTTP request pipeline.
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }
    app.UseCors();
    app.UseHttpsRedirection();
    app.UseAuthentication();
    app.UseAuthorization();
    app.MapHub<ChatRoomHub>("/Hubs/ChatRoomHub"); //启用SignalR中间件
    app.MapControllers();
    

    第3步:
    我们需要编写一个静态HTML页面提供交互界面。按照前后端分离的理念,我们应该把HTML页面放到一个单独的前端项目中。创建一个前端项目,然后执行如下命令安装SignalR的JavaScript客户端SDK(software development kit,软件开发工具包):npm install @microsoft/signalr。

    在SignalR的JavaScript客户端中:

    1. 使用HubConnectionBuilder来创建从客户端到服务器端的连接;
    2. 通过withUrl方法来设置服务器端集线器的地址,该地址必须是包含域名等的全路径,必须和在服务器端MapHub设置的路径一致;
    3. 通过withAutomaticReconnect设置自动重连机制。虽然withAutomaticReconnect不是必须设置的,但是设置这个选项之后,如果连接被断开,客户端就会尝试重连,因此使用起来更方便。需要注意的是,客户端重连之后,由于这是一个新的连接,因此在服务器端获得的ConnectionId是一个新的值。
    4. 对HubConnectionBuilder设置完成后,我们调用build就可以构建完成一个客户端到集线器的连接。
    5. 我们通过build获得的到集线器的连接只是逻辑上的连接,还需要调用start方法来实际启动连接。
    6. 一旦连接建立完成,我们就可以通过连接对象的invoke函数来调用集线器中的方法,我们也可以通过on函数来注册监听服务器端使用SendAsync发送的消息的代码。
    <template>
      <input
        type="text"
        v-model="state.userMessage"
        v-on:keypress="txtMsgOnkeypress"
        />
        <div>
          <ul>
            <li v-for="(msg, index) in state.messages" :key="index">{{ msg }}</li>
          </ul>
        </div>
      </template>
    
    
    <script>
      import { reactive, onMounted } from "vue";
      import * as signalR from "@microsoft/signalr";
      let connection;
      export default {
        name: "Login",
        setup() {
          const state = reactive({ userMessage: "", messages: [] });
          const txtMsgOnkeypress = async function (e) {
            if (e.keyCode != 13) return;
            await connection.invoke("SendPublicMessage", state.userMessage);
            state.userMessage = "";
          };
          onMounted(async function () {
            connection = new signalR.HubConnectionBuilder() // 创建从客户端到服务器端的连接
              .withUrl("https://localhost:7002/Hubs/ChatRoomHub") // 设置服务器端集线器的地址
              .withAutomaticReconnect() // 设置自动重连机制
              .build(); // 构建完成
            await connection.start(); // 启动
            // 通过on函数来注册监听服务器端使用SendAsync发送的消息的代码
            connection.on("ReceivePublicMessage", (msg) => {
              state.messages.push(msg);
            });
          });
          return { state, txtMsgOnkeypress };
        },
      };
    </script>
    

    可以看到,在onMounted方法中,我们创建并且启动了客户端到服务器端集线器的连接,并且监听了服务器端向客户端发送的“ReceivePublicMessage”消息,客户端还会把收到的消息添加到页面上。

    在23到27行中,对用户在输入框内的按键进行监听,当用户按Enter键的时候,我们就调用集线器中的SendPublicMessage方法把用户输入的消息发送给服务器端,服务器端再把消息转发给连接到这个集线器的全部客户端。这样我们就实现了一个简单的聊天室。

    最后:
    启动ASP.NET Core项目和前端项目,然后打开两个聊天室页面,并分别在两个页面中发送一些消息。我们可以发现,在A页面中发送的消息,在B页面中能立即看到;在B页面中发送的消息,在A页面中也能立即看到。

    本文学习参考自:ASP.NET Core技术内幕与项目实战

  • 相关阅读:
    CF1305 Ozon Tech Challenge 2020 游戏存档
    CF1310A Recommendations 题解
    CF755G PolandBall and Many Other Balls 题解
    关于后缀自动机
    具体数学学习笔记
    Flask-SQLAlchemy中解决1366报错
    常用的SQLalchemy 字段类型
    flask_model防止循环引用
    navicate远程访问ubuntu上的mysql数据库
    flask运行环境搭建(nginx+gunicorn)
  • 原文地址:https://www.cnblogs.com/nullcodeworld/p/16736072.html
Copyright © 2020-2023  润新知