ASP.NET Core SignalR 双工通信

01. 介绍 🎯

ASP.NET Core SignalR 是一个开放源代码库,它简化了向应用添加实时 Web 功能的过程。 实时 Web 功能使服务器端代码可以在服务器上激发事件时将事件推送到连接的客户端。 使用 SignalR,客户端也可以将消息发送到服务器,从而实现双工通信。

✨示例方案✨

假设你是开发团队的一员,你需要更新现有网站的实时应用功能。 实时地图是下单系统的一部分,它让 Contoso Pizza 的顾客可以跟踪订单进度。

当前的解决方案是通过客户端轮询订单状态更改更新来实现的。 这种方法不是很理想,因为可能会有大量请求涌入服务器。 Contoso Pizza 一直收到订单无法处理的报告,导致销售额和潜在回头客的流失。

日志中报告的故障发生在促销日的高峰时间。 如果有太多顾客同时查看实时订单地图,则服务器会收到大量请求,进程将失败。 在本模块中,你将使用 SignalR 来解决这些问题。

02. 什么是 ASP.NET Core SignalR?

所有连接了 Internet 的应用程序都由服务器和客户端组成。 客户端依赖于服务器获取数据,而它们获取数据的主要机制是通过发出超文本传输协议 (HTTP) 请求来进行的。 某些客户端应用程序需要经常更改的数据。

ASP.NET Core SignalR 提供了一个 API,用于创建服务器到客户端远程过程调用 (RPC)。 RPC 从服务器端 .NET Core 代码中调用客户端上的函数。 支持多个平台,每个平台都有各自的客户端 SDK。 因此,RPC 调用所调用的编程语言可能会有所不同。

2.1 传输 🕸️

SignalR 支持以下方法(或传输)来处理实时通信:

  1. 🥇WebSockets 
  2. 🥈Server-Sent Events 
  3. 🥉长轮询

上面列出的传输的顺序表示其正常回退顺序。 换句话说,Websocket 优于 Server-Sent Events,而 Server-Sent Events 优于 Long Polling,但是这三种传输方式皆可用。 SignalR 会自动在服务器和客户端的能力范围内选择最佳的传输方法。 有关详细信息,请参阅 SignalR 传输协议的正式规范。

2.2 服务器 🖥️

服务器负责公开 SignalR 终结点。 终结点映射到 Hub 或 Hub<T> 子类。 服务器可存在于本地、云提供商(如 Azure)或 Azure SignalR 服务中。 服务器公开可以从客户端调用的中心方法以及客户端可以订阅的事件。 它们被视为远程过程。

2.2.1 集线器 🧶

在 SignalR 中, hub 用于在客户端和服务器之间进行通信。 中心是一种高级管道,允许客户端和服务器相互调用方法。 为此,SignalR 会自动跨计算机边界处理调度。 你可以将中心看作是所有连接的客户端和服务器之间的代理。

2.2.2 协议 📧

SignalR 协议是通过任意 基于消息的传输实现双向 RPC 的协议。 连接中的任何一方都可以对另一方调用程序,并且程序可以返回零个或多个结果或者返回错误。 SignalR 提供了两个内置的 hub  协议:

  • 基于 JSON 的文本协议,这是默认值。
  • 基于 MessagePack 的二进制协议,与 JSON 相比, MessagePack 通常会创建较小的消息。

若要使用 MessagePack 协议,服务器和客户端都需要选择加入以对其进行配置,并且服务器和客户端都必须支持它。 还有第三个中心协议,名为 BlazorPack ,但它仅用于 Blazor-Server 应用程序。 在没有 Blazor-Server 托管模型的情况下,无法使用该协议。 有关详细信息,请参阅 SignalR Hub 协议的官方规范。

2.2.3 用户 🤵‍♂️

系统中的用户是一个个体,但也可以是组的一部分。 消息可以发送到组,所有组成员都会收到通知。 单个用户可以从多个客户端应用程序进行连接。 例如,同一用户可以使用一个移动设备和一个 Web 浏览器,同时在这两个上获取实时更新。

