Abp vNext(五)集成MQTTnet,可收发消息

news2024/9/20 19:03:41

一 前言

MQTT的相关理论内容这里不做过多介绍,请看下面两篇文章:

Introduction · MQTT协议中文版

MQTT协议-CSDN博客

这篇文章只做代码实现,文章中使用MQTTnet作为MQTT开发的组件。

MQTT分为服务端和客户端,一个服务端对应多个客户端。其中服务端相当于是一台服务器,它是MQTT消息传输的枢纽,负责将MQTT客户端发送来的消息传递给另一个客户端;MQTT服务端还负责管理客户端,确保客户端之间的通讯顺畅,保证MQTT消息得以正确接收和准确投递。

MQTT客户端可以向服务端发布信息,也可以从服务端接受信息,我们把客户端向服务端发送消息的行为称为“发布”消息,客户端也可以“订阅”消息。

二 服务端

服务端可以不用自己开发,有几个常用的第三方服务端,比如EMQ,EMQ怎么使用的,可以查看官网:物联网实时消息引擎 | EMQ

这里不介绍第三方服务,这里具体介绍如何自己动手开发服务端。

1、添加MQTTnet引用

新建一个控制台应用程序,打开NuGet程序包,添加MQTTnet,版本选择3.0.13,选择版本这里要注意一下,不同的版本实现方式不同,下面的实现代码中,如果选择高版本,可能会有异常。

2、代码实现

 不啰嗦,直接上代码

using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Receiving;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System.Text;

namespace ConsoleApp2
{
    internal class Program
    {
        static void Main(string[] args)
        {
            MqttServerClass serverClass = new MqttServerClass();
            serverClass.StartMqttServer().Wait();
            Console.ReadLine();
        }


    }

    public static class Config
    {
        public static int Port { get; set; } = 1883;
        public static string UserName { get; set; } = "Username";
        public static string Password { get; set; } = "Password";

    }
    public class UserInstance
    {
        public string ClientId { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
    }
    public class MqttServerClass
    {
        private IMqttServer mqttServer;
        private List<MqttApplicationMessage> messages = new List<MqttApplicationMessage>();

