【C++ QT项目3】——网络调试助手的设计与实现

【C++ QT项目3】——网络调试助手的设计与实现

  • 1 TCP网络调试助手
    • 1.1 项目整体开发流程
    • 1.2 QTtcp服务器的关键流程
    • 1.3 QTtcp客户端的关键流程
    • 1.4 UI界面的设计
    • 1.5 TCP协议理论知识
    • 1.6 Socket网络通信理论
  • 2 网络通信核心代码
    • 2.1 TCP服务端连接的核心代码
    • 2.2 TCP服务端的数据通信核心代码
    • 2.3 TCP客户端的核心代码
  • 3 TCP服务端项目功能优化
    • 3.1 自动刷新IP地址
    • 3.2 服务器向不同客户端发数据
    • 3.3 TextEdit设置特定位置文字颜色
    • 3.4 客户端断开检测
    • 3.5 停止监听的实现
  • 4 TCP客户端项目开发及优化
    • 4.1 检测连接状态
    • 4.2 其他细节功能
  • 5 项目总结

1 TCP网络调试助手

   本次设计的网络调试助手简化界面UI的设计,重点在于网络通信的相关知识点,侧重服务器与客户端的创建以及服务器与客户端的数据交互,同时复习巩固之前UI控件

1.1 项目整体开发流程

在这里插入图片描述

1.2 QTtcp服务器的关键流程

工程建立,需要在.pro加入网络权限
在这里插入图片描述
创建一个基于 QTcpServer 的服务端涉及以下关键步骤:

1. 创建并初始化 QTcpServer 实例:

  • 实例化 QTcpServer
  • 调用listen 方法在特定端口监听传入的连接

2. 处理新连接:

  • newConnection 信号连接一个槽函数。
  • 在槽函数中,使用 nextPendingConnection 获取 QTcpSocket 以与客户端通信。

3. 读取和发送数据:

  • 通过连接 QTcpSocketreadyRead 信号来读取来自客户端的数据。
  • 使用 write 方法发送数据回客户端

4. 关闭连接:

  • 在适当的时候关闭 QTcpSocket

示例代码可能如下:

class MyServer : public QObject {Q_OBJECT
public:MyServer() {QTcpServer *server = new QTcpServer(this);connect(server, &QTcpServer::newConnection, this, &MyServer::onNewConnection);server->listen(QHostAddress::Any, 1234);}
private slots:void onNewConnection() {QTcpSocket *clientSocket = server->nextPendingConnection();connect(clientSocket, &QTcpSocket::readyRead, this, &MyServer::onReadyRead);// ...}void onReadyRead() {QTcpSocket *clientSocket = qobject_cast<QTcpSocket *>(sender());// 读取数据QByteArray data = clientSocket->readAll();// 处理数据// ...}
};

确保在使用 QTcpServerQTcpSocket 时妥善处理网络错误和异常情况

1.3 QTtcp客户端的关键流程

工程建立,需要在.pro加入网络权限
在这里插入图片描述
创建一个基于 QTcpSocket 的Qt客户端涉及以下步骤:
1. 创建 QTcpSocket 实例:

  • 实例化 QTcpSocket

2. 连接到服务器:

  • 使用 connectToHost 方法连接到服务器的IP地址和端口。

3. 发送数据到服务器:

  • 使用 write 方法发送数据。

4. 接收来自服务器的数据:

  • readyRead 信号连接一个槽函数来接收数据。

5. 关闭连接:

  • 关闭 QTcpSocket 连接。

示例代码如下:

class MyClient : public QObject {Q_OBJECT
public:MyClient() {QTcpSocket *client = new QTcpSocket(this);connect(client , &QTcpSocket::readyRead, this, &MyClient::onReadyRead);client->connectToHost("server_address", 1234);}
private slots:void onReadyRead() {QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());QByteArray data = socket->readAll();// 处理接收到的数据// ...}
};

这个客户端尝试连接到指定的服务器地址和端口,然后等待和处理来自服务器的数据。记得根据需要管理和处理网络错误和异常情况。

1.4 UI界面的设计

TCP服务端UI界面设计:
在这里插入图片描述
TCP客户端UI界面设计:
在这里插入图片描述

1.5 TCP协议理论知识