2.2.4 组 👨‍👩‍👧‍👦

一个组包含一个或多个连接。 服务器可以创建组,将连接添加到组,以及从组中删除连接。 组具有指定的名称,该名称充当其唯一标识符。 组充当范围界定机制来帮助定位消息。 也就是说,实时功能只能发送给已命名组中的用户。

2.2.5 连接 🔗

与中心的连接由唯一标识符表示,该标识符只有服务器和客户端知道。 每个 hub 类型都存在单个连接。 每个客户端都有一个到服务器的唯一连接。 也就是说,单个用户可以在多个客户端上表示,但每个客户端连接都有各自的标识符。

2.3 客户端 💻

客户端负责通过 HubConnection 对象建立到服务器终结点的连接。 Hub 连接在每个目标平台中表示:

  • .NET 客户端:Microsoft.AspNetCore.SignalR.Client.HubConnection
  • JavaScript 客户端:@microsoft/signalr.HubConnection
  • Java 客户端:com.microsoft.signalr.HubConnection

有关详细信息,请参阅 ASP.NET Core SignalR 支持的平台。

当中心连接实例成功启动后,消息可以自由地双向流动。 用户可以自由地将通知发送到服务器,以及从服务器接收通知。 客户端是任何已连接的应用程序,例如 Web 浏览器、移动应用或桌面应用等。

03. ASP.NET Core SignalR 的工作原理

3.1 服务器和 Hub 类

Hub类是 SignalR 服务器的概念。 它在 Microsoft.AspNetCore.SignalR 命名空间中定义,并且是 Microsoft.AspNetCore.SignalR NuGet 包的一部分。 面向 Microsoft.NET.Sdk.Web SDK 的 ASP.NET Core Web 应用无需添加 SignalR 的包引用,因为它已作为共享框架的一部分提供。

Hub 通过路由公开。 例如,https://www.contoso-pizza.com/hubs/orders 路由可用于表示 OrdersHub 实现。 通过各种中心 API,作者可以定义方法和事件。

有两个方式可以在中心上公开方法。 创建以下类型的子类和编写方法:

  • Hub:标准中心。
  • Hub<T>:强类型泛型中心。

3.1.1 示例 Hub

作为参考点,请考虑以下 Notification 对象:

namespace RealTime.Models;

public record Notification(string Text, DateTime Date);

可以在使用 .NET 客户端 SDK 时共享该对象,使服务器和客户端具有完全相同的对象。 假设有一个如下所述的通知中心:

using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleServer.Hubs;

public sealed class NotificationHub : Hub
{
    public Task NotifyAll(Notification notification) =>
        Clients.All.SendAsync("NotificationReceived", notification);
}

对于方法与事件之间的差异,上述中心实施中的方法是 NotifyAll,而事件是 NotificationReceived NotificationHub 必须是 Hub 的子类。 NotifyAll 方法返回 Task,并接受单个 Notification 参数。 该方法表示为从 Clients.All 调用 SendAsyncClients.All 表示所有连接的客户端。 将激发 NotificationReceived 事件,依赖于 notification 实例。

3.1.2 IHubContext 实例

 Hub  IHubContext 实例激发事件。 SignalR 中心是用于将消息发送到连接到 SignalR 服务器的客户端的核心抽象。 还可以使用以下任一类型从应用中的其他位置发送消息:

  • IHubContext<THub>:一个上下文,其中 THub 表示标准中心。
  • IHubContext<THub,T>:一个上下文,其中 THub 表示强类型泛型中心,T 表示相应类型的客户端。

💥重要:

IHubContext 用于向客户端发送通知。 它不用于调用 Hub 上的方法。

IHubContext 示例

考虑到上一个通知中心实施,你可以使用 IHubContext<NotificationHub>,如下所示:

using Microsoft.AspNetCore.SignalR;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleServer.Services;

public sealed class NotificationService(
    IHubContext<NotificationHub> hubContext)
{
    public Task SendNotificationAsync(Notification notification) =>
        notification is not null
            ? hubContext.Clients.All.SendAsync("NotificationReceived", notification)
            : Task.CompletedTask;
}