        public async Task StartMqttServer()
        {
            try
            {
                if (mqttServer == null)
                {
                    var optionsBuilder = new MqttServerOptionsBuilder()
                    .WithDefaultEndpoint()
                    .WithDefaultEndpointPort(Config.Port)
                    //连接拦截器
                    .WithConnectionValidator(
                        c =>
                        {
                            //var flag = c.Username == Config.UserName && c.Password == Config.Password;
                            //if (!flag)
                            //{
                            //    c.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
                            //    return;
                            //}
                            //设置代码为 Success
                            c.ReasonCode = MqttConnectReasonCode.Success;
                            //instances.Add(new UserInstance()  //缓存到内存的List集合当中
                            //{
                            //    ClientId = c.ClientId,
                            //    UserName = c.Username,
                            //    Password = c.Password
                            //});
                        })
                    //订阅拦截器
                    .WithSubscriptionInterceptor(
                        c =>
                        {
                            if (c == null) return;
                            c.AcceptSubscription = true;
                        })
                    //应用程序消息拦截器
                    .WithApplicationMessageInterceptor(
                        c =>
                        {
                            if (c == null) return;
                            c.AcceptPublish = true;
                        })
                   //clean session是否生效
                   .WithPersistentSessions();

                    mqttServer = new MqttFactory().CreateMqttServer();

                    //客户端断开连接拦截器
                    //mqttServer.UseClientDisconnectedHandler(c =>
                    //{
                    //    //var user = instances.FirstOrDefault(t => t.ClientId == c.ClientId);
                    //    //if (user != null)
                    //    //{
                    //    //    instances.Remove(user);
                    //    //}
                    //});

                    //服务开始
                    mqttServer.StartedHandler = new MqttServerStartedHandlerDelegate(OnMqttServerStarted);
                    //服务停止
                    mqttServer.StoppedHandler = new MqttServerStoppedHandlerDelegate(OnMqttServerStopped);
                    //客户端连接
                    mqttServer.ClientConnectedHandler = new MqttServerClientConnectedHandlerDelegate(OnMqttServerClientConnected);
                    //客户端断开连接(此事件会覆盖拦截器)
                    mqttServer.ClientDisconnectedHandler = new MqttServerClientDisconnectedHandlerDelegate(OnMqttServerClientDisconnected);
                    //客户端订阅
                    mqttServer.ClientSubscribedTopicHandler = new MqttServerClientSubscribedHandlerDelegate(OnMqttServerClientSubscribedTopic);
                    //客户端取消订阅
                    mqttServer.ClientUnsubscribedTopicHandler = new MqttServerClientUnsubscribedTopicHandlerDelegate(OnMqttServerClientUnsubscribedTopic);
                    //服务端收到消息
                    mqttServer.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(OnMqttServerApplicationMessageReceived);

                    await mqttServer.StartAsync(optionsBuilder.Build());

                    //主动发送消息到客户端
                    //await mqttServer.PublishAsync(new
                    //     MqttApplicationMessage
                    //{
                    //    Topic = "testtopic",
                    //    Payload = Encoding.UTF8.GetBytes("dsdsd")
                    //});
                    //mqttServer.GetClientStatusAsync();
                    //mqttServer.GetRetainedApplicationMessagesAsync();
                    //mqttServer.GetSessionStatusAsync();

                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"MQTT Server start fail.>{ex.Message}");
            }
        }
        private void OnMqttServerStarted(EventArgs e)
        {
            if (mqttServer.IsStarted)
            {
                Console.WriteLine("MQTT服务启动完成!");
            }
        }
        private void OnMqttServerStopped(EventArgs e)
        {
            if (!mqttServer.IsStarted)
            {
                Console.WriteLine("MQTT服务停止完成!");
            }
        }
        private void OnMqttServerClientConnected(MqttServerClientConnectedEventArgs e)
        {
            Console.WriteLine($"客户端[{e.ClientId}]已连接");
        }
        private void OnMqttServerClientDisconnected(MqttServerClientDisconnectedEventArgs e)
        {
            Console.WriteLine($"客户端[{e.ClientId}]已断开连接!");
        }
        private void OnMqttServerClientSubscribedTopic(MqttServerClientSubscribedTopicEventArgs e)
        {
            Console.WriteLine($"客户端[{e.ClientId}]已成功订阅主题[{e.TopicFilter}]!");
        }
        private void OnMqttServerClientUnsubscribedTopic(MqttServerClientUnsubscribedTopicEventArgs e)
        {
            Console.WriteLine($"客户端[{e.ClientId}]已成功取消订阅主题[{e.TopicFilter}]!");
        }
        private void OnMqttServerApplicationMessageReceived(MqttApplicationMessageReceivedEventArgs e)
        {
            messages.Add(e.ApplicationMessage);
            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:fff"));
            Console.WriteLine($"客户端[{e.ClientId}]>> Topic[{e.ApplicationMessage.Topic}] Payload[{Encoding.UTF8.GetString(e.ApplicationMessage.Payload ?? new byte[] { })}] Qos[{e.ApplicationMessage.QualityOfServiceLevel}] Retain[{e.ApplicationMessage.Retain}]");
        }
    }


}

三 客户端

客户端可以做成一个独立的项目,如果有什么地方需要调用比如发送消息的方法,可以直接引用MQTT服务直接进行调用,当然这是其中一个思路,我目前把MQTT服务放在了Application项目中,具体的实现思路就不细聊了,看官方文档吧,我这里直接上代码,主打一个拿来就用。

注意:

1、先启动mqtt服务端,在启动客户端,同时客户端配置文件appsetting.json中MqttHost配置要加上,节点MqttHost中无值,MQTT客户端不启用,目前主题是固定的testTopic

2、项目即可发送消息,又可接收消息

先看项目结构

1、appsettings.json增加配置节点

HttpApi.Host项目中的appsettings.json增加MQTT相关配置节点

"MqttSettingsProvider": {
  "BrokerHostSettings": {
    "MqttHost": "", //localhost //服务端ip
    "MqttPort": 1883  //服务端端口
  },
  "ClientSettings": {
    "ClientId": "5eb020f043ba8930506acbdd2",
    "UserName": "",
    "Password": ""
  },
  "TopicName": "testTopic"
}

2、添加MQTTnet引用

在Application项目中通过NuGet包添加MQTTnet引用,版本与服务端保持一致3.0.13

3、代码示例

下面的代码示例不是按代码书写顺序来的,为了方便写文档,直接按文件顺序粘贴代码

Application→MqttServer→AspCoreMqttClientOptionBuilder.cs

using MQTTnet.Client.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WMSInterface.MqttServer
{
    public class AspCoreMqttClientOptionBuilder : MqttClientOptionsBuilder
    {
        public IServiceProvider ServiceProvider { get; }

        public AspCoreMqttClientOptionBuilder(IServiceProvider serviceProvider)
        {
            ServiceProvider = serviceProvider;
        }
    }
}

Application→MqttServer→BrokerHostSettings.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WMSInterface.MqttServer
{
    public class BrokerHostSettings
    {
        public string MqttHost { get; set; }

        public int MqttPort { get; set; }
    }
}

Application→MqttServer→ClientSettings.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WMSInterface.MqttServer
{
    public class ClientSettings
    {
        public string ClientId { get; set; }

        public string UserName { get; set; }

        public string Password { get; set; }
    }
}

Application→MqttServer→IMessageSendService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WMSInterface.MqttServer
{
    public interface IMessageSendService
    {
        int Order { get; }

       Task SendMessage(MessageContext context);
    }
}

Application→MqttServer→IMqttClientService.cs

using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Receiving;


namespace WMSInterface.MqttServer
{
    public interface IMqttClientService : IHostedService, IMqttClientConnectedHandler, IMqttClientDisconnectedHandler, IMqttApplicationMessageReceivedHandler
    {
        Task Publish(string topicName, string message);
    }
}

Application→MqttServer→MessageContext.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WMSInterface.MqttServer
{
    public class MessageContext
    {
        public string TopicName { get; set; }

        public string Title { get; set; }

        public string Content { get; set; }

        //public IList<UserModel> UserList { get; set; } = new List<UserModel>();


        public string[] Users { get; set; }

        public string ObjId { get; set; }

    }
}

Application→MqttServer→MqttClientService.cs

using MQTTnet.Client;
using MQTTnet.Protocol;
using MQTTnet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Microsoft.Extensions.Options;
using MQTTnet.Server;
using System.Threading;
using Microsoft.Extensions.Hosting;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Receiving;
using Microsoft.Extensions.DependencyInjection;
using MQTTnet.Client.Options;
using WMSInterface.Server;

namespace WMSInterface.MqttServer
{
    public class MqttClientService : IMqttClientService, IHostedService, IMqttClientConnectedHandler, IMqttClientDisconnectedHandler, IMqttApplicationMessageReceivedHandler
    {
        private IMqttClient mqttClient;

