14-3_Qt 5.9 C++开发指南_QUdpSocket实现 UDP 通信_UDP 单播和广播

文章目录

  • 1.UDP通信概述
  • 2. UDP 单播和广播
    • 2.1 UDP 通信实例程序功能
    • 2.2 主窗口类定义和构造函数
    • 2.3 UDP通信的实现
    • 2.4 源码
      • 2.4.1 可视化UI设计
      • 2.4.2 mainwindow.h
      • 2.4.3 mainwindow.cpp

1.UDP通信概述

UDP(User Datagram Protocol,用户数据报协议)是轻量的、不可靠的、面向数据报 (datagram)、无连接的协议,它可以用于对可靠性要求不高的场合。与 TCP 通信不同,两个程序之间进行 UDP 通信无需预先建立持久的 socket 连接,UDP 每次发送数据报都需要指定目标地址和端口(如图14-6 所示)。

在这里插入图片描述

QUdpSocket 类用于实现 UDP 通信,它从 QAbstractSocket 类继承,因而与 QTcpSocket 共享大部分的接口函数。主要区别是 QUdpSocket 以数据报传输数据,而不是以连续的数据流。发送数据报使用函数QUdpSocket::writeDatagram(),数据报的长度一般少于512 字节,每个数据报包含发送者和接收者的 IP 地址和端口等信息。
要进行 UDP 数据接收,要用 QUdpSocket::bind()函数先绑定一个端口,用于接收传入的数据报。当有数据报传入时会发射readyRead()信号,使用readDatagram()函数来读取接收到的数据报。

UDP 消息传送有单播、广播、组播三种模式,其示意图如图 14-7 所示

在这里插入图片描述

  • 单播 (unicast)模式:一个 UDP 客户端发出的数据报只发送到另一个指定地址和端口的UDP 客户端,是一对一的数据传输。

  • 广播(broadcast)模式:一个 UDP 客户端发出的数据报,在同一网络范围内其他所有的UDP 客户端都可以收到。QUdpSocket 支持IPv4 广播。广播经常用于实现网络发现的协议。要获取广播数据只需在数据报中指定接收端地址为 QHostAddress::Broadcast,一般的广播地址是 255.255.255.255

  • 组播 (multicast)模式:也称为多播。UDP 客户端加入到另一个组播IP 地址指定的多播组,成员向组播地址发送的数据报组内成员都可以接收到,类似于 QQ 群的功能。QUdpSocket::joinMulticastGroup()函数实现加入多播组的功能,加入多播组后,UDP 数据的收发和UDP数据收发方法一样。

使用广播和多播模式,UDP 可以实现一些比较灵活的通信功能,而 TCP 通信只有单播模式没有广播和多播模式。所以,UDP 通信虽然不能保证数据传输的准确性,但是具有灵活性,一般的即时通信软件都是基于UDP 通信的。

QUdpSocket 类从QAbstractSocket 继承而来,但是又定义了较多新的功能函数用于实现 UDP特有的一些功能,如数据报读写和多播通信功能。QUdpSocket 没有定义新的信号。QUdpSocket的主要功能函数见表 14-6 (包括从 QAbstractSocket 继承的函数,省略了函数中的 const 关键字,省略了缺省参数)

在这里插入图片描述

在单播、广播和多播模式下,UDP 程序都是对等的,不像 TCP 通信那样分为客户端和服务器端。多播和广播的实现方式基本相同,只是数据报的目标IP 地址设置不同,多播模式需要加入多播组,实现方式有较大差异。
为分别演示这三种 UDP 通信模式,本节设计了两个实例。Samp14_3 实例演示 UDP 单播和广播通信,Samp14_4实例演示UDP组播通信。

2. UDP 单播和广播

2.1 UDP 通信实例程序功能