前面的 C# 代码依赖 IHubContext<NotificationHub> 来访问客户端的上下文列表,公开了广播通知的功能。 在作用域中捕获的 hubContext 主构造函数参数用于触发 "NotificationReceived" 事件,但它并不旨在用于调用中心的 NotifyAll 方法。

3.1.3 方法

Hub  Hub<T> 方法与其他任何 C# 方法相同。 它们都定义返回类型、方法名称和参数。

  • Hub方法最常见的返回类型为 Task  Task<TResult>,后者表示异步 hub 操作。
  • 方法名称用于从客户端调用方法。 你可以使用 HubMethodNameAttribute 对其进行自定义。
  • 参数是可选的,但在定义时,客户端应提供相应的参数。

💢方法不需要触发事件,但通常会触发。

3.1.4 事件

可以从客户端按名称来订阅事件。 服务器负责引发事件。 HubHub<T>IHubContext<THub> 和 IHubContext<THub, T> 事件已命名,并且最多可以定义 10 个参数。 事件在服务器上触发,并由感兴趣的客户端进行处理。 当客户端订阅其 hub 连接上的事件时,该客户端则视为感兴趣的客户端。 当客户端调用由于其调用而激发事件的中心方法时,会间接地触发事件。 不过,客户端无法直接触发事件,因为这是服务器的职责。

事件客户端范围

从 IClientProxy 实例调用事件。 你从 Clients 类型实现 IHubClients和 IHubCallerClients 接口。 有多种方法可以将范围界定到特定 IClientProxy 实例。 可以从 Hub.Clients 属性定位以下范围:

成员详细信息
All所有连接的客户端(例如广播)。
AllExcept所有连接的客户端,不包括指定的连接(例如筛选的广播)。
Caller触发方法的连接的客户端(如回显)。
Client指定的客户端连接(单个连接)。
Clients指定的客户端连接(多个连接)。
Group指定组中的所有连接的客户端。
GroupExcept指定组中的所有连接的客户端(不包括指定的连接)。
Groups指定组中的所有连接的客户端(多个组)。
Others所有连接的客户端(不包括触发方法的客户端)。
OthersInGroup指定组中的所有连接的客户端(不包括触发方法的客户端)。
User指定用户的所有连接的客户端(单个用户可以连接到多个设备)。
Users指定用户的所有连接的客户端。

💢示例范围

请考虑以下图片,这些图片直观地显示了中心是如何向目标客户端发送消息的。 你可以展开图片,以方便阅读。

  • 广播到所有对象 👨‍👩‍👧‍👦

    ASP.NET Core SignalR hub sending message with Clients.All syntax.

    所有连接的客户端都将收到此消息,而不考虑它们可能属于或不属于的组。

  • 独立用户 🤵‍♂️

    ASP.NET Core SignalR hub sending message with Clients.User syntax.

    单个用户会将收到此消息,无论当前正在使用多少设备。

  • 独立组 💑

    ASP.NET Core SignalR hub sending message with Clients.Group syntax.

    只有属于特定组的客户端才会收到此消息。

3.2 客户端和 HubConnection 类

HubConnection 类是 SignalR 客户端概念,表示客户端与服务器 Hub 的连接。 它在 Microsoft.AspNetCore.SignalR.Client 命名空间中定义,并且是 Microsoft.AspNetCore.SignalR.Client NuGet 包的一部分。

你使用生成器模式和相应的 HubConnectionBuilder 类型创建 HubConnection。 考虑到中心的路由(或者 System.Uri),你可以创建 HubConnection。 生成器还可以指定其他配置选项,包括日志记录、所需的协议、身份验证令牌转发和自动重新连接,等等。

HubConnection API 公开启动和停止函数,分别用于启动和停止到服务器的连接。 此外,还提供了流式处理、调用 hub 方法和订阅事件的功能。

3.2.1 创建 HubConnection 示例

若要从 .NET SignalR 客户端 SDK 创建 HubConnection 对象,请使用 HubConnectionBuilder 类型:

using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleClient;

public sealed class Consumer : IAsyncDisposable
{
    private readonly string HostDomain =
        Environment.GetEnvironmentVariable("HOST_DOMAIN");
    
    private HubConnection _hubConnection;

    public Consumer()
    {
        _hubConnection = new HubConnectionBuilder()
            .WithUrl(new Uri($"{HostDomain}/hub/notifications"))
            .WithAutomaticReconnect()
            .Build();
    }

    public Task StartNotificationConnectionAsync() =>
        _hubConnection.StartAsync();

    public async ValueTask DisposeAsync()
    {
        if (_hubConnection is not null)
        {
            await _hubConnection.DisposeAsync();
            _hubConnection = null;
        }
    }
}

3.2.2 调用 hub 方法

如果客户端已经有一个已成功启动的客户端 HubConnection 实例,则该客户端可以使用 InvokeAsync 或 SendAsync 扩展调用中心上的方法。 如果中心方法返回 Task<TResult>,则 InvokeAsync<TResult> 结果的类型为 TResult。 如果中心方法返回 Task,则不会生成任何结果。 InvokeAsync  SendAsync 都需要中心方法的名称,以及 0 到 10 个参数。

  • InvokeAsync:使用指定的方法名称和可选参数调用服务器上的中心方法。
  • SendAsync:使用指定的方法名称和可选参数调用服务器上的中心方法。 此方法不会等待接收方的响应。

3.2.3 中心方法调用示例

SendNotificationAsync 向之前的 Consumer 类添加方法时,SendNotificationAsync 将委托给 _hubConnection,并根据 Notification 实例调用服务器中心上的 NotifyAll 方法。

public Task SendNotificationAsync(string text) =>
    _hubConnection.InvokeAsync(
        "NotifyAll", new Notification(text, DateTime.UtcNow));

3.2.4 处理事件

若要处理事件,请在 HubConnection 实例上注册一个处理程序。 如果你知道中心方法的名称并拥有 0 到 8 个参数,请调用其中一个 HubConnectionExtensions.On 重载。 处理程序可以满足以下任何 Action 变体:

  • Action
  • Action<T>
  • Action<T1,T2>
  • Action<T1,T2,T3>
  • Action<T1,T2,T3,T4>
  • Action<T1,T2,T3,T4,T5>
  • Action<T1,T2,T3,T4,T5,T6>
  • Action<T1,T2,T3,T4,T5,T6,T7>
  • Action<T1,T2,T3,T4,T5,T6,T7,T8>

或者,可以使用异步处理程序 API,当 TResult 是 Task 变体时,它们是 Func<TResult>

  • Func<Task>
  • Func<T,Task>
  • Func<T1,T2,Task>
  • ASP.NET Core SignalR 入门
  • Func<T1,T2,T3,T4,Task>
  • Func<T1,T2,T3,T4,T5,Task>
  • Func<T1,T2,T3,T4,T5,T6,Task>
  • Func<T1,T2,T3,T4,T5,T6,T7,Task>
  • Func<T1,T2,T3,T4,T5,T6,T7,T8,Task>

注册事件处理程序的结果是 IDisposable,它充当订阅。 若要取消订阅处理程序,请调用 Dispose。

3.2.5 事件注册示例

更新上一个 Consumer 类时,通过提供处理程序并调用 On 来注册事件:

using Microsoft.AspNetCore.SignalR.Client;
using System;
using System.Threading.Tasks;
using RealTime.Models;

namespace ExampleClient;

public sealed class Consumer : IAsyncDisposable
{
    private readonly string HostDomain =
        Environment.GetEnvironmentVariable("HOST_DOMAIN");
    
    private HubConnection _hubConnection;

    public Consumer()
    {
        _hubConnection = new HubConnectionBuilder()
            .WithUrl(new Uri($"{HostDomain}/hub/notifications"))
            .WithAutomaticReconnect()
            .Build();

        _hubConnection.On<Notification>(
            "NotificationReceived", OnNotificationReceivedAsync);
    }