        private IMqttClientOptions options;

        private readonly IServiceProvider _serviceProvider;

        public MqttClientService(IMqttClientOptions options, IServiceProvider serviceProvider)
        {
            this.options = options;
            _serviceProvider = serviceProvider;
            mqttClient = new MqttFactory().CreateMqttClient();
            ConfigureMqttClient();
        }

        private void ConfigureMqttClient()
        {
            mqttClient.ConnectedHandler = this;
            mqttClient.DisconnectedHandler = this;
            mqttClient.ApplicationMessageReceivedHandler = this;
        }

        public async Task HandleApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs e)
        {
            IEnumerable<IMqttMessageHandler> handlers = _serviceProvider.GetServices<IMqttMessageHandler>();
            foreach (IMqttMessageHandler handler in handlers)
            {
                await handler.HandleMessage(e.ApplicationMessage.Topic, e.ApplicationMessage.Payload);
            }
        }

        public async Task Publish(string topicName, string message)
        {
            string topic = topicName.Trim();
            string msg = message.Trim();
            if (string.IsNullOrEmpty(topic))
            {
                Console.Write("主题不能为空!");
            }
            else if (!mqttClient.IsConnected)
            {
                Console.Write("MQTT客户端尚未连接!");
            }
            else
            {
                await MqttClientExtensions.PublishAsync(applicationMessage: new MqttApplicationMessageBuilder()
                    .WithTopic(topic)
                    .WithPayload(msg)
                    .WithAtMostOnceQoS()
                    .WithRetainFlag(value: false)
                    .Build(), client: mqttClient);
            }
        }