  以下内容自行阅读和消化,主要在面试之前类似八股文问答,实际编程我们不需要关系这么多, QTcpSocket类底下的API已经做好所有的封装。
  TCP(传输控制协议)是一种广泛使用的网络通信协议,设计用于在网络中的计算机之间可靠地传输数据。它是互联网协议套件的核心部分,通常与IP(互联网协议)一起使用,合称为TCP/IP。

以下是TCP协议的一些基本特点:
  1. 面向连接: 在数据传输之前,TCP 需要在发送方和接收方之间建立一个连接。这包括三次握手过
程,确保两端都准备好进行数据传输。
  2. 可靠传输: TCP 提供可靠的数据传输服务,这意味着它保证数据包准确无误地到达目的地。如果发
生数据丢失或错误,TCP 会重新发送数据包。
  3. 顺序控制: TCP 保证数据包的传输顺序。即使数据包在网络中的传输顺序被打乱,接收方也能按照正确的顺序重组这些数据。
  4. 流量控制: TCP 使用窗口机制来控制发送方的数据传输速率,以防止网络过载。这有助于防止接收
方被发送方发送的数据所淹没。
  5. 拥塞控制: TCP 还包括拥塞控制机制,用来检测并防止网络拥塞。当网络拥塞发生时,TCP 会减少
其数据传输速率。
  6. 数据分段: 大块的数据在发送前会被分割成更小的段,以便于传输。这些段会被独立发送并在接收
端重新组装。
  7. 确认和重传: 接收方对成功接收的数据包发送确认(ACK)信号。如果发送方没有收到确认,它会
重传丢失的数据包。
  8. 终止连接: 数据传输完成后,TCP 连接需要被正常关闭,这通常涉及到四次挥手过程。
  TCP 适用于需要高可靠性的应用,如网页浏览、文件传输、电子邮件等。然而,由于它的这些特性,TCP在处理速度上可能不如其他协议(如UDP)那么快速。TCP协议中的三次握手和四次挥手是建立和终止连接的重要过程。

三次握手(建立连接)
三次握手的主要目的是在两台设备之间建立一个可靠的连接。它包括以下步骤:
  1. SYN: 客户端向服务器发送一个SYN(同步序列编号)报文来开始一个新的连接。此时,客户端进
入SYN-SENT状态。
  2. SYN-ACK: 服务器接收到SYN报文后,回复一个SYN-ACK(同步和确认)报文。此时服务器进入SYN-RECEIVED状态。
  3. ACK: 客户端接收到SYN-ACK后,发送一个ACK(确认)报文作为回应,并进入ESTABLISHED(已建立)状态。服务器在收到这个ACK报文后,也进入ESTABLISHED状态。这标志着连接已经建立。
在这里插入图片描述
四次挥手(断开连接)
四次挥手的目的是终止已经建立的连接。这个过程包括以下步骤:
  1. FIN: 当通信的一方完成数据发送任务后,它会发送一个FIN(结束)报文来关闭连接。发送完FIN报文后,该方进入FIN-WAIT-1状态。
  2. ACK: 另一方接收到FIN报文后,发送一个ACK报文作为回应,并进入CLOSE-WAIT状态。发送FIN报文的一方在收到ACK后,进入FIN-WAIT-2状态。
  3. FIN: 在等待一段时间并完成所有数据的发送后,CLOSE-WAIT状态的一方也发送一个FIN报文来请求关闭连接。
  4. ACK: 最初发送FIN报文的一方在收到这个FIN报文后,发送一个ACK报文作为最后的确认,并进入TIME-WAIT状态。经过一段时间后,确保对方接收到了最后的ACK报文,该方最终关闭连接。
在这里插入图片描述
在这两个过程中,三次握手主要确保双方都准备好进行通信,而四次挥手则确保双方都已经完成通信并同意关闭连接。

1.6 Socket网络通信理论

  Socket 不是一个协议,而是一种编程接口(API)或机制,用于在网络中实现通信。Socket 通常在应用层和传输层之间提供一个端点,使得应用程序可以通过网络发送和接收数据。它支持多种协议,主要是TCP 和 UDP。

以下是 Socket 的一些基本特点:

  • 类型: 有两种主要类型的 Sockets —— TCP Socket(面向连接,可靠)和 UDP Socket(无连接,不可靠)。
  • 应用: 在各种网络应用中广泛使用,如网页服务器、聊天应用、在线游戏等。
  • 编程语言支持: 大多数现代编程语言如 Python, Java, C++, 等都提供 Socket 编程的支持。
  • 功能: 提供了创建网络连接、监听传入的连接、发送和接收数据等功能。
  • QT: 在QT组件中,QTcpSocket用来管理和实现TCP Socket通信,QUdpSocket用来管理和实现UDP Socket通信

总之,Socket 是实现网络通信的基础工具之一,它抽象化了网络层的复杂性,为开发者提供了一种相对简单的方式来建立和管理网络连接

2 网络通信核心代码

  QTcpServer 是 Qt 网络模块的一部分,用于构建 TCP 服务器。它提供了一种机制来异步监听来自客户端的连接。一旦接受了一个连接,服务器就可以与客户端进行数据交换。

2.1 TCP服务端连接的核心代码

  在类定义时,将服务器对象定义为成员变量,供槽函数调用,在构造函数中对服务器对象进行实例化。

//类定义
QTcpServer *server;//构造函数内
//1. 创建服务器对象
server = new QTcpServer(this);

启动监听槽函数
  在监听槽函数中,启动监听,在有新的客户端接入时会发出 newConnection 信号,因此通过绑定信号与槽可检测客户端接入。

//监听按钮槽函数 启动监听
void Widget::on_btnListen_clicked()
{//设置服务器IP地址和端口号变量 启动监听QHostAddress addr("192.168.1.106");quint16 port = 8888;//2. 启动监听bool ret = server->listen(addr, port); //自动获取IP地址if(ret == false)return;//绑定监听客户端接入的信号与槽函数connect(server, SIGNAL(newConnection()), this, SLOT(on_newClient_connect()));
}

客户端成功接入槽函数
  在有客户端成功接入时,通过 nextPendingConnection 方法获取客户端对象,将客户端的IP地址与端口号分别打印在数据接收框内,同时绑定数据接收函数(当有数据达到时,会触发 readyRead() 信号)

//检测客户端接入槽函数 对应newConnection信号
void Widget::on_newClient_connect()
{//判断是否有新的客户端接入if(server->hasPendingConnections()){//获取客户端对象QTcpSocket *tcpSocket = server->nextPendingConnection();//打印客户端IP地址与端口号qDebug() << "client addr: " << tcpSocket->peerAddress().toString();qDebug() << "client port: " << tcpSocket->peerPort();//接收消息框显示客户端IP地址与端口号ui->textEdit_Rev->append("addr: " + tcpSocket->peerAddress().toString());ui->textEdit_Rev->append("port: " +QString::number(tcpSocket->peerPort()));//绑定接收readyRead信号与槽函数connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(on_readyRead_handler()));//将新接入的客户端端口加入选项框ui->comboBox_child->addItem(QString::number(tcpSocket->peerPort()));ui->comboBox_child->setCurrentText(QString::number(tcpSocket->peerPort()));}
}

2.2 TCP服务端的数据通信核心代码