    private async Task OnNotificationReceivedAsync(Notification notification)
    {
        // Do something meaningful with the notification.
        await Task.CompletedTask;
    }

    // Omitted for brevity.
}

当服务器的 hub 实例激发 "NotificationReceived" 事件时,将调用 OnNotificationReceivedAsync 方法。

3.3 Contoso Pizza 实时订单更新

Web 应用的服务器代码需要具有 Hub 实施,并且向客户端公开路由。 Hub 可以使用订单对象的唯一标识符创建用于跟踪的组。 然后,可以在此组中传达所有订单状态更改更新。

还需要更新客户端代码,以指示 Contoso Pizza 应用程序是 Blazor WebAssembly 应用。 可以使用 JavaScript SDK 或 .NET 客户端 SDK。 然后,将客户端轮询功能替换为生成 HubConnection 的代码,并启动到服务器的连接。 当导航到订单跟踪页时,代码必须加入订单的特定组,更改更新将发送到该组。 你需要订阅事件以获取订单状态更改,并进行相应的处理。

04.何时使用 ASP.NET Core SignalR

SignalR 提供实时 Web 功能。 回想一下,Contoso Pizza 需要实时地图来跟踪订单的状态和送达情况。 高峰时段的销售额损失促使团队调查比客户端轮询更好的解决方案。

决策条件

了解何时选择 SignalR 与了解何时应选择 SignalR 一样重要。 使用实时 Web 功能时,应用的用户体验依赖于其响应能力。 最好了解应用程序的哪些部分需要实时更新。

不应使用 SignalR 的情况

SignalR 的持久性取决于基础连接。 也就是说,如果有理由担心客户端应用程序的连接性,则 SignalR 并不是最佳选择。

另一个考虑因素是 SignalR 的可伸缩性。 根据并发连接的客户端数,Web 服务器在达到其限制时可能会发生资源争用。 在这种情况下,可能需要将应用程序部署到服务器场并使用底板。 自行实现此操作可能很繁琐。

或者,可以使用 Azure SignalR 服务解决此问题。 或者,可以利用各种复原能力和灾难恢复机制来缓解此问题。

SignalR 模态示例

可以在本地或云中使用 SignalR,也可以与 Azure SignalR 服务一起使用。

  • 本地:

    Diagram of ASP.NET Core SignalR being used on-premises.

  • 在云中:

    Diagram of ASP.NET Core SignalR being used in the cloud.

  • 与 Azure SignalR 服务一起:

    Diagram of using Azure SignalR Service.

💢有效用例

SignalR 不能替代传统的 HTTP 请求。 应用程序可以使用 SignalR 了解何时发出特定的 HTTP 请求。 这样一来,它们可以相互补充。

SignalR 有许多有效的用例。 以下列表表示 SignalR 的良好候选项:

  • 💯需要从服务器进行高频率更新的应用:
    • 游戏
    • 社交网络
    • Voting
    • 拍卖
    • GPS 应用
  • 💯仪表板和监视应用:
    • 公司仪表板
    • 实时地图
    • 即时销售更新
    • 旅行警报
    • 持续集成和持续交付 (CI/CD) 管道页
  • 💯协作式和多用户交互式应用:
    • 白板应用
    • 团队会议应用
    • 文档共享应用
    • Visual Studio Live Share
  • 💯需要即时通知的应用:
    • 电子邮件应用
    • 聊天应用
    • 基于轮次的游戏
    • 时序报告
    • GitHub Actions、发出和拉取请求系统

💯Contoso Pizza 方案

如果你在考虑 Contoso Pizza 实时订单地图中的客户端轮询解决方案,SignalR 可能是一种可行的替代方法。 与所有的编程和体系结构决策一样,请务必权衡 SignalR 的优点和缺点。

 🪁 快速创建请移步: ASP.NET Core SignalR 入门

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/930283.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Sonar基于SonarQube统一产品命名,助力提升开发者体验,以及本地、云端或IDE端的代码质量与安全