        /// <summary>
        /// 订阅连接成功事件
        /// </summary>
        /// <param name="eventArgs"></param>
        /// <returns></returns>
        public async Task HandleConnectedAsync(MqttClientConnectedEventArgs eventArgs)
        {
            await mqttClient.SubscribeAsync("testTopic");
            //...可订阅多个主题
        }
        /// <summary>
        /// 订阅断开连接事件
        /// </summary>
        /// <param name="eventArgs"></param>
        /// <returns></returns>
        public async Task HandleDisconnectedAsync(MqttClientDisconnectedEventArgs eventArgs)
        {
            await mqttClient.UnsubscribeAsync("testTopic");
            //尝试重新连接
            //await mqttClient.ConnectAsync(options);
        }

        public async Task StartAsync(CancellationToken cancellationToken)
        {
            await mqttClient.ConnectAsync(options);
            if (!mqttClient.IsConnected)
            {
                await mqttClient.ReconnectAsync();
            }
        }

        public async Task StopAsync(CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                MqttClientDisconnectOptions disconnectOption = new MqttClientDisconnectOptions
                {
                    ReasonCode = MqttClientDisconnectReason.NormalDisconnection,
                    ReasonString = "NormalDiconnection"
                };
                await mqttClient.DisconnectAsync(disconnectOption, cancellationToken);
            }
            await mqttClient.DisconnectAsync();
        }

    }
}

Application→MqttServer→MqttClientServiceProvider.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WMSInterface.MqttServer
{
    public class MqttClientServiceProvider
    {
        public readonly IMqttClientService MqttClientService;

        public MqttClientServiceProvider(IMqttClientService mqttClientService)
        {
            MqttClientService = mqttClientService;
        }
    }
}

Application→MqttServer→MqttMessageService.cs

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WMSInterface.MqttServer
{
    public class MqttMessageService : IMessageSendService
    {
        private string _topicName = string.Empty;
        private readonly IMqttClientService _mqttClientService;
        private readonly ILogger<MqttMessageService> _logger;
        private readonly IConfiguration _configuration;

        public int Order => 0;

        public MqttMessageService(MqttClientServiceProvider mqttClientServiceProvider, 
            ILogger<MqttMessageService> logger, 
            IConfiguration configuration)
        {
            _mqttClientService = mqttClientServiceProvider.MqttClientService;
            _logger = logger;
            _configuration = configuration;
            _topicName = configuration["MqttSettingsProvider:TopicName"];
        }

        public Task SendMessage(MessageContext context)
        {
            try
            {
                if (!string.IsNullOrEmpty(context.Content))
                {
                    var content = new
                    {
                        content = context.Content,
                        //users = context.UserList.Select((UserModel m) => m.Id.ToString()).ToList()
                    };
                    _mqttClientService.Publish(_topicName, JsonConvert.SerializeObject(content));
                }
            }
            catch (Exception e)
            {
                _logger.LogError(e, "MQTT发送消息错误。");
            }
            return Task.CompletedTask;
        }
    }
}

Application→MqttServer→MqttServerModule.cs

注意:这里跟配置文件appsettings.json中的节点MqttHost有关联,MqttHost为空,不启动MQTT客户端服务,MqttHost不为空,会客户端会连接服务端。

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.Modularity;

namespace WMSInterface.MqttServer
{
    public class MqttServerModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            IConfiguration configuration = context.Services.GetConfiguration();
            MqttSettingsProvider mqttSettingsProvider = configuration.GetSection("MqttSettingsProvider").Get<MqttSettingsProvider>();
            if (!string.IsNullOrEmpty(mqttSettingsProvider.BrokerHostSettings?.MqttHost))
            {
                context.Services.AddMqttClientHostedService(mqttSettingsProvider);
                context.Services.TryAddEnumerable(ServiceDescriptor.Transient<IMessageSendService, MqttMessageService>());
            }
        }
    }
}

