【Qt网络编程】Tcp多线程并发服务器和客户端通信

目录

一、编写思路

1、服务器

(1)总体思路widget.c(主线程)

(2)详细流程widget.c(主线程)

(1)总体思路chat_thread.c(处理聊天逻辑线程)

(2)详细流程chat_thread.h(处理聊天逻辑线程)

2、客户端

(1)总体思路widget.c(主线程)

(2)详细思路widget.c(主线程)

(1)总体思路chat_thread.c(处理聊天逻辑线程)

(2)详细流程chat_thread.c(处理聊天逻辑线程)

二、实现效果

1、服务器

2、客户端

 

如需咨询请添加个人微信:a15135158368

欢迎叨扰,多多交流

完整代码请自行下载资源

一、编写思路

1、服务器

(1)总体思路widget.c(主线程)

  1. 初始化界面

    创建窗口、输入框、按钮等基本UI元素。

  2. 创建服务器对象

    实现 My_tcp_server 并监听客户端连接。

  3. 处理新客户端连接

    当有新客户端连接时,创建新的 Chat_thread 线程来处理通信。

  4. 绑定信号槽

    确保主线程与客户端处理线程间的信号槽连接,使用 Qt::QueuedConnection 处理跨线程通信。

  5. 处理消息传递

    接收和发送消息,并在界面上更新显示。

  6. 服务器启动与关闭

    通过按钮控制服务器的启动和关闭,管理所有客户端线程的安全退出。