日前&#xff0c;领先的代码质量和安全解决方案提供商Sonar宣布&#xff0c;将围绕SonarQube简化其现有的产品命名。 作为Sonar的旗舰品牌&#xff0c;SonarQube代表了公司的核心使命&#xff1a;提高所有代码的质量和安全性&#xff0c;同时提供更好的开发人员体验。这些变化…

搭建高可用负载均衡系统:Nginx 与云服务的最佳实践

搭建高可用负载均衡系统&#xff1a;Nginx 与云服务的最佳实践 引言 在项目开发过程中&#xff0c;我们通常在开发和测试阶段采用单机架构进行开发和测试。这是因为在这个阶段&#xff0c;系统的主要目的是功能实现和验证&#xff0c;单机架构足以满足开发人员的日常需求&…

芯科科技突破性超低功耗Wi-Fi 6和低功耗蓝牙5.4模块加速设备部署

致力于以安全、智能无线连接技术&#xff0c;建立更互联世界的全球领导厂商Silicon Labs&#xff08;亦称“芯科科技”&#xff0c;今日宣布推出SiWx917Y超低功耗Wi-Fi 6和低功耗蓝牙&#xff08;Bluetooth LE&#xff09;5.4模块。 作为成功的第二代无线开发平台的新产品&…

CentOS7虚拟机 网络适配器 NAT模式和桥接模式区别

一、环境介绍 宿主机&#xff1a;Windows电脑 虚拟机&#xff1a;VMware下的CentOS7 局域网&#xff1a;路由器下的各真实主机组成的网络 内部局域网&#xff1a;宿主机构建的一个内部网路 二、NAT和桥接网络链接模式区别 NAT模式&#xff1a;相当于宿主机构建一个内部局域网&a…

【AIGC半月报】AIGC大模型启元:2024.12(上)

【AIGC半月报】AIGC大模型启元&#xff1a;2024.12&#xff08;上&#xff09; &#xff08;1&#xff09;OpenAI-12日发布会&#xff08;持续更新中........&#xff09;Day01-12.06&#xff1a;o1满血版上线&#xff08;已发布&#xff09;Day02-12.07&#xff1a;强化微调&a…

开源堡垒机JumpServer配置教程:使用步骤与配置

开源堡垒机JumpServer配置教程&#xff1a;使用步骤与配置 上一篇文章星哥讲了如何安装JumpServer堡垒机&#xff0c;本篇文章来讲如何配置和使用JumpServer。 安装成功后&#xff0c;通过浏览器访问登录 JumpServer 地址: http://<JumpServer服务器IP地址>:<服务运…

gulp应该怎么用,前端批量自动化替换文件

背景 最近公司准备把所有项目中用到的国际化相关的key规范化&#xff0c;原因是: 一直以来公司的app和web端 在针对相同的需求以及相同的国际化语言&#xff0c;需要设置不同的两份国际化文件&#xff0c;难以维护旧版的国际化文件中&#xff0c;存在的大量值重复&#xff0c…

【5G】5G Physical Layer物理层(一)

5G多址接入和物理层与长期演进&#xff08;LTE&#xff09;存在一些差异。在下行方向&#xff0c;5G与LTE相似&#xff0c;依旧采用正交频分多址&#xff08;OFDMA&#xff09;。而在上行方向&#xff0c;5G采用了OFDMA和单载波频分多址&#xff08;SC-FDMA&#xff09;&#x…

Docker 安装 中文版 GitLab

Docker 安装系列 安装GitLab、解决服务器内存不足问题、使用域名/IP地址访问项目 1、拉取 [rootTseng ~]# docker pull twang2218/gitlab-ce-zh:latest latest: Pulling from twang2218/gitlab-ce-zh 8ee29e426c26: Pull complete 6e83b260b73b: Pull complete e26b65fd11…

力扣-图论-4【算法学习day.54】

前言 ###我做这类文章一个重要的目的还是给正在学习的大家提供方向和记录学习过程&#xff08;例如想要掌握基础用法&#xff0c;该刷哪些题&#xff1f;&#xff09;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非…