Application→MqttServer→MqttServiceCollectionExtension.cs

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WMSInterface.MqttServer
{
    public static class MqttServiceCollectionExtension
    {
        public static IServiceCollection AddMqttClientHostedService(this IServiceCollection services, MqttSettingsProvider mqttSettingsProvider)
        {
            AddMqttClientServiceWithConfig(services, delegate (AspCoreMqttClientOptionBuilder aspOptionBuilder)
            {
                IConfiguration configuration = services.GetConfiguration();
                aspOptionBuilder
                .WithCredentials(mqttSettingsProvider.ClientSettings.UserName, mqttSettingsProvider.ClientSettings.Password)
                .WithClientId(mqttSettingsProvider.ClientSettings.ClientId)
                .WithTcpServer(mqttSettingsProvider.BrokerHostSettings.MqttHost);
            });
            return services;
        }

        private static IServiceCollection AddMqttClientServiceWithConfig(this IServiceCollection services, Action<AspCoreMqttClientOptionBuilder> configure)
        {
            services.AddSingleton(delegate (IServiceProvider serviceProvider)
            {
                AspCoreMqttClientOptionBuilder aspCoreMqttClientOptionBuilder = new AspCoreMqttClientOptionBuilder(serviceProvider);
                configure(aspCoreMqttClientOptionBuilder);
                return aspCoreMqttClientOptionBuilder.Build();
            });
            services.AddSingleton<MqttClientService>();
            services.AddSingleton((Func<IServiceProvider, IHostedService>)((IServiceProvider serviceProvider) => serviceProvider.GetService<MqttClientService>()));
            services.AddSingleton(delegate (IServiceProvider serviceProvider)
            {
                MqttClientService service = serviceProvider.GetService<MqttClientService>();
                return new MqttClientServiceProvider(service);
            });
            return services;
        }
    }
}

Application→MqttServer→MqttSettingsProvider.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WMSInterface.MqttServer
{
    public class MqttSettingsProvider
    {
        public BrokerHostSettings BrokerHostSettings { get; set; }

        public ClientSettings ClientSettings { get; set; }
    }
}

Application→Server→IMqttMessageHandler.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WMSInterface.Server
{
    public interface IMqttMessageHandler
    {
        Task HandleMessage(string topic, byte[] data);
    }
}

Application→Server→MqttMessageHandler.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;

namespace WMSInterface.Server
{
    public class MqttMessageHandler: IMqttMessageHandler,ITransientDependency
    {
       public async Task HandleMessage(string topic, byte[] data)
        {
            if (!string.IsNullOrEmpty(topic) || data != null)
            {
                Console.WriteLine($"接收到的主题:{topic},消息:{Encoding.UTF8.GetString(data)}");
            }
        }
    }
}

xx.HttpApi.Host→xxHttpApiHostModule.cs

typeof(MqttServerModule)

MQTT客户端发送消息代码示例

xx.HttpApi→Controllers→MqttController.cs

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
using WMSInterface.MqttServer;

namespace WMSInterface.Controllers
{
    /// <summary>
    /// mqtt
    /// </summary>
    [ApiController]
    [Route("api/[controller]/[action]")]
    public class MqttController : AbpController
    {
        private readonly IConfiguration _configuration;
        private readonly IMessageSendService _messageSendService;

        public MqttController(IConfiguration configuration, 
            IMessageSendService messageSendService)
        {
            _configuration = configuration;
            _messageSendService = messageSendService;
        }

        #region 发送消息
        /// <summary>
        /// 发送消息
        /// </summary>
        /// <param name="body"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> SendAsync(MessageContext body)
        {
            await _messageSendService.SendMessage(body);
            return Ok("ok");
        }
        #endregion
    }
}

四  MQTT收发消息测试

下载MQTTX工具进行MQTT消息的测试,使用方法就不具体介绍了,基本上是拿来就用

