【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、客户端


完整代码请到指定链接下载:Qt网络编程-Tcp多线程并发服务器和客户端通信: 【Qt网络编程】Tcp多线程并发服务器和客户端通信

一、编写思路

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.rhkb.cn/news/429980.html

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

相关文章

SQL 多表联查

目录 1. 内联接&#xff08;INNER JOIN&#xff09; 2. 左外联接&#xff08;LEFT JOIN&#xff09; 3. 右外联接&#xff08;RIGHT JOIN&#xff09; 4. 全外联接&#xff08;FULL JOIN&#xff09; 5. 交叉联接&#xff08;CROSS JOIN&#xff09; 6. 自联接&#xff0…

MySQL篇(存储引擎)(持续更新迭代)

目录 一、简介 二、使用存储引擎 1. 建表时指定存储引擎 2. 查询当前数据库支持的存储引擎 三、三种常见存储引擎 1. InnoDB存储引擎 1.1. 简介 1.2. 特点 1.3. 文件格式 1.4. 逻辑存储结构 表空间 段 区 页 行 2. MyISAM存储引擎 2.1. 简介 2.2. 特点 2.3. …

【Linux】入门【更详细,带实操】

Linux全套讲解系列&#xff0c;参考视频-B站韩顺平&#xff0c;本文的讲解更为详细 目录 1、课程内容 2、应用领域 3、概述 4、 Linux和Unix 5、VMware15.5和CentOS7.6安装 6、网络连接三种方式 7、虚拟机克隆 8、虚拟机快照 9、虚拟机迁移删除 10、vmtools 11、目录…

Gartner:中国企业利用GenAI提高生产力的三大策略

作者&#xff1a;Gartner高级首席分析师 雷丝、Gartner 研究总监 闫斌、Gartner高级研究总监 张桐 随着生成式人工智能&#xff08;GenAI&#xff09;风靡全球&#xff0c;大多数企业都希望利用人工智能&#xff08;AI&#xff09;技术进行创新&#xff0c;以收获更多的业务成果…

python是什么语言写的

Python是一种计算机程序设计语言。是一种面向对象的动态类型语言。现今Python语言很火&#xff0c;可有人提问&#xff0c;这么火的语言它的底层又是什么语言编写的呢&#xff1f; python是C语言编写的&#xff0c;它有很多包也是用C语言写的。 所以说&#xff0c;C语言还是很…

SSM+vue音乐播放器管理系统

音乐播放器管理系统 随着社会的发展&#xff0c;计算机的优势和普及使得音乐播放器管理系统的开发成为必需。音乐播放器管理系统主要是借助计算机&#xff0c;通过对首页、音乐推荐、付费音乐、论坛信息、个人中心、后台管理等信息进行管理。减少管理员的工作&#xff0c;同时…

2024年华为杯数学建模E题-高速公路应急车道启用建模-基于YOLO8的数据处理代码参考(无偿分享)

利用YOLO模型进行高速公路交通流量分析 识别效果&#xff1a; 免责声明 本文所提供的信息和内容仅供参考。尽管我尽力确保所提供信息的准确性和可靠性&#xff0c;但我们不对其完整性、准确性或及时性作出任何保证。使用本文信息所造成的任何直接或间接损失&#xff0c;本人…

《深度学习》—— 卷积神经网络(CNN)的简单介绍和工作原理

文章目录 一、卷积神经网络的简单介绍二、工作原理(还未写完)1.输入层2.卷积层3.池化层4.全连接层5.输出层 一、卷积神经网络的简单介绍 基本概念 定义&#xff1a;卷积神经网络是一种深度学习模型&#xff0c;通常用于图像、视频、语音等信号数据的分类和识别任务。其核心思想…

Java笔试面试题AI答之设计模式(5)

文章目录 21. 简述Java什么是适配器模式 ?适配器模式的主要组成部分包括&#xff1a;适配器模式的实现方式主要有两种&#xff1a;适配器模式的优点&#xff1a;适配器模式的缺点&#xff1a;示例说明&#xff1a; 22. 请用Java代码实现适配器模式的案例 &#xff1f; 21. 简述…

【Transformers基础入门篇1】基础知识与环境安装