  在上述绑定信号接收槽函数后,当接收到数据后,会自动触发数据接收槽函数对数据进行处理,其中数据接收槽函数如下:

//接收数据槽函数
void Widget::on_readyRead_handler()
{//因为数据由客户端的QTcpSocket进行发送,以此需要通过 `qobject_cast<>(sender())` 来获取信号发送方//模仿 串口调试助手中的on_command_button_clicked 获取信号发送者QTcpSocket *tcpSocket = qobject_cast<QTcpSocket *>(sender());//接收数据QByteArray revData = tcpSocket->readAll();//将接收到的数据显示到文本框ui->textEdit_Rev->append("client: " + revData);
}

数据发送按钮槽函数

void Widget::on_btnSend_clicked()
{//通过server的findChildren方法获取客户端对象QList<QTcpSocket *> clients = server->findChildren<QTcpSocket*>();//遍历所有子客户端,调用write方法像所有客户端发送消息for(QTcpSocket *temp: clients){temp->write(ui->textEdit_Send->toPlainText().toStdString().c_str());}
}

2.3 TCP客户端的核心代码

客户端的设计与服务端类似,定义客户端对象并在构造函数内实例化对象

QTcpSocket *client;//实例化客户端对象
client = new QTcpSocket(this);

连接按钮槽函数
  在连接按钮槽函数中主要利用 connectToHost 方法将客户端与服务器建立连接,同时绑定 readyRead() 信号与数据接收槽函数

//连接按钮槽函数
void Widget::on_btnConnect_clicked()
{//获取行编辑器内IP地址与端口号QString addr(ui->lineEdit_addr->text());quint16 port = ui->lineEdit_port->text().toInt();//请求连接服务器client->connectToHost(addr, port);//绑定接收readyRead信号与槽函数connect(client, SIGNAL(readyRead()), this, SLOT(on_readyRead_handler()));
}

数据接收槽函数

//接收数据槽函数
void Widget::on_readyRead_handler()
{//获取数据 并添加至文本框QByteArray revData = client->readAll();ui->textEdit_rev->append("server: " + revData);
}

数据发送按钮槽函数

void Widget::on_btnSend_clicked()
{//发送文本框内数据QByteArray sendData = ui->textEdit_send->toPlainText().toUtf8();client->write(sendData);
}

3 TCP服务端项目功能优化

3.1 自动刷新IP地址

  为了程序自主检测系统上可用的IP地址,为了实现自主刷新可用IP地址并添加至选项框,在构造函数中采用QNetworkInterface 下的 allAddresses() 来检测可用的IP地址,并添加至选项框,同时在启动监听时读取选项框当前IP地址数据即可。

//自动扫描IP地址 存放在列表中
QList<QHostAddress> addrList = QNetworkInterface::allAddresses();
for(QHostAddress addr :  addrList)
{
//IP地址协议为IPv4协议if(addr.protocol() == QAbstractSocket::IPv4Protocol){
//将网络添加至选项框ui->comboBox_Addr->addItem(addr.toString());}
}

3.2 服务器向不同客户端发数据

  通过自定义控件、左键点击选项框触发事件获取当前接口的客户端的端口号,并添置至选项框,最后通过不同的端口号向不同的客户端发送数据。自定义myComboBox类,并继承于QComboBox,同时在UI界面将IP地址选项框提升为自定义控件

1. 自定义类自定义继承实现:
在这里插入图片描述
在这里插入图片描述

2. 重写鼠标左键事件,触发事件发生自定义信号,最后将事件重新传回原路径执行

//声明
protected:void mousePressEvent(QMouseEvent *e) override;
signals:void on_ComboBox_clicked();

槽函数的实现:

void myComboBox::mousePressEvent(QMouseEvent *e)
{if(e->button() == Qt::LeftButton)emit on_ComboBox_clicked();QComboBox::mousePressEvent(e);
}

3. 绑定信号与槽,该自定义信号供主页面程序检测,若检测到该信号则跳转到相应的槽函数进行端口号的刷新。

//绑定信号与槽,检测comboBox按下,扫描已接入的客户端
connect(ui->comboBox_child, &myComboBox::on_ComboBox_clicked, this, &Widget::on_refresh_comboBox);

选项框按下槽函数:刷新不同客户端的端口号

//刷新不同客户端接入的端口号
void Widget::on_refresh_comboBox()
{ui->comboBox_child->clear();//通过server获取客户单对象QList<QTcpSocket *> clients = server->findChildren<QTcpSocket*>();for(QTcpSocket *temp: clients){//客户端不为空 且端口号不为0 加入选项框ui->comboBox_child->addItem(QString::number(temp->peerPort()));}ui->comboBox_child->addItem("all");
}

4. UI界面的控件提升:
在这里插入图片描述

5. 优化发送函数,根据个人选项,向不同的客户端发送数据,根据用户选择 找出具体客户端进行数据通信, 通过端口查找下标确定

//数据发送槽函数
void Widget::on_btnSend_clicked()
{//通过server获取客户单对象QList<QTcpSocket *> clients = server->findChildren<QTcpSocket*>();if(clients.isEmpty())return;//选择特定端口发送if(ui->comboBox_child->currentText() != "all"){//根据用户选择 找出具体客户端进行数据通信, 通过端口查找下标确定QString currentName = ui->comboBox_child->currentText();for(QTcpSocket *temp : clients){if(QString::number(temp->peerPort()) == currentName){temp->write(ui->textEdit_Send->toPlainText().toStdString().c_str());}}}//所有端口发送else{//遍历所有子客户端,调用write方法像所有客户端发送消息for(QTcpSocket *temp: clients){temp->write(ui->textEdit_Send->toPlainText().toStdString().c_str());}}//光标移至末尾ui->textEdit_Rev->moveCursor(QTextCursor::End);ui->textEdit_Rev->ensureCursorVisible();
}

3.3 TextEdit设置特定位置文字颜色