实例程序 samp14_3 实现 UDP 单播和广播,其主窗口是继承自 QMainWindow 的类,界面用UI 设计器设计。程序可以进行 UDP 数据报的发送和接收,samp14_3 的两个运行实例之间可以进行 UDP 通信,这两个实例可以运行在同一台计算机上,也可以运行在不同的计算机上。图 14-8和图14-9是samp14_3两个实例在一台计算机上运行时通信的界面。
在同一台计算机上运行时,两个运行实例需要绑定不同的端口,例如实例A 定端口 1200,实例B绑定端口 3355。实例A 向实例 B 发送数据报时,需要指定实例 B 所在主机的IP 地址、绑定端口作为目标地址和目标端口(为了准确识别相应的客户端,与上篇中元组的概念很相似),这样实例 B 才能接收到数据报。
如果两个实例在不同计算机上运行,则可以使用相同的端口,因为 IP 地址不同了,不会导致绑定时发生冲突。一般的UDP 通信程序都是在不同的计算机上运行的,约定一个固定的端口作为通信端口。

2.2 主窗口类定义和构造函数

在这里插入图片描述

主窗口是基于QMainWindow 的类 MainWindow,界面采用 UI 设计器设计。MainWindow类的定义如下(省略了 UI 设计器为 actions 和按钮生成的槽函数声明):

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>#include    <QUdpSocket>
#include    <QLabel>namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTprivate:QLabel  *LabSocketState;//socket状态显示标签QUdpSocket  *udpSocket;//QString getLocalIP();//获取本机IP地址public:explicit MainWindow(QWidget *parent = 0);~MainWindow();private slots:
//自定义槽函数void    onSocketStateChange(QAbstractSocket::SocketState socketState);void    onSocketReadyRead();//读取socket传入的数据
...private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

QUdpSocket 类型的私有变量 udpSocket 是用于UDP 通信的 socket。

定义了两个自定义槽函数,onSocketStateChange()与 udpSocket 的 stateChange()信号关联,用于显示 udpSocket 当前的状态;onSocketReadyRead()信号与 udpSocket 的readyRead()信号关联,用于读取缓冲区的数据报。
MainWindow 的构造函数主要完成udpSocket 的创建、信号与槽函数的关联,代码如下:

MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);LabSocketState=new QLabel(QString::fromLocal8Bit("Socket状态:"));//LabSocketState->setMinimumWidth(200);ui->statusBar->addWidget(LabSocketState);QString localIP=getLocalIP();//本机IPthis->setWindowTitle(this->windowTitle()+QString::fromLocal8Bit("----本机IP:")+localIP);ui->comboTargetIP->addItem(localIP);udpSocket=new QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocketconnect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));onSocketStateChange(udpSocket->state());connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}

onSocketStateChange(udpSocket->state());代码与上篇TCP通信程序里的完全一样。

2.3 UDP通信的实现

要实现UDP 数据的接收,必须先用QUdpSocket::bind()函数绑定一个端口,用于监听传入的数据报,解除绑定则使用 abort()函数。程序主窗口上的“绑定端口”和“解除绑定”按钮的响应代码如下:

void MainWindow::on_actStart_triggered()
{//绑定端口quint16     port=ui->spinBindPort->value(); //本机UDP端口if (udpSocket->bind(port))//绑定端口成功{ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**已成功绑定"));ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**绑定端口:")+QString::number(udpSocket->localPort()));ui->actStart->setEnabled(false);ui->actStop->setEnabled(true);}elseui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**绑定失败"));
}void MainWindow::on_actStop_triggered()
{//解除绑定udpSocket->abort(); //不能解除绑定ui->actStart->setEnabled(true);ui->actStop->setEnabled(false);ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**已解除绑定"));
}

绑定端口后,socket 的状态变为已绑定状态“BoundState”,解除绑定后状态变为未连接状态UnconnectedState”
发送点对点消息和广播消息都使用 QUdpSocket:: writeDatagram()函数,窗口上“发送消息和“广播消息”两个按钮的代码如下:

void MainWindow::on_btnSend_clicked()
{//发送消息 按钮QString     targetIP=ui->comboTargetIP->currentText(); //目标IPQHostAddress    targetAddr(targetIP);quint16     targetPort=ui->spinTargetPort->value();//目标portQString  msg=ui->editMsg->text();//发送的消息内容QByteArray  str=msg.toUtf8();udpSocket->writeDatagram(str,targetAddr,targetPort); //发出数据报ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("[out] ")+msg);ui->editMsg->clear();ui->editMsg->setFocus();
}void MainWindow::on_btnBroadcast_clicked()
{ //广播消息 按钮quint16     targetPort=ui->spinTargetPort->value(); //目标端口QString  msg=ui->editMsg->text();QByteArray  str=msg.toUtf8();udpSocket->writeDatagram(str,QHostAddress::Broadcast,targetPort);ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("[broadcast] ")+msg);ui->editMsg->clear();ui->editMsg->setFocus();
}

使用writeDatagram()函数向一个目标用户发送消息时,需要指定目标地址和端口。

在广播消息时,只需将目标地址更换为一个特殊地址,即广播地址 OHostAddress::Broadcast,一般是255.255.255.255。通信演示如下图所示:
在这里插入图片描述

QUdpSocket 发送的数据报是 QByteArray 类型的字节数组,数据报的长度一般不超过 512 字节。数据报的内容可以是文本字符串,也可以自定义格式的二进制数据,文本字符串无需以换行符结束。

QUdpSocket 接收到数据报后发射 readyRead()信号,在关联的槽函数 onSocketReadyRead()里读取缓冲区的数据报,代码如下:

void MainWindow::onSocketReadyRead()
{//读取收到的数据报while(udpSocket->hasPendingDatagrams()){QByteArray   datagram;datagram.resize(udpSocket->pendingDatagramSize());QHostAddress    peerAddr;quint16 peerPort;udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);QString str=datagram.data();QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"] ";ui->plainTextEdit->appendPlainText(peer+str);}
}

hasPendingDatagrams()表示是否有待读取的传入数据报。

pendingDatagramSize()返回待读取数据报的字节数。

readDatagram()函数用于读取数据报的内容,其函数原型为:

qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)

输入参数data和 maxSize 是必须的,表示最多读取 maxSize 字节的数据到变量 data 里。address和 port 变量是可选的,用于获取数据报来源的地址和端口。上面的代码中使用了完整的参数形式,从而可以获得数据报来源的地址 peerAddr 和端口 peerPort。如果无需获取来源地址和端口,可以采用简略形式,即:

udpSocket->readDatagram(datagram,data(),datagram,size());

读取的数据报内容是 QByteArray 字节数组,因为本程序只是传输字符串,所以简单地将其转换为字符串即可。如果传输的是自定义格式的字符串或二进制数据,需要对接收到的数据进行解析。

2.4 源码

2.4.1 可视化UI设计

在这里插入图片描述

2.4.2 mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>#include    <QUdpSocket>
#include    <QLabel>namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTprivate:QLabel  *LabSocketState;//socket状态显示标签QUdpSocket  *udpSocket;//QString getLocalIP();//获取本机IP地址public:explicit MainWindow(QWidget *parent = 0);~MainWindow();private slots:
//自定义槽函数void    onSocketStateChange(QAbstractSocket::SocketState socketState);void    onSocketReadyRead();//读取socket传入的数据
//void on_actStart_triggered();void on_actStop_triggered();void on_actClear_triggered();void on_btnSend_clicked();void on_actHostInfo_triggered();void on_btnBroadcast_clicked();private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