减少30%人工处理时间,AI OCR与表格识别助力医疗化验单快速处理

在医疗行业&#xff0c;化验单作为重要的诊断依据和数据来源&#xff0c;涉及大量的文字和表格信息&#xff0c;传统的手工输入和数据处理方式不仅繁琐&#xff0c;而且容易出错&#xff0c;给医院的运营效率和数据准确性带来较大挑战。随着人工智能技术的快速发展&#xff0c;…

剖析千益畅行,共享旅游-卡,合规运营与技术赋能双驱下的旅游新篇

在数字化浪潮席卷各行各业的当下&#xff0c;旅游产业与共享经济模式深度融合&#xff0c;催生出旅游卡这类新兴产品。然而&#xff0c;市场乱象丛生&#xff0c;诸多打着 “共享” 幌子的旅游卡弊病百出&#xff0c;让从业者与消费者都深陷困扰。今天&#xff0c;咱们聚焦技术…

Mac曲线救国实现Bandizip右键一级菜单

一、前言 个人认为&#xff1a;Bandizip是Mac上最好用的压缩软件&#xff0c;没有之一。 在Mac系统上&#xff0c;学习版的Bandizip由于签名检验问题无法在访达右键的一级菜单显示 解压相关菜单。 有能力的&#xff0c;希望还是支持正版&#xff0c;找找优惠渠道应该100左右。…

理解 package.json 中版本号符号

今天&#xff0c;聊一聊在前端开发中&#xff0c; package.json 中怎么看版本号符号。 版本号符号的解释 版本号通常由三部分组成&#xff1a;主版本号、次版本号、补丁版本号&#xff0c;格式为 major.minor.patch。常见的符号有&#xff1a; ^&#xff1a;更新时允许自动…

【自用】管材流转项目前端重部署流程 vue2 webpackage4 vuecli4

一、配置 1.下载项目&#xff0c;使用 IDEA 打开&#xff0c;并配置 Nodejs 它提示我&#xff0c;需要 Node.js&#xff0c;因为 nodejs 14 的 installer 已经官网已经找不到了&#xff0c;使用 fnm 又太麻烦&#xff0c; 所以直接采用在 IDEA 中下载的方式就好了。 2.清除缓…

【优选算法篇】:双指针算法--开启高效编码的两把“魔法指针”,练习题演练

✨感谢您阅读本篇文章&#xff0c;文章内容是个人学习笔记的整理&#xff0c;如果哪里有误的话还请您指正噢✨ ✨ 个人主页&#xff1a;余辉zmh–CSDN博客 ✨ 文章所属专栏&#xff1a;c篇–CSDN博客 文章目录 一.双指针算法二.例题1.移动零2.复写零3.快乐数4.盛水最多的容器5.…

Figma入门-旋转效果

Figma入门-旋转效果 前言 在之前的工作中&#xff0c;大家的原型图都是使用 Axure 制作的&#xff0c;印象中 Figma 一直是个专业设计软件。 最近&#xff0c;很多产品朋友告诉我&#xff0c;很多原型图都开始用Figma制作了&#xff0c;并且很多组件都是内置的&#xff0c;对…

ffmpeg转码与加水印

文章目录 转码 与加水印引入jar包代码ffmpeg安装错误解决方法 转码 与加水印 引入jar包 <dependency><groupId>net.bramp.ffmpeg</groupId><artifactId>ffmpeg</artifactId><version>0.6.2</version></dependency>代码 impo…

网络原理 网络协议栈

POSIX API与网络协议栈 unix有不同的衍生版本&#xff0c;针对不同的版本&#xff0c;通过Posix定义了一套标准的操作系统接口API&#xff0c;使得不同的开发版本可以使用相同的API调用&#xff0c;具有可移植性。 网络连接相关API&#xff1a; 客户端 socket() bind() con…

将vscode上的项目提交到github上

1.windows终端中 创建github仓库 创建完成 提交代码 git init git config --global user.email "fuyulai2024163.com" git config --global user.name "Fuyulai-Hub" git add . git commit -m "first commit" git remote add origin https://g…