(2)详细流程widget.c(主线程)

  1. 创建 Qt 界面及设置窗口属性: 首先通过 ui->setupUi(this); 来初始化用户界面,并设置窗口标题、大小等基本属性。这是 Qt 项目的常见步骤,通过 .ui 文件生成的类进行界面管理。

    Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)  // 初始化UI对象
    {ui->setupUi(this);  // 设置UI界面this->setWindowTitle("--服务器--");  // 设置窗口标题this->resize(1024, 960);  // 设置窗口大小
    ​ui->le_ip->setText("127.0.0.1");ui->le_port->setText("9999");
    }

  2. 初始化服务器对象 My_tcp_server 并处理客户端连接

    • 创建 tcp_server 对象以处理客户端的连接。

    • 使用 connect 函数连接 tcp_servernew_descriptor 信号和匿名槽函数,确保一旦有新客户端连接,便创建一个 Chat_thread 来处理该客户端。

    this->tcp_server = new My_tcp_server(this);
    ​
    connect(tcp_server, &My_tcp_server::new_descriptor, this, [=](qintptr socketDescriptor){QMessageBox::information(this, "提示", "新的客户端连接!", QMessageBox::Ok, QMessageBox::Information);
    ​ui->btn_send->setEnabled(true);  // 启用“发送消息”按钮
    ​// 创建新线程处理客户端Chat_thread *chat_thread = new Chat_thread(socketDescriptor);chat_thread->moveToThread(chat_thread);  // 将线程和对象绑定到同一线程,防止冲突
    ​thread_list.append(chat_thread);// 启动线程处理客户端通信chat_thread->start();
    });
  3. 管理客户端线程 Chat_thread

    • 每当有新客户端连接时,创建一个 Chat_thread 并启动它处理客户端通信。通过 moveToThreadChat_thread 的执行线程与该对象保持一致,避免跨线程冲突。

    • 使用 connect 绑定线程中的信号(如连接断开、接收消息)和主界面槽函数,确保客户端状态能够正确显示。

    Chat_thread *chat_thread = new Chat_thread(socketDescriptor);
    chat_thread->moveToThread(chat_thread);  // 将线程与对象绑定在同一线程
    ​
    thread_list.append(chat_thread);
    ​
    // 连接信号和槽
    connect(chat_thread, &Chat_thread::break_connect, this, [=](){ui->te_receive->append(currentTime + "\n【状态】客户端断开连接...");ui->btn_send->setEnabled(false);  // 禁用“发送消息”按钮
    });
    ​
    connect(chat_thread, &Chat_thread::recv_info, this, [=](QString data){currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");ui->te_receive->append(currentTime + "form client\n    【数据】 " + data);  // 在文本框中显示消息
    });
    ​
    chat_thread->start();  // 启动线程
  4. 处理启动和关闭服务器的按钮事件

    • on_btn_connect_clicked() 处理连接按钮点击事件,启动或关闭服务器。

    • 启动时,检查 IP 地址和端口的有效性,成功后开始监听客户端连接。

    • 关闭服务器时,停止监听,并确保所有已连接客户端线程安全退出。

    void Widget::on_btn_connect_clicked()
    {if (!is_server_running){// 启动服务器QString ip_address = ui->le_ip->text().trimmed();QString port_text = ui->le_port->text().trimmed();if (!tcp_server->listen(QHostAddress(ip_address), port_text.toUInt())){QMessageBox::warning(this, "warning", "服务器监听失败");return;}is_server_running = true;ui->btn_connect->setText("关闭服务器");ui->te_receive->append(currentTime + "\n【状态】服务器开始监听...");}else{// 停止服务器并关闭所有客户端线程tcp_server->close();for (Chat_thread *thread : qAsConst(thread_list)){thread->exit();thread->wait();thread->deleteLater();}thread_list.clear();is_server_running = false;ui->btn_connect->setText("创建服务器");ui->te_receive->append(currentTime + "\n【状态】服务器已停止监听...");}}
  1. 处理发送消息按钮的点击事件

    • 当点击“发送消息”按钮时,触发 send_request 信号,利用信号槽机制将输入的消息发送给客户端。需要确保主线程和子线程的信号槽通信是异步进行的(通过 Qt::QueuedConnection)。

    void Widget::on_btn_send_clicked()
    {QString data = ui->te_send->toPlainText().toUtf8();emit send_request(data);  // 发出 send_request 信号
    }

  2. 服务器监听客户端的状态和信息传递

    • 服务器通过 recv_infosend_info 信号接收客户端消息并在界面上显示。

    • 在客户端连接成功或断开时,更新界面显示状态。

    connect(chat_thread, &Chat_thread::recv_info, this, [=](QString data){currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");ui->te_receive->append(currentTime + "form client\n    【数据】 " + data);  // 显示接收的客户端数据
    });
    ​
    connect(chat_thread, &Chat_thread::send_info, this, [=](QString data){currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");ui->te_receive->append(currentTime + "to client\n    【数据】 " + data);  // 显示发送给客户端的数据
    });

(1)总体思路chat_thread.c(处理聊天逻辑线程)

  1. 构造函数

    初始化 socketDescriptor 以供后续线程使用。

  2. 线程启动与套接字初始化

    run() 函数中创建 QTcpSocket,并关联 socketDescriptor

  3. 获取客户端信息

    通过 peerAddress()peerPort() 获取客户端 IP 地址和端口号,并进行错误处理。

  4. 信号槽机制连接

    将套接字状态、接收数据、错误处理等信号连接到相应的槽函数。

  5. 处理连接状态变化

    通过 handler_client_changed() 处理客户端的连接或断开,并发出相应的信号。

  6. 处理接收消息

    receive_message() 函数中处理客户端发送的消息,并发出信号 recv_info

  7. 发送消息

    send_message() 函数中,检查连接状态并发送消息,发出 send_info 信号。

  8. 错误处理

    处理客户端连接中的错误,删除资源并退出线程。

(2)详细流程chat_thread.h(处理聊天逻辑线程)

  1. 构造函数初始化

    • Chat_thread 构造函数接受一个 socketDescriptor 参数,并将其存储为类的成员变量,以供 run() 函数中使用。注意,QTcpSocket 对象将在 run() 函数中创建,以确保在新线程中创建并使用。

    Chat_thread::Chat_thread(qintptr socketDescriptor, QObject *parent): QThread{parent}, socketDescriptor(socketDescriptor)
    {// socketDescriptor 存储为成员变量
    }
  2. 线程启动和套接字初始化

    • run() 函数中创建 QTcpSocket 对象,并通过 setSocketDescriptor() 将套接字描述符与 QTcpSocket 关联。这允许线程使用此套接字与客户端通信。

    • 如果套接字初始化失败,进行错误处理并返回。

    void Chat_thread::run()
    {// 创建 QTcpSocket 对象,用于处理与客户端的通信this->socket = new QTcpSocket();
    ​// 将套接字描述符与 QTcpSocket 关联if (!socket->setSocketDescriptor(socketDescriptor)){qDebug() << "Error: Failed to get new socketDescriptor.";return;}
    ​// 错误处理:检查是否成功获取客户端连接if (socket == nullptr){qDebug() << "Error: Failed to get new client connection.";return;  // 如果获取失败,直接返回}
    }
  3. 获取客户端信息

    • 在成功创建套接字后,获取客户端的 IP 地址和端口号。

    • 如果获取失败,进行错误处理并断开连接。

    // 获取客户端的IP地址和端口号
    QString ip_addr = socket->peerAddress().toString();
    quint16 port = socket->peerPort();
    ​
    // 错误处理:检查是否成功获取IP地址和端口号
    if (ip_addr.isEmpty() || port == 0)
    {qDebug() << "Error: Failed to get client's IP address or port.";socket->disconnectFromHost();  // 断开连接socket->deleteLater();  // 删除客户端套接字对象return;  // 如果获取失败,直接返回
    }
  4. 信号槽机制的连接

    • 连接套接字的状态改变信号 stateChanged 到槽函数 handler_client_changed,以便监控客户端连接状态的变化。

    • 连接 QTcpSocketreadyRead 信号到 receive_message 槽函数,用于处理接收数据。

    • 处理套接字错误时,连接 errorOccurred 信号到 handle_socket_error 槽函数。

    // 处理连接状态变化的槽函数
    connect(socket, &QTcpSocket::stateChanged, this, &Chat_thread::handler_client_changed);
    ​
    // 错误处理:处理客户端的异常断开情况
    connect(socket, &QTcpSocket::errorOccurred, this, &Chat_thread::handle_socket_error);
    ​
    // 处理接收数据的槽函数
    connect(socket, &QTcpSocket::readyRead, this, &Chat_thread::receive_message);

  5. 处理客户端连接状态变化

    • handler_client_changed() 槽函数中,根据客户端的连接状态(如断开、已连接)做相应处理并发出信号,通知其他部分更新状态。

    void Chat_thread::handler_client_changed(QAbstractSocket::SocketState socket_state)
    {socket = (QTcpSocket*)sender();  // 获取发信的客户端套接字if(!socket) return;
    ​switch (socket_state){case QAbstractSocket::UnconnectedState:  // 客户端断开连接emit break_connect();break;
    ​case QAbstractSocket::ConnectedState:  // 客户端已连接emit complete_connect();break;
    ​default:break;}
    }
  6. 接收消息的处理

    • receive_message() 槽函数中,通过 socket->readAll() 读取客户端发送的所有数据,并发出信号 recv_info 通知上层处理。

    void Chat_thread::receive_message()
    {if (socket){QString data = socket->readAll();  // 读取客户端发送的所有数据emit recv_info(data);  // 发出信号,通知收到消息}
    }
  7. 发送消息

    • send_message() 函数中,检查客户端是否处于连接状态,如果是则发送消息,否则输出警告信息。

    • 发送完成后,发出 send_info 信号。

    void Chat_thread::send_message(QString data)
    {if (socket->state() == QAbstractSocket::ConnectedState){socket->write(data.toUtf8());  // 发送数据}else{qDebug() << "warning:   客户端未连接,无法发送消息";  // 输出警告}
    ​emit send_info(data);  // 发出信号,通知发送消息
    }
  8. 错误处理

    • handle_socket_error() 函数中处理 QTcpSocket 的错误。如果出现错误,打印错误信息,并退出线程。

    • 删除套接字对象并退出线程事件循环。

    void Chat_thread::handle_socket_error(QAbstractSocket::SocketError socketError)
    {qDebug() << "Client connection error, error code: " << socketError;
    ​this->exit();  // 退出线程this->wait();  // 等待线程完全退出socket->deleteLater();  // 删除客户端套接字对象
    ​// 停止线程事件循环quit();
    }

2、客户端

(1)总体思路widget.c(主线程)

  1. 初始化界面

    设置窗口属性并初始化用户输入的默认值。

  2. 创建线程和通信任务对象

    实现异步通信,使用 QThread 和自定义 Chat_thread 处理服务器交互。

  3. 信号槽机制的建立

    连接 UI 和工作线程之间的信号槽,确保各操作异步处理。

  4. 线程管理

    在析构函数中确保线程安全退出,释放资源。

  5. 处理连接与断开

    通过按钮触发连接和断开操作,并更新 UI 显示。

  6. 消息传递与显示

    处理消息的发送与接收,并在 UI 界面上更新显示结果。

(2)详细思路widget.c(主线程)

  1. 初始化界面

    • 使用 ui->setupUi(this) 初始化用户界面,并设置窗口标题和窗口大小。

    • 初始化 IP 地址和端口号的默认值。

    Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget), is_connected(false)  // 初始化连接状态为未连接
    {ui->setupUi(this);  // 设置UI界面this->setWindowTitle("-客户端-");  // 设置窗口标题this->resize(1024, 960);  // 设置窗口大小
    ​ui->le_ip->setText("127.0.0.1");  // 设置默认IP地址ui->le_port->setText("8888");  // 设置默认端口号
    }

  2. 创建线程和通信任务对象

    • 创建 QThread 对象以进行异步通信任务。

    • 创建 Chat_thread 对象负责与服务器进行通信操作。

    • 使用 moveToThread 将通信任务对象移到新的线程中执行,并启动该线程。

    // 创建线程对象
    thread = new QThread;
    ​
    // 创建任务对象,负责与服务器的通信
    Chat_thread *worker = new Chat_thread;
    ​
    worker->moveToThread(thread);  // 将任务对象移至线程
    thread->start();  // 启动工作线程

  3. 信号槽机制的建立

    • 使用信号槽连接 UI 和工作线程之间的交互。例如,连接服务器、发送消息、断开连接等操作通过信号槽机制进行。

    • 信号从 UI 线程发出,工作线程的槽函数接收信号并执行相关操作。

    // 信号槽连接:从UI线程发出连接信号,worker线程接收并执行连接操作
    connect(this, &Widget::connect_server, worker, &Chat_thread::start_connected);
    connect(this, &Widget::send_info, worker, &Chat_thread::start_send);
    connect(this, &Widget::quit_connect, worker, &Chat_thread::break_connected);
    ​
    // 连接断开信号槽,worker线程通知UI线程更新UI
    connect(worker, &Chat_thread::connect_cancel, this, &Widget::submit_connect_cancel);
    connect(worker, &Chat_thread::connected, this, &Widget::submit_connect_info);
    connect(worker, &Chat_thread::transfer_recv_info, this, &Widget::submit_recv_info);

  4. 管理线程的生命周期

    • 在析构函数中,确保工作线程在窗口关闭时被正确停止,并释放相关资源。

    • 如果线程正在运行,需要先请求线程退出,然后等待其完全退出后再删除。

    Widget::~Widget()
    {if (thread->isRunning()){thread->quit();  // 请求线程退出thread->wait();  // 等待线程结束}delete worker;  // 删除任务对象delete thread;  // 删除线程对象delete ui;  // 删除UI对象
    }

  5. 处理连接成功或断开连接的槽函数

    • 当客户端成功连接到服务器时,工作线程发出 connected 信号,UI 界面通过槽函数 submit_connect_info() 来更新显示状态,并启用“发送消息”按钮。

    • 断开连接时,UI 界面通过槽函数 submit_connect_cancel() 来禁用“发送消息”按钮,并更新状态显示。

    // 连接成功时的槽函数,更新UI显示信息
    void Widget::submit_connect_info()
    {currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");ui->te_receive->append(currentTime + "\n【状态】已成功连接到服务器");ui->btn_connect->setText("断开服务器");ui->btn_send->setEnabled(true);   // 启用发送按钮is_connected = true;
    }
    ​
    // 断开连接时的槽函数,更新UI显示信息
    void Widget::submit_connect_cancel()
    {is_connected = false;
    }

  6. 处理消息的发送与接收

    • 当用户点击“发送消息”按钮时,获取文本框中的消息,发出 send_info 信号,将消息发送到服务器。

    • 当从服务器接收到消息时,工作线程发出 transfer_recv_info 信号,UI 界面更新显示接收到的消息。

    // 当用户点击发送按钮时,读取输入框中的内容并发送给服务器
    void Widget::on_btn_send_clicked()
    {currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");QString message = ui->te_send->toPlainText().toUtf8();  // 获取用户输入的消息ui->te_receive->append(currentTime + " to server\n    【数据】" + message + "\n");emit send_info(message);  // 发出信号,通知工作线程发送消息
    }
    ​
    // 当接收到服务器发送的消息时,更新UI显示接收到的消息
    void Widget::submit_recv_info(QString message)
    {currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");ui->te_receive->append(currentTime + " form server\n    【数据】" + message + "\n");  // 显示服务器的消息
    }

  7. 处理连接与断开的按钮事件

    • 当点击“连接”按钮时,获取 IP 地址和端口号,检查输入的有效性后发出 connect_server 信号,通知工作线程与服务器建立连接。

    • 当点击“断开服务器”按钮时,发出 quit_connect 信号,通知工作线程断开连接。

    // 当用户点击"连接"按钮时触发该槽函数
    void Widget::on_btn_connect_clicked()
    {if (!is_connected){QString ip_address = ui->le_ip->text().trimmed();QString port_text = ui->le_port->text().trimmed();
    ​QHostAddress address;if (!address.setAddress(ip_address))  // 检查IP地址的有效性{QMessageBox::warning(this, "warning", "无效的IP地址,请重新输入!");return;}
    ​bool ok;unsigned int port = port_text.toUInt(&ok);if (!ok || port == 0 || port > 65535)  // 检查端口号的有效性{QMessageBox::warning(this, "warning", "无效的端口号,请输入1到65535之间的数值!");return;}
    ​emit connect_server(ip_address, port);  // 发出连接服务器的信号}else{currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");ui->te_receive->append(currentTime + "\n【状态】已断开与服务器的连接");ui->btn_send->setEnabled(false);  // 禁用发送按钮is_connected = false;emit quit_connect();  // 发出断开连接的信号}
    }

(1)总体思路chat_thread.c(处理聊天逻辑线程)

  1. 构造函数

    初始化 Chat_thread 对象。

  2. 接收消息

    通过 readyRead 信号槽接收服务器发送的数据,并将其转发给主线程。

  3. 处理连接状态变化

    监控与服务器的连接状态,并打印调试信息。

  4. 断开连接

    关闭套接字连接并释放资源,发出连接断开信号。

  5. 启动连接

    通过指定的 IP 和端口号连接服务器,并处理连接成功、失败、断开、接收数据等事件。

  6. 发送消息

    检查连接状态并发送消息。如果未连接,则发出未连接信号。

(2)详细流程chat_thread.c(处理聊天逻辑线程)

  1. 构造函数

    • 构造函数 Chat_thread::Chat_thread(QObject *parent) 初始化 Chat_thread 对象。在这个阶段不需要任何复杂的逻辑,主要是确保对象正常创建。

    Chat_thread::Chat_thread(QObject *parent): QObject{parent}
    {}

  2. 接收消息处理

    • receive_message() 是一个槽函数,用于接收从服务器发送的数据。当 QTcpSocket 对象有数据可读取时,信号 readyRead 会被触发,调用此槽函数。读取数据后,通过 transfer_recv_info 信号将接收到的消息发送出去。

    void Chat_thread::receive_message()
    {QString message = socket->readAll();  // 从服务器读取数据emit transfer_recv_info(message);  // 发出信号,通知接收到的数据
    }

  3. 处理连接状态变化

    • state_changed() 函数是一个槽函数,用于处理客户端与服务器的连接状态变化。根据不同的 QAbstractSocket::SocketState 枚举值,打印调试信息并处理相应状态的变化。

    void Chat_thread::state_changed(QAbstractSocket::SocketState socketstate)
    {QString stateStr;  // 用于保存状态的字符串switch (socketstate){case QAbstractSocket::UnconnectedState:qDebug()<< "\n【状态】与服务器断开连接...";stateStr = "UnconnectedState";break;
    ​case QAbstractSocket::ConnectedState:stateStr = "ConnectedState";qDebug()<< "【状态】与服务器建立连接...";break;
    ​case QAbstractSocket::HostLookupState:stateStr = "HostLookupState";qDebug()<< "【状态】正在查找主机...";break;
    ​case QAbstractSocket::ConnectingState:stateStr = "ConnectingState";qDebug()<< "【状态】正在连接服务器...";break;
    ​case QAbstractSocket::ClosingState:stateStr = "ClosingState";qDebug()<< "【状态】正在关闭连接...";break;
    ​default:stateStr = "UnknownState";qDebug()<< "未知的错误, 当前状态: " + stateStr;break;}
    }

  4. 断开连接处理

    • break_connected() 用于处理断开与服务器的连接。当套接字连接断开时,关闭并释放资源,并发出 connect_cancel 信号通知主线程。

    void Chat_thread::break_connected()
    {socket->close();  // 关闭套接字socket->deleteLater();  // 延迟删除套接字,释放资源emit connect_cancel();  // 发出连接断开信号
    }

  5. 开始连接服务器

    • start_connected() 用于发起连接服务器的请求。创建 QTcpSocket 对象并尝试连接到指定的 IP 和端口。连接成功、失败、断开、接收数据等事件都会通过信号槽机制进行处理。

    void Chat_thread::start_connected(QString IP, unsigned short PORT)
    {socket = new QTcpSocket;  // 创建套接字对象
    ​socket->connectToHost(QHostAddress(IP), PORT);  // 连接到服务器
    ​// 连接成功时,发送 connected 信号通知主线程上传消息connect(socket, &QTcpSocket::connected, this, &Chat_thread::connected);
    ​// 连接失败时处理connect(socket, &QTcpSocket::errorOccurred, this, [=](QAbstractSocket::SocketError socketError){qDebug() << "连接失败";QMessageBox::critical(nullptr, "连接失败", "连接失败,错误代码:" + QString::number(socketError));});
    ​// 连接断开时处理connect(socket, &QTcpSocket::disconnected, this, &Chat_thread::break_connected);
    ​// 监听数据接收connect(socket, &QTcpSocket::readyRead, this, &Chat_thread::receive_message);
    }

  6. 发送消息

    • start_send() 用于发送消息到服务器。首先检查套接字是否处于连接状态,如果已连接,则发送消息。如果未连接,则发出 not_connected 信号。

    void Chat_thread::start_send(QString message)
    {if (socket && socket->state() == QAbstractSocket::ConnectedState){socket->write(message.toUtf8());  // 发送消息}else{emit not_connected();  // 如果未连接,发出未连接信号}
    }

二、实现效果

1、服务器

2、客户端

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

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

相关文章

运筹说 第125期 | 存储论经典例题讲解1

通过前几期的学习&#xff0c;我们已经学会了存储论的基本概念、确定型存储模型、单周期的随机型存储模型、其他的随机型存储模型以及存储论应用研究中的一些问题。在实际工作中&#xff0c;我们能发现存储论在能源行业中有着许多应用&#xff0c;本期小编选择了其中一些确定型…

PyQt5-折叠面板效果

效果预览 实际效果中带有白色面板,看如下代码 实现代码 import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QFrame, QLabel, QSizePolicy from PyQt5.QtCore import QPropertyAnimation, QEasingCurve, Qtclass CollapsiblePanel(QW…

C#:强大编程语言的多面魅力

C#&#xff1a;强大编程语言的多面魅力 一、C# 语言的特点与优势 &#xff08;一&#xff09;简洁的语法与精心设计 C# 在继承 C 和 C 的强大功能的同时&#xff0c;去掉了一些复杂特性&#xff0c;如宏和多重继承&#xff0c;使得语言更加简洁易懂。C# 是一种面向对象的语言…

openGauss之NestedLoop Join内表 Reuse

一. 前言 openGuass支持在做nestloop的时候&#xff0c;支持通过Materialize的方式将内表缓存到内存中&#xff0c;然后外表的数据内表数据进行碰撞的时候&#xff0c;如果内表已经缓存了数据&#xff0c;那么直接从缓存中直接读取内表的数据&#xff0c;从而实现内部数据Reuse…

基于SSM的在线家用电器销售系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSSMVueMySQL的在线家…

7--SpringBoot-后端开发、原理

配置优先级 SpringBoot 项目当中支持的三类配置文件&#xff1a; application.properties application.yml application.yaml 配置文件优先级排名&#xff08;从高到低&#xff09;&#xff1a; 1. properties配置文件 2. yml配置文件 3. yaml配置文件 在SpringBoot项目当…

MySQL 日志篇:Redo 相关线程

在 MySQL 中&#xff0c;用户线程开启事务更改数据时&#xff0c;系统内部会生成相应的 Redo Record。为了保证事务的持久性&#xff0c;这些 Redo Record 需要以 Redo Log 的形式在事务提交之前写入磁盘 (也称为“落盘”)。 为了提高事务的吞吐率 (单位时间内系统处理的事务数…

JavaSE - 面向对象编程01

01 什么是面向对象编程(oop) 答&#xff1a;就是只关心对象之间的交互&#xff0c;而并不关心任务是怎样具体完成的。例如把一个大象放进冰箱需要几步&#xff1f;如果是面向对象编程只会思考冰箱和大象之间的交互&#xff0c;那么给出的答案就是&#xff1a;把冰箱门打开&…

不可错过的AIGC浪潮:提升效率与竞争力的必备神器

随着人工智能生成内容&#xff08;AIGC&#xff09;技术的迅猛发展&#xff0c;它在提升工作效率和改善生活质量方面展示了巨大的潜力。对职场人来说&#xff0c;了解AIGC如何改变各个行业&#xff0c;并探讨其未来发展中的风险和机遇&#xff0c;将有助于他们更好地利用这项技…

三相可控整流电路 (三相半波,三相桥式)

目录 1. 三相半波整流电路 2. 三相桥式全控整流电路 三相可控整流电路利用三相交流电源&#xff0c;通过可控硅&#xff08;晶闸管&#xff09;将交流电整流为直流电。主要有两种常见类型&#xff1a;三相半波整流电路和三相桥式全控整流电路。 1. 三相半波整流电路 三相半波…

Java数据存储结构——二叉查找树

文章目录 22.1.2二叉查找树22.1.2.1 概述22.1.2.1二叉查找树添加节点22.1.2.2二叉查找树查找节点22.1.2.3 二叉树遍历22.1.2.4 二叉查找树的弊端 22.1.2二叉查找树 22.1.2.1 概述 二叉查找树,又称二叉排序树或者二叉搜索树 二叉查找树的特点&#xff1a; 每一个节点上最多有…

你的绩效是不是常年都是B

原创不易&#xff0c;求赞&#xff0c;求关注&#xff0c;&#x1f64f;&#x1f64f;&#x1f64f;&#x1f64f;&#x1f64f;&#x1f64f;&#x1f64f;&#x1f64f; 目录 原创不易&#xff0c;求赞&#xff0c;求关注&#xff0c;&#x1f64f;&#x1f64f;&#x1f64…

PCL 点云生成DSM图 (高程模型图)

🙋 结果预览 🔗接上篇 Python 点云生成高程模型图(DSM) 一、代码实现 #include <pcl/io/pcd_io.h> //PCD读写类相关的头文件 #include

使用java程序对字符串进行加密

程序功能 程序的功能是对用户输入的字符串&#xff0c;使用常见的三种加密算法&#xff08;MD5、SHA-1 和 SHA-256&#xff09;进行加密&#xff0c;并输出每种算法加密后的结果。 主要步骤包括&#xff1a; 用户通过控制台输入一个字符串。 程序使用 MessageDigest 类&#x…

DFS:深搜+回溯+剪枝实战解决OJ问题

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一 排列、子集问题 1.1 全排列I 1.2 子集I 1.3 找出所有子集的异或总和 1.4 全排列II 1.5 字母大小写全排列 1.6 优美的排列 二 组合问题 2.1 电话号码的数字组合 …

三菱模拟量入门接线与编程详解

当我们学会完基础后。下面就需要学习模拟量,希望小编的文章对读者有所帮助! 什么是模拟量? 模拟量是指一些连续变化的物理量(简单来说就是连续变化的量),在PLC中通常电压信号为0~10V,电流信号为4~20mA。 为什么要使用模拟量? 当我们需要检测如电压、电流、压力、速度、…

【计网】从零开始使用TCP进行socket编程 --- 客户端与服务端的通信实现

阵雨后放晴的天空中&#xff0c; 出现的彩虹很快便会消失。 而人心中的彩虹却永不会消失。 --- 太宰治 《斜阳》--- 从零开始使用TCP进行socket编程 1 TCP与UDP2 TCP服务器类2.1 TCP基础知识2.2 整体框架设计2.3 初始化接口2.4 循环接收接口与服务接口 3 服务端与客户端测试…

JS落叶动画代码分析

秋天到了&#xff0c;秋高气爽的季节。我们来做一个落叶动画吧&#xff01;来迎接秋天的到来 文字可以更换。 1.目录如下 在线演示&#xff1a;点击我在线演示 images两张照片&#xff0c;首先&#xff0c;你得要准备一个vscode编辑器。和一个chorme浏览器或edge浏览器。 …

PyTorch 激活函数及非线性变换详解

激活函数是深度学习模型的重要组成部分&#xff0c;它们引入非线性&#xff0c;从而使模型能够更好地拟合复杂的数据模式。本文将详细介绍激活函数的作用、常见类型、经典应用示例&#xff0c;并比较它们的优缺点。 激活函数的作用 激活函数的主要作用是引入非线性变换&#…

理解高并发

文章目录 1、如何理解高并发2、高并发的关键指标3、高并发系统设计的目标是什么&#xff1f;1_宏观目标2_微观目标1.性能指标2.可用性指标3.可扩展性指标 4、高并发的实践方案有哪些&#xff1f;1_通用的设计方法1.纵向扩展&#xff08;scale-up&#xff09;2.横向扩展&#xf…