MQTTX:全功能 MQTT 客户端工具

发送消息入口

接收消息示例

五 结尾

本文章不做MQTT科普,默认具有一定的MQTT认知,主要目的是让大家可以直接在abp框架中快速集成MQTT

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker + Win 10 学习记录

下载Docker Release notes | Docker Docs 推荐使用4.33版本&#xff0c;最新的Docker版本在win10 22H2无法安装。需要升级到win11. 查看Win10版本是否与最新版的Docker兼容 运行 win R&#xff0c; 然后输入winver 如果你的Docker版本无法在当前的win10安装&#xff0c;请更…

编码器-解码器架构_by《李沐:动手学深度学习v2》pytorch版

系列文章目录 文章目录 系列文章目录一、引言编码器解码器合并编码器和解码器小结练习答案1. 编码器和解码器是否必须是同一类型的神经网络&#xff1f;2. 除了机器翻译&#xff0c;还有其它可以适用于“编码器&#xff0d;解码器”架构的应用吗&#xff1f; 一、引言 正如我们…

LocalDateTime,OffsetDateTime和ZonedDateTime(上)

图片来源&#xff1a;https://www.cnblogs.com/yourbatman/p/14324575.html 一. LocalDate和LocalTime LocalDate&#xff1a;代表不含时区信息的日期&#xff0c;它只能表示年、月、日。它适用于记录一个日子&#xff0c;比如生日、纪念日、或者任何只需要日期而不需要具体时…

除猫毛用粘毛器还是宠物空气净化器?希喂/米家/352/范罗士/有哈空气净化器对比

微博之夜&#xff0c;明星互送礼物环节&#xff0c;要求所有嘉宾准备一份礼物&#xff0c;再由其他明星随机抽取互换礼物。田曦薇送粘毛器可是引起了广泛的争议和批评。不说价格&#xff0c;粘毛器对咱养猫人来讲还真是刚需啊。我朋友家三只猫&#xff0c;出门不用说啥&#xf…

掌握数据中心虚拟化:关键挑战与解决方案

数据中心虚拟化是使用云软件平台将物理数据中心转变为数字数据中心的过程&#xff0c;使企业能够远程访问信息和应用程序。它包括在数据中心内创建物理基础设施的多个虚拟版本&#xff0c;通过将服务器、存储和网络等资源划分为虚拟实体来实现资源的高效利用。 虚拟化环境中的关…

[c++进阶(八)]STL容器适配器之queue

1.前言 和stack一样&#xff0c;队列也没有把他放在容器的一栏里面&#xff0c;而是把他放在容器适配器的一栏。这也是因为queue是使用了别人的相关接口&#xff0c;空间然后来封装自己的内容&#xff0c;最后再给上层用户使用。 2.队列 队列的性质就是先进先出&#xff0c;他…

【C++ 学习】多态的基础和原理(10)

目录 前言1. 概念2. 多态的定义及实现2.1 多态的构成条件2.2 虚函数2.3 虚函数重写2.4 虚函数重写的例外2.4.1 协变2.4.1 析构函数的重写 2.5 多态调用和普通调用2.6 函数重写/函数隐藏/函数重载 的对比2.6.1 函数重写2.6.2 函数隐藏2.6.3 函数重载 2.7 C11 final 和override 3…

HT326 免电感滤波2x20W D类立体声音频功放

特点 输出功率(BTL模式) 2x20W (VDD14.5V,RL4Ω,THDN1%) 单电源系统: 4.5V-18V; 超过90%效率&#xff0c;无需散热器 扩频功能&#xff0c;免电感滤波 模拟差分/单端输入可选 增益:32dB 保护功能:过压/过流/过热/欠压异常&#xff0c;直流检测 和短路保护 无铅无卤封装&#x…

three.js 热力图

使用three.js 和 heatMap.js 实现在 三维场景中展示热力图的效果&#xff0c;以下代码复制粘贴即可在你的本机运行。 在线编辑运行预览可方位 https://threehub.cn/#/codeMirror?navigationThreeJS&classifyexpand&idheatmap3D 在 https://threehub.cn 中还有很多案例…