  为了设置 textEdit 中特定位置的文字颜色,需要光标级别的设置。通过 textCursor() 方法来获取当前的光标位置, 继而通过 cursor 下的 setCharFormat 来实现字符格式。

其中设置的函数原型与嵌套关系如下:

QTextCursor:		QTextEdit::textCursor() const
QTextCursor:      	void QTextCursor::setCharFormat(const QTextCharFormat &format)  //方法
QTextCharFormat:	void setForeground(const QBrush &brush)                         //方法
QBrush:           	QBrush(const QColor &color, const QPixmap &pixmap)              //构造函数
QColor:           	QColor(const QColor &color)   									//构造函数

将设置特定位置文本颜色功能封装成函数,其参数分别为字体颜色和待显示的文本

void Widget::setInsertColor(Qt::GlobalColor color, QString str)
{//获取当前光标位置QTextCursor cursor = ui->textEdit_rev->textCursor();QTextCharFormat format;//配置颜色format.setForeground(QColor(color));cursor.setCharFormat(format);cursor.insertText(str + "\n");
}

3.4 客户端断开检测

当客户端主动断开后,服务器会收到一个 disconnected() 信号,通过绑定信号与槽实现对客户端断开连接的检测

//绑定disconnect断开连接信号
connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(on_disconnected()));

客户端断开连接槽函数: 检测到客户端断开后,主要在文本框内进行提示,在端口选项框中移除该客户端的端口号,并将客户端进行删除,

//客户端断开连接信号槽函数
void Widget::on_disconnected()
{//通过 qobject_cast 获取信号发送者QTcpSocket *tcpSocket = qobject_cast<QTcpSocket *>(sender());ui->textEdit_Rev->append("client quit!");//通过内容找到选项框索引,并在选项框中移除int tempIdx;tempIdx = ui->comboBox_child->findText(QString::number(tcpSocket->peerPort()));ui->comboBox_child->removeItem(tempIdx);//删除客户端tcpSocket->deleteLater();//判断是否还存在连接着的客户端if(server->findChildren<QTcpSocket*>().count() == 0)ui->btnSend->setEnabled(false);
}

3.5 停止监听的实现

停止监听槽函数主要通过服务器的 findChildren<>()查找所有接入的客户端并遍历删除即可,同时关闭服务器

//停止监听槽函数
void Widget::on_btnStopListen_clicked()
{//获取所有客户端QList<QTcpSocket *> clients = server->findChildren<QTcpSocket*>();for(QTcpSocket *temp: clients){temp->close();}server->close();
}

4 TCP客户端项目开发及优化

4.1 检测连接状态

  客户端在成功接入服务器后会发出 connected() 信号,接入失败会发出 error() 信号。但实际接入失败发出 error() 信号需要一定的时间,因此通过定时器计算接入的时间,若接入时间大于3秒则认为连接超时。

构造函数内定时器的设置

//设置定时器 检测连接超时
timer = new QTimer(this);
timer->setSingleShot(true); //触发一次
timer->setInterval(3000);   //设置连接超时时间
//启动定时器
timer->start();

绑定信号与槽

//成功连接发出connected信号
connect(client, SIGNAL(connected()), this, SLOT(on_connected()));
//绑定定时器溢出槽函数
connect(timer, SIGNAL(timeout()), this, SLOT(on_timer_out()));