文章目录 一、自然语言处理基础知识1.1 常见自然语言处理任务1.2 自然语言处理的几个阶段 二、Transformers简单介绍2.1 Transformers相关库介绍2.2 Transformers 相关库安装 三、简单代码&#xff0c;启动NLP应用 一、自然语言处理基础知识 1.1 常见自然语言处理任务 情感分…

2024风湿免疫科常用评估量表汇总,附操作步骤与评定标准!

常笑医学整理了5个风湿免疫科常用的评估量表&#xff0c;包括类风湿关节炎患者病情评价&#xff08;DAS28&#xff09;、系统性狼疮活动性测定&#xff08;SLAM&#xff09;等。这些量表在常笑医学网均支持在线评估、下载和创建项目使用。 01 类风湿关节炎患者病情评价 &#x…

【MYSQL】聚合查询、分组查询、联合查询

目录 聚合查询聚合函数count()sum()avg()max()和min()总结 分组查询group by 子句having 子句 联合查询笛卡尔积内连接外连接自连接子查询单行子查询多行子查询from子句使用子查询 合并查询 聚合查询 聚合查询就是针对表中行与行之间的查询。 聚合函数 count() count(列名)&a…

828华为云征文 | 使用Flexus X实例搭建Dubbo-Admin服务

一、Flexus X实例简介 华为云推出的Flexus云服务&#xff0c;作为专为中小企业及开发者设计的新一代云服务产品&#xff0c;以其开箱即用、体验卓越及高性价比而著称。其中的Flexus云服务器X实例&#xff0c;更是针对柔性算力需求量身打造&#xff0c;能够智能适应业务负载变化…

工业交换机故障快速排查的方法有哪些

在现代工业自动化的环境中&#xff0c;工业交换机作为网络连接的重要设备&#xff0c;其稳定性和可靠性至关重要。然而&#xff0c;实际使用过程中难免会遇到各种故障&#xff0c;这对生产线和系统的正常运作造成了影响。为了有效应对这些问题&#xff0c;下面将介绍一些工业交…

一文详解大语言模型Transformer结构

目录 1. 什么是Transformer 2. Transformer结构 2.1 总体结构 2.2 Encoder层结构 2.3 Decoder层结构 2.4 动态流程图 3. Transformer为什么需要进行Multi-head Attention 4. Transformer相比于RNN/LSTM&#xff0c;有什么优势&#xff1f;为什么&#xff1f; 5. 为什么说Transf…

Vue项目之Element-UI(Breadcrumb)动态面包屑效果 el-breadcrumb

效果预览 需要导航的页面Vue.js 最笨的方法就是在每个需要面包屑的页面中固定写好 <template><div class="example-container"><el-breadcrumb separator="/"

【Linux】指令和权限的这些细节,你确定都清楚吗?

&#x1f680;个人主页&#xff1a;奋斗的小羊 &#x1f680;所属专栏&#xff1a;Linux 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 前言&#x1f4a5;一、Linux基本指令&#x1f4a5;1.1 mv 指令&#x1f4a5;1.2 cat 指令&#x1f4a5;…

HarmonyOS鸿蒙开发实战(5.0)自定义全局弹窗实践

鸿蒙HarmonyOS开发实战往期文章必看&#xff1a; HarmonyOS NEXT应用开发性能实践总结 最新版&#xff01;“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线&#xff01;&#xff08;从零基础入门到精通&#xff09; 非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线&am…

新手上路:在Windows CPU上安装Anaconda和PyCharm

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一. 确认自己是CPU为什么选择CPU教程&#xff1f;GPU与CPU的区别如何判断自己是CPU 二. Anaconda 安装包 和 Pycharm 安装包步骤1&#xff1a;下载Anaconda步骤2&am…

Golang开发的OCR-身份证号码识别(不依赖第三方)

身份证号码识别&#xff08;golang&#xff09; 使用golang的image库写的身份证号码识别&#xff0c;还有用了一个resize外部库&#xff0c;用来更改图片尺寸大小&#xff0c;将每个数字所在的图片的大小进行统一可以更好的进行数字识别&#xff0c;库名 &#xff1a;“github…