[PTA]7-3 乘法口诀数列

[PTA]7-3 乘法口诀数列 输出格式&#xff1a; 在一行中输出数列的前 n 项。数字间以 1 个空格分隔&#xff0c;行首尾不得有多余空格。 输入样例&#xff1a; 2 3 10 输出样例&#xff1a; 2 3 6 1 8 6 8 4 8 4 样例解释&#xff1a; 数列前 2 项为 2 和 3。从 2 开始&#…

Java集合(三)

目录 Java集合&#xff08;三&#xff09; Java双列集合体系介绍 HashMap类 HashMap类介绍 HashMap类常用方法 HashMap类元素遍历 LinkedHashMap类 LinkedHashMap类介绍 LinkedHashMap类常用方法 LinkedHashMap类元素遍历 Map接口自定义类型去重的方式 Set接口和Ma…

grafana 使用常见问题

一、点击 panel 没有反应&#xff0c;没有出现 edit 选项。 方法一 将鼠标放在 panel 的任意位置&#xff0c;然后键盘输入 "e"&#xff0c;然后再次点击 title&#xff0c;即可出现选项框。 方法二 F12 查看当前 panel id&#xff0c;然后在浏览器 url 地址上拼接…

探索AI大模型的未来:电信运营商与云服务商的新征途@附58页PDF文件下载

在数字化浪潮的推动下&#xff0c;人工智能&#xff08;AI&#xff09;大模型正成为推动行业变革的关键力量。近日&#xff0c;腾讯云联合中国信通院及中国通信标准化协会发布了《2024年AI大模型应用发展研究报告》&#xff0c;深入探讨了电信运营商与云服务商在AI大模型领域的…

Unicode与UTF-8的关系

Unicode又称统一码&#xff0c;万国码。uni是一个英文词根&#xff0c;原型是one, 表示“单一, 一个”&#xff0c;所以unicode本意是“一个码”&#xff0c;就是让每个字符都有一个唯一的编码。它就像个武林盟主&#xff0c;把世上所有的语言符号一勺烩&#xff0c;一统了编码…

反相求和电路设计

1 简介 该电路可对两个输入信号进行求和&#xff08;相加&#xff09;&#xff0c;并将其在输出端反相。输入信号通常要求低阻抗源&#xff0c;因为该电路的输入阻抗由输入电阻R1和R2决定。反相放大器的共模电压等于连接到同相节点的电压。 2 设计目标 2.1 输入 2.2 输出 2.3…

9.4 溪降技术:带包下降

目录 9.4 携包下降概述观看视频课程电子书&#xff1a;携包下降在瀑布中管理背包扔背包滑索传送背包固定到安全带 V7 提示&#xff1a;将背包固定到安全带总结 9.4 携包下降 概述 在水流和悬崖边缘携包下降是最危险的情况&#xff01; 正如我们之前所学&#xff0c;在峡谷探险中…

Tomcat 后台弱⼝令部署war包

漏洞原理 在tomcat8环境下默认进⼊后台的密码为 tomcat/tomcat &#xff0c;未修改造成未授权即可进⼊后台&#xff0c;或者管理员把密码设置成弱⼝令。 影响版本 全版本(前提是⼈家存在弱⼝令) 环境搭建 8 cd vulhub-master/tomcat/tomcat8 docker-compose up -d 漏洞复…

AD9854 为什么输出波形幅度受限??

&#x1f3c6;本文收录于《全栈Bug调优(实战版)》专栏&#xff0c;主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&am…

【CSS in Depth 2 精译_035】5.5 Grid 网格布局中的子网格布局(全新内容)

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09; 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位&#xff08;已完结&#xff09; 2.1 相对…

C++速通LeetCode中等第6题-找到字符串中所有字母异位词(滑动窗口最详细代码注释)

滑动窗口法&#xff1a; class Solution { public:vector<int> findAnagrams(string s, string p) {unordered_map<char,int> need,window;for(char c : p) need[c];int left 0,right 0;int valid 0;vector<int> res;//窗口数据更新while(right < s.s…