2.4.3 mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include    <QtNetwork>QString MainWindow::getLocalIP()
{QString hostName=QHostInfo::localHostName();//本地主机名QHostInfo   hostInfo=QHostInfo::fromName(hostName);QString   localIP="";QList<QHostAddress> addList=hostInfo.addresses();//if (!addList.isEmpty())for (int i=0;i<addList.count();i++){QHostAddress aHost=addList.at(i);if (QAbstractSocket::IPv4Protocol==aHost.protocol()){localIP=aHost.toString();break;}}return localIP;
}MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);LabSocketState=new QLabel(QString::fromLocal8Bit("Socket状态:"));//LabSocketState->setMinimumWidth(200);ui->statusBar->addWidget(LabSocketState);QString localIP=getLocalIP();//本机IPthis->setWindowTitle(this->windowTitle()+QString::fromLocal8Bit("----本机IP:")+localIP);ui->comboTargetIP->addItem(localIP);udpSocket=new QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocketconnect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));onSocketStateChange(udpSocket->state());connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));
}MainWindow::~MainWindow()
{udpSocket->abort();delete udpSocket;delete ui;
}void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
{switch(socketState){case QAbstractSocket::UnconnectedState:LabSocketState->setText(QString::fromLocal8Bit("scoket状态:UnconnectedState"));break;case QAbstractSocket::HostLookupState:LabSocketState->setText(QString::fromLocal8Bit("scoket状态:HostLookupState"));break;case QAbstractSocket::ConnectingState:LabSocketState->setText(QString::fromLocal8Bit("scoket状态:ConnectingState"));break;case QAbstractSocket::ConnectedState:LabSocketState->setText(QString::fromLocal8Bit("scoket状态:ConnectedState"));break;case QAbstractSocket::BoundState:LabSocketState->setText(QString::fromLocal8Bit("scoket状态:BoundState"));break;case QAbstractSocket::ClosingState:LabSocketState->setText(QString::fromLocal8Bit("scoket状态:ClosingState"));break;case QAbstractSocket::ListeningState:LabSocketState->setText(QString::fromLocal8Bit("scoket状态:ListeningState"));}
}void MainWindow::onSocketReadyRead()
{//读取收到的数据报while(udpSocket->hasPendingDatagrams()){QByteArray   datagram;datagram.resize(udpSocket->pendingDatagramSize());QHostAddress    peerAddr;quint16 peerPort;udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);QString str=datagram.data();QString peer="[From "+peerAddr.toString()+":"+QString::number(peerPort)+"] ";ui->plainTextEdit->appendPlainText(peer+str);}
}void MainWindow::on_actStart_triggered()
{//绑定端口quint16     port=ui->spinBindPort->value(); //本机UDP端口if (udpSocket->bind(port))//绑定端口成功{ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**已成功绑定"));ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**绑定端口:")+QString::number(udpSocket->localPort()));ui->actStart->setEnabled(false);ui->actStop->setEnabled(true);}elseui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**绑定失败"));
}void MainWindow::on_actStop_triggered()
{//解除绑定udpSocket->abort(); //不能解除绑定ui->actStart->setEnabled(true);ui->actStop->setEnabled(false);ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**已解除绑定"));
}void MainWindow::on_actClear_triggered()
{ui->plainTextEdit->clear();
}void MainWindow::on_btnSend_clicked()
{//发送消息 按钮QString     targetIP=ui->comboTargetIP->currentText(); //目标IPQHostAddress    targetAddr(targetIP);quint16     targetPort=ui->spinTargetPort->value();//目标portQString  msg=ui->editMsg->text();//发送的消息内容QByteArray  str=msg.toUtf8();udpSocket->writeDatagram(str,targetAddr,targetPort); //发出数据报ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("[out] ")+msg);ui->editMsg->clear();ui->editMsg->setFocus();
}void MainWindow::on_actHostInfo_triggered()
{//本机地址 按钮QString hostName=QHostInfo::localHostName();//本地主机名ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("本机主机名:")+hostName+"\n");QHostInfo   hostInfo=QHostInfo::fromName(hostName);QList<QHostAddress> addList=hostInfo.addresses();//if (!addList.isEmpty())for (int i=0;i<addList.count();i++){QHostAddress aHost=addList.at(i);if (QAbstractSocket::IPv4Protocol==aHost.protocol()){QString IP=aHost.toString();ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("本机IP地址:")+aHost.toString());if (ui->comboTargetIP->findText(IP)<0)ui->comboTargetIP->addItem(IP);}}}void MainWindow::on_btnBroadcast_clicked()
{ //广播消息 按钮quint16     targetPort=ui->spinTargetPort->value(); //目标端口QString  msg=ui->editMsg->text();QByteArray  str=msg.toUtf8();udpSocket->writeDatagram(str,QHostAddress::Broadcast,targetPort);ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("[broadcast] ")+msg);ui->editMsg->clear();ui->editMsg->setFocus();
}

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

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

相关文章

基于边缘无线协同感知的低功耗物联网LPIOT技术:赋能智慧园区方案以及数字工厂领域

回到2000年左右&#xff0c;物联网的底层技术支撑还是“ZigBee”&#xff0c;虽然当时ZigBee的终端功耗指标其实也并不庞大&#xff0c;但是&#xff0c;“拓扑复杂导致工程实施难度大”、“网络规模小导致的整体效率低下”都成为限制其发展的主要因素。 LPWAN&#xff0c;新一…

Android Tencent Shadow 插件接入指南

Android Tencent Shadow 插件接入指南 插件化简述一、clone 仓库二、编译运行官方demo三、发布Shadow到我们本地仓库3.1、安装Nexus 3.x版本3.2、修改发布配置3.3、发布仓库3.4、引用仓库包 四、编写我们自己的代码4.1、新建项目导入maven等共同配置4.1.1、导入buildScript4.1.…

51单片机学习--LED点阵屏显示图形动画

为了通用性考虑&#xff0c;需要把用到的几个口用特殊位声明来重新命名&#xff0c;由于RCLK在头文件中已有定义&#xff0c;所以这里把P3^5声明成RCK吧。。这样的做法可以提高可读性 sbit RCK P3^5; //RCLK sbit SCK P3^6; //SRCLK sbit SER P3^4;接下来编写74HC595的输…

dflow工作流使用1——架构和基本概念

对于容器技术、工作流等概念完全不懂的情况下理解dflow的工作方式会很吃力&#xff0c;这里记录一下个人理解。 dflow涉及的基本概念 工作流的概念很好理解&#xff0c;即某个项目可以分为多个步骤&#xff0c;每个步骤可以实现独立运行&#xff0c;只保留输入输出接口&#x…

【方法】Excel表格如何拆分数据?

当需要把多个数据逐个填到Excel单元格的时候&#xff0c;我们可以利用Excel的数据拆分功能&#xff0c;可以节省不少时间。 小编以下面的数据为例&#xff0c;看看如何进行数据拆分。 首先&#xff0c;要选择数字所在的单元格&#xff0c;然后依次点击菜单栏中的“数据”>…

Spring中的事务

一、为什么需要事务&#xff1f; 事务定义 将一组操作封装成一个执行单元&#xff08;封装到一起&#xff09;&#xff0c;要么全部成功&#xff0c;要么全部失败。 为什么要用事务&#xff1f; 比如转账分为两个操作&#xff1a; 第一步操作&#xff1a; A 账户 -100 元…

jsqlparser 安装和使用

jsqlparser是sql语句解析工具&#xff0c;可以解析sql并分析语法。 安装 <dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>4.3</version> </dependency>使用 String s …

【数据结构】快速排序

快速排序是一种高效的排序算法&#xff0c;其基本思想是分治法。它将一个大问题分解成若干个小问题进行解决&#xff0c;最后将这些解合并得到最终结果。 快速排序的主要思路如下&#xff1a; 选择一个基准元素&#xff1a;从待排序的数组中选择一个元素作为基准&#xff08;…

Spring指定bean在哪个应用加载

1.背景 某项目,spring架构,有2个不同的WebAppApplication入口,大部分service类共用,小部分类有区别,只需要在一个应用中加载,不需要在另一个应用中加载. 2.实现代码 自定义限制注解 package mis.shared.annotation;import java.lang.annotation.ElementType; import java.lan…

springboot+maven插件调用mybatis generator自动生成对应的mybatis.xml文件和java类

mybatis最繁琐的事就是sql语句和实体类&#xff0c;sql语句写在java文件里很难看&#xff0c;字段多的表一开始写感觉阻力很大&#xff0c;没有耐心&#xff0c;自动生成便成了最称心的做法。自动生成xml文件&#xff0c;dao接口&#xff0c;实体类&#xff0c;虽一直感觉不太优…

skywalking全链路追踪

文章目录 一、介绍二、全链路追踪1. 测试1 - 正常请求2. 测试2 - 异常请求 三、过滤非业务请求链路1. 链路忽略插件2. 配置3. 测试 一、介绍 在上一篇文章skywalking安装教程中我们介绍了skywalking的作用以及如何将其集成到我们的微服务项目中。本篇文章我们介绍在微服务架构…

router 跳转打开新窗口

let url router.resolve({name: screen, })?.hrefwindow.open(url, _black)注意&#xff1a;新窗口无法全屏 参考链接&#xff1a;https://stackoverflow.com/questions/29281986/run-a-website-in-fullscreen-mode/30970886#30970886

数据库索引失效的情况

1.对添加了索引的字段进行函数运算 2.如果是字符串类型的字段&#xff0c;如果不加单引号也会导致索引失效 3.如果最索引字段使用模糊查询&#xff0c;如果是头部模糊索引将失效&#xff0c;如果是尾部模糊索引则正常 4.如果使用or分割符&#xff0c;如果or前面的条件中的列有…

基于Yolov2深度学习网络的车辆检测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1. 卷积神经网络&#xff08;CNN&#xff09; 4.2. YOLOv2 网络 4.3. 实现过程 4.4. 应用领域 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022A 3.部分核心…

使用css和js给按钮添加微交互的几种方式

使用css和js给按钮添加微交互的几种方式 在现实世界中&#xff0c;当我们轻弹或按下某些东西时&#xff0c;它们会发出咔嗒声&#xff0c;例如电灯开关。有些东西会亮起或发出蜂鸣声&#xff0c;这些响应都是“微交互”&#xff0c;让我们知道我们何时成功完成了某件事。在本文…

【Winform学习笔记(五)】引用自定义控件库(dll文件)

引用自定义控件库dll文件 前言正文1、生成dll文件2、选择工具箱项3、选择需要导入的dll文件4、确定需要导入的控件5、导入及使用 前言 在本文中主要介绍 如何引用自定义控件库(dll文件)。 正文 1、生成dll文件 通过生成解决方案 或 重新生成解决方案 生成 dll 文件 生成的…

如何发布自己的npm包

发布一个简单的npm包 首先创建一个文件夹&#xff08;唯一的命名&#xff09;创建package.json包&#xff0c;输出npm init&#xff0c;一直回车就好。创建index.js文件&#xff0c;向外暴露方法。 将包上传或更新到 npm 执行登录命令&#xff1a;npm login 登录npm官网&…

Java中的SPI机制与扫描class原理

文章目录 前言ClassLoaderJAVA SPI机制Spring SPI机制示例原理 如何加载jar包里的class 前言 Java的SPI机制与Spring中的SPI机制是如何实现的&#xff1f; ClassLoader 这里涉及到了class Loader的机制&#xff0c;有些复杂&#xff0c;jdk中提供默认3个class Loader&#x…

AP AUTOSAR在软件定义汽车生态系统中的角色

AP AUTOSAR在软件定义汽车生态系统中的角色 AP AUTOSAR是AUTOSAR(汽车开放系统架构)的最新版本之一,它是一种面向服务的软件平台,旨在满足未来汽车电子系统的需求,特别是高性能计算、高带宽通信、软件无线更新(OTA)等方面。 AP AUTOSAR在软件定义汽车生态系统中扮演着…

docker 部署mysql 5.6集群

docker搭建mysql的集群&#xff08;一主双从&#xff09; 1.拉取镜像 docker pull mysql:5.6 2.启动master容器 docker run -it -d --name mysql_master -p 3306:3306 --ip 192.168.162.100 \ -v /data/mysql_master/mysql:/var/lib/mysql \ -v /data/mysql_master/conf.d…