成功接入

连接成功进入 connected() 信号的槽函数

void Widget::on_connected()
{//连接成功 关闭定时器timer->stop();//文本框提示连接成功ui->textEdit_rev->append("连接成功");//设置控件属性ui->btnDisconnect->setEnabled(true);ui->btnSend->setEnabled(true);ui->lineEdit_addr->setEnabled(false);ui->lineEdit_port->setEnabled(false);ui->btnConnect->setEnabled(false);this->setEnabled(true);//光标移至末尾ui->textEdit_rev->moveCursor(QTextCursor::End);ui->textEdit_rev->ensureCursorVisible();
}

连接超时

当定时器超过3秒溢出后则认为是连接超时(失败),中止本次连接

void Widget::on_timer_out()
{ui->textEdit_rev->append("连接超时");client->abort();			//中止客户端连接this->setEnabled(true);on_btnDisconnect_clicked(); //手动调用断开连接
}

4.2 其他细节功能

文本框特定颜色分区:同TCP服务器功能的3.3

//设置插入数据的颜色
void Widget::setInsertColor(Qt::GlobalColor color, QString str)
{//获取当前光标位置QTextCursor cursor = ui->textEdit_rev->textCursor();QTextCharFormat format;//配置文本框颜色format.setForeground(QColor(color));cursor.setCharFormat(format);cursor.insertText(str + "\n");
}

设置控件失能与使能:利用 setEnabled 方法可设置控件的有效性,其中参数为 true 时表使能控件,false 失能控件

//设置控件属性
ui->btnDisconnect->setEnabled(false);
ui->btnSend->setEnabled(false);

文本框显示最后一行(即最新的内容)

//光标移至末尾
ui->textEdit_rev->moveCursor(QTextCursor::End);
ui->textEdit_rev->ensureCursorVisible();

5 项目总结

  • TCPServer类关于监听,连接,发送,接收的API
  • TCPServer在网络通信中常用的信号
  • TCPScoket在QT实现Socket常用的API
  • TCPScoket在QT实现Socket常用的信号
  • EditText的内容读取方法,内容写入方法,在特定行写入特点颜色的方法

TCPServerTCPScoketEditText 的信息整合到一个表格中:

类别功能API方法描述
TCPServer监听bool listen(const QHostAddress &address, quint16 port )在指定的 IP 地址和端口上开始监听传入的连接
连接void close()停止服务器监听传入的连接
QTcpSocket *nextPendingConnection()返回下一个待处理连接的QTcpSocket 指针
TCPServer信号newConnection()当有新连接时发出
TCPScoket连接void connectToHost(const QString &host, quint16 port)连接到指定的主机和端口
发送qint64 write(const QByteArray &data)向连接的套接字写入数据
接收QByteArray readAll()读取可用的所有数据
断开void disconnectFromHost()断开与主机的连接
TCPScoket信号connected()成功连接到主机时发出
disconnected()从主机断开连接时发出
readyRead()当有可读数据时发出
bytesWritten(qint64 bytes)成功写入数据时发出
EditText读取内容String getText()获取 EditText 的内容
写入内容void setText(String text)设置 EditText 的内容
使用光标改变文本颜色void changeTextColor(int start, int end, int color)使用光标(cursor)改变EditText 中从 start 位置到 end 位置的文本颜色为color

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

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

相关文章

如何使用Pycharm中的image模块以及导入打开图片(属性)

在学习pytorch深度学习的过程中&#xff0c;通常会使用到大量的数据集&#xff0c;包括训练集和测试集。 以下是pytorch加载数据集的流程&#xff1a; 在notebook中使用help方法查看Dataset类的功能以及操作&#xff1a; 使用dataset需要继承Dataset父类 重写__getitem__方法和…

测试开发【Mock平台】12基础:拦截器服务实现(三) 接口匹配逻辑

【Mock平台】为系列测试开发教程&#xff0c;从0到1编码带你一步步使用Spring Boot 和 Antd React框架完成搭建一个测试工具平台&#xff0c;希望作为一个实战项目对各位的测试开发学习之路有帮助&#xff0c;关注公众号发送“mock”获取github项目源码地址&#xff0c;大奇一个…

如何确定分库还是 分表?

分库分表 分库分表使用的场景不一样&#xff1a; 分表因为数据量比较大&#xff0c;导致事务执行缓慢&#xff1b;分库是因为单库的性能无法满足要求。 分片策略 1、垂直拆分 水平拆分 3 范围分片&#xff08;range&#xff09; 垂直水平拆分 4 如何解决数据查询问题&a…

数据可视化利器:五款必备工具推荐

在数据可视化的世界里&#xff0c;工具的选择往往决定了工作的效率和效果。作为一名资深的数据可视化用户&#xff0c;我尝试并使用了众多的数据可视化工具。今天&#xff0c;我想向大家推荐五款我认为最好用、最实用的数据可视化工具。 1. 山海鲸可视化 山海鲸可视化以其强大…

ansible剧本中的角色

1 roles角色 1.1 roles角色的作用&#xff1f; 可以把playbook剧本里的各个play看作为一个角色&#xff0c;将各个角色打的tasks任务、vars变量、template模版和copy、script模块使用的相关文件等内容放置在指定角色的目录里统一管理&#xff0c;在需要的时候可在playbook中使…

删除挖矿木马yaya病毒

文章目录 挖矿木马&#xff08;yaya&#xff09; 挖矿木马&#xff08;yaya&#xff09; 参考文档&#xff1a;解决挖矿 将隐藏内核模块可见 kill -63 0 查看隐藏的内核模块nonono cat /proc/modules |head -n 10 删除内核模块 rmmod nonono 结束挖矿进程 使用top命令查…

浅谈js事件机制

事件是什么&#xff1f;事件模型&#xff1f; 原始事件模型&#xff08;DOM0级&#xff09; HTML代码中指定属性值&#xff1a;在js代码中指定属性值&#xff1a;优点&#xff1a;缺点&#xff1a; IE 事件模型DOM2事件模型 对事件循环的理解 宏任务&#xff08;Macrotasks&…

PWM功能介绍 和配置

泰山派默认提供了3组PWM的GPIO &#xff0c; 为了检测PWM的输出&#xff0c;我们可以配合逻辑分析仪来查看效果&#xff0c;或者搭配STC8的LED灯 PWM 测试 列举所有的PWM设备&#xff1a; # 查找所有有pwm名称的文件 find / -name "pwm" # pwm4: pwmfe6e0000 edp屏幕…

短剧弯道超车拿下2024开年第一爆,谁在打造新的暴富神话?

2024开年第一爆竟然被一部土味小短剧拿下了。 春节期间,无论是刷抖音还是微博,都能看到《我在八零年代当后妈》这部微短剧,女大学生穿越到八十年代当后妈,集狗血、土味、爽点于一体,让人相当上头。 春节档长视频平台爱、优、腾都拿出了当家大戏抢夺剧集市场,没想到被短…

人机工程学和人机交互理论:智能座舱设计

hello家人们...本人熟悉PS、Xd、Ai、Sketch、Figma、墨刀、即时设计、mastergo、Pixso等行业设计软件以及前端开发等技能&#xff0c;拥有10年的UI经验&#xff0c;我们可以通过关注评论私信交流以帮助到您解决UI工作中的烦恼&#xff01;谢谢 人机工程学与人机交互理论&#x…

Gin框架: HTML模板渲染之配置与语法详解

Gin的HTML模板配置 1 &#xff09;单一目录的配置 配置模板目录&#xff0c;在与main.go同级下, 新建目录&#xff0c;下面二选一&#xff0c;仅作举例, 这里选择 tpls templatestpls 在 tpls 目录下新建 news.html <!-- 最简单的 --> <h1>News Page</h1>&l…

matplotlib图例使用案例1.1:在不同行或列的图例上添加title

我们将图例进行行显示或者列显示后&#xff0c;只能想继续赋予不同行或者列不同的title来进行分类。比较简单的方式&#xff0c;就是通过ax.annotate方法添加标签&#xff0c;这样方法复用率比较低&#xff0c;每次使用都要微调ax.annotate的显示位置。比较方便的方法是在案例1…