QT TCP网络通信编程

学习目标: TCP网络通信编程

前置环境

运行环境:qt creator 4.12

学习内容

一、TCP 协议基础知识:

  1. TCP 是一种面向连接的、可靠的、基于字节流的传输层通信协议。
  2. TCP 拥塞控制算法包括慢启动、拥塞避免、快速重传和快速恢复。
  3. TCP 通信需要建立连接,Qt 提供 QTcpSocket 和 QTcpServer 类用于 TCP 客户端和服务器编程。
  4. QTcpServer 类用于建立网络监听和 socket 连接,主要接口函数如 listen()、newConnection() 等。

QTcpServer* tcpServer; 它负责监听指定的 IP 地址和端口,并在有新的客户端连接时发出 newConnection() 信号。

QTcpSocket* tcpSocket; 它代表一个与服务端建立的 TCP 连接。

QTcpServer类

QTcpServer 是 Qt 框架提供的 TCP 服务器类,它提供了一系列常用的成员函数和信号,用于实现 TCP 服务器的基本功能。以下是 QTcpServer 的一些常用成员函数:

  1. 构造函数和析构函数:

    • QTcpServer(QObject *parent = nullptr):创建一个 QTcpServer 对象。
    • ~QTcpServer():析构函数,用于释放资源。
  2. 服务器状态控制:

    • bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0):开始监听指定的地址和端口。
    • void close():停止监听并关闭服务器。
      • abort()是强行立即关闭连接,相当于调用disconnectFromHost()。关闭后会释放所有相关资源并触发 QAbstractSocket::abort信号。对端可能不知道连接已经关闭。

      • close()是通过标准关闭序列完成关闭,等待对端确认,保证数据完整性。它会优雅地关闭socket连接,先发送关闭请求给对端,并等待对端确认。然后会进入关闭状态,触发QAbstractSocket::stateChanged(QAbstractSocket::ClosingState)和QAbstractSocket::disconnected信号。

    • bool isListening() const:检查服务器是否正在监听。
  3. 获取服务器信息:

    • QHostAddress serverAddress() const:返回服务器绑定的地址。
    • quint16 serverPort() const:返回服务器绑定的端口。
    • QAbstractSocket::SocketError socketError() const:返回最近一次发生的套接字错误。
    • QString errorString() const:返回最近一次发生的错误的描述字符串。
  4. 新连接管理:

    • QTcpSocket *nextPendingConnection():返回下一个待处理的连接。
    • int hasPendingConnections() const:返回待处理连接的数量。
  5. 信号处理:

    • void newConnection():当有新的连接到达时发出此信号。
    • void acceptError(QAbstractSocket::SocketError socketError):当接受新连接时发生错误时发出此信号。
  6. 其他功能:

    • void setMaxPendingConnections(int numConnections):设置服务器可以同时处理的最大待处理连接数。
    • int maxPendingConnections() const:返回服务器的最大待处理连接数。
    • void setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value):设置套接字选项。
    • QVariant socketOption(QAbstractSocket::SocketOption option) const:获取套接字选项的当前值。
  7. 地址和端口设置:

    • void setAddress(const QHostAddress &address):设置服务器监听的地址。
    • QHostAddress address() const:返回服务器监听的地址。
    • void setPort(quint16 port):设置服务器监听的端口。
    • quint16 port() const:返回服务器监听的端口。
  8. SSL/TLS 支持:

    • void setSecureMode(QSsl::SslMode mode):设置服务器的 SSL/TLS 模式。
    • QSsl::SslMode secureMode() const:返回服务器的 SSL/TLS 模式。
    • void setLocalCertificate(const QSslCertificate &certificate):设置服务器的本地证书。
    • QSslCertificate localCertificate() const:返回服务器的本地证书。
    • void setPrivateKey(const QSslKey &key):设置服务器的私钥。
    • QSslKey privateKey() const:返回服务器的私钥。
  9. 线程安全:

    • void setThreadPool(QThreadPool *threadPool):设置用于处理新连接的线程池。
    • QThreadPool *threadPool() const:返回用于处理新连接的线程池。
  10. 日志记录:

    • void setProxy(const QNetworkProxy &proxy):设置代理服务器。
    • QNetworkProxy proxy() const:返回当前使用的代理服务器。

这些成员函数提供了更多的灵活性和控制能力,使开发者能够根据具体需求配置和管理 TCP 服务器。例如,可以设置 SSL/TLS 模式以提供安全的通信,使用线程池来提高并发处理能力,以及设置代理服务器以实现更复杂的网络拓扑。

QTcpServer 类的使用:

  • QTcpServer 是从 QObject 继承的类,用于服务器建立网络监听和创建网络 socket 连接。
  • 主要接口函数包括 listen()nextPendingConnection()serverAddress() 和 serverPort() 等。

服务器Server提供的回调函数

在 Qt TCP 编程中,主要提供了以下几种重要的回调接口:

  1. QTcpServer 相关的回调:

    • QTcpServer::newConnection(): 当有新的客户端连接到达时触发该信号。
    • QTcpServer::acceptError(QAbstractSocket::SocketError socketError): 当服务器无法接受新的连接时触发该信号,可以获取错误信息。
  2. QTcpSocket 相关的回调:

    • QTcpSocket::connected(): 当套接字成功连接到远程主机时触发该信号。
    • QTcpSocket::disconnected(): 当套接字断开连接fin 位 为1时触发该信号。
    • QTcpSocket::readyRead(): 当套接字有新数据可读时触发该信号。
    • QTcpSocket::bytesWritten(qint64 bytes): 当成功写入数据到套接字时触发该信号,并返回写入的字节数。
    • QTcpSocket::error(QAbstractSocket::SocketError socketError): 当套接字发生错误时触发该信号,可以获取错误信息。
    • QTcpSocket::stateChanged(QAbstractSocket::SocketState socketState): 当套接字的连接状态发生变化时触发该信号。
    • bytesWritten(qint64 bytes) 信号:

      • 当成功写入数据到 QTcpSocket 时会触发此信号。
      • bytes 参数表示成功写入的字节数。
      • 可以用来监控数据发送的进度。
    • aboutToClose() 信号:

      • 当 QTcpSocket 将要关闭时会触发此信号。
      • 可以在此信号的槽函数中执行一些数据刷新或保存操作。
    • hostFound() 信号:

      • 当 QTcpSocket 成功解析了主机地址时会触发此信号。
      • 可以用来监控 DNS 解析的进度。
  3. QSslSocket 相关的回调(用于 SSL/TLS 连接):

    • QSslSocket::sslErrors(const QList<QSslError> &errors): 当 SSL/TLS 连接发生错误时触发该信号。
    • QSslSocket::encrypted(): 当 SSL/TLS 连接成功加密时触发该信号。
  4. QNetworkProxy 相关的回调:

    • QTcpSocket::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator): 当需要代理服务器认证时触发该信号。

这些回调接口涵盖了 TCP 连接的整个生命周期,包括连接建立、数据交互、连接断开以及各种错误情况。通过监听和处理这些信号,我们可以更好地控制和管理 TCP 连接,提高应用程序的可靠性和健壮性。

QT TCP网络通信编程项目

本地聊天传输,项目效果:

TCP服务端代码

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);setWindowTitle("TCP通讯服务端");//获取主机名和ip地址QHostInfo info =QHostInfo::fromName(QHostInfo::localHostName());QList<QHostAddress> addrs=info.addresses();if(!addrs.empty()){foreach(const QHostAddress & addr,addrs){if(addr.protocol()==QAbstractSocket::IPv4Protocol){ui->serverip->addItem(addr.toString());}}}tcpServer=new QTcpServer(this);//注册新连接回调 表示有新的客户端连接到达服务器。connect(tcpServer,&QTcpServer::newConnection,this,&MainWindow::ConnectCallback);}void MainWindow::ConnectCallback(){ //连接到达tcpSocket = tcpServer->nextPendingConnection();//连接成功后回调 表示当前 TCP 连接已经成功建立。auto clientconnect=[this](){// 客户端连接ui->plainTextEdit->appendPlainText("**********客户端socket连接成功**********");ui->plainTextEdit->appendPlainText("**********peer address:"+tcpSocket->peerAddress().toString());ui->plainTextEdit->appendPlainText("**********peer port:"+QString::number(tcpSocket->peerPort()));};connect(tcpSocket,&QTcpSocket::connected,this,clientconnect);clientconnect(); //因为当前连接已经到达了 所有手动调用一次//读前回调 当 tcpSocket 有数据可读时,该信号会被触发connect(tcpSocket,&QTcpSocket::readyRead,this,[this](){while(tcpSocket->canReadLine()){QString result = "ip:%1 prot:%2 in:%3 ";result = result.arg(tcpSocket->peerAddress().toString()).arg(QString::number(tcpSocket->peerPort())).arg(QString(tcpSocket->readLine()));ui->plainTextEdit->appendPlainText(result);}});//错误回调  当 tcpSocket 发生错误时,该信号会被触发connect(tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error),this, [this](QAbstractSocket::SocketError error) {// ui->plainTextEdit->appendPlainText("Socket error:" + error + tcpSocket->errorString());});//当套接字的连接状态发生变化时,该信号会被触发,并且会传递一个 QAbstractSocket::SocketState 类型的参数,表示当前的连接状态。connect(tcpSocket,QOverload<QAbstractSocket::SocketState>::of(&QTcpSocket::stateChanged),this,[this](QTcpSocket::SocketState state){// ui->plainTextEdit->appendPlainText("套接字状态变更:" + state);});//断开连接 fin位确认 回调connect(tcpSocket,&QTcpSocket::disconnected,this,[this](){// 客户端断开连接QString result = "**********客户端socket断开连接[ip:%1,prot:%2,]";result = result.arg(tcpSocket->peerAddress().toString()).arg(QString::number(tcpSocket->peerPort()));ui->plainTextEdit->appendPlainText(result);tcpSocket->deleteLater();});
}
MainWindow::~MainWindow()
{delete ui;if(tcpServer->isListening()){// 关闭TCP服务器tcpServer->close();tcpServer->deleteLater(); //最终关闭qDebug() << "TCP server MainWindow.";}
}
void MainWindow::closeEvent(QCloseEvent *e){ //关闭窗口//关闭closeMainWindow::on_stopServer_clicked();e->accept(); //允许窗口完成关闭操作。
}void MainWindow::on_startServer_clicked()
{QString ip(ui->serverip->currentText());uint16_t port =ui->serverport->value();tcpServer->listen(QHostAddress(ip),port);ui->plainTextEdit->appendPlainText("$$$$$$$$$$开始监听$$$$$$$$$$");ui->plainTextEdit->appendPlainText("服务器地址:"+tcpServer->serverAddress().toString());ui->plainTextEdit->appendPlainText("服务器端口:"+QString::number(tcpServer->serverPort()));ui->startServer->setEnabled(false);ui->stopServer->setEnabled(true);}void MainWindow::on_stopServer_clicked() //关闭tcpserver
{//先关闭所有socketif(!tcpSocket){tcpSocket->disconnect(); //用于断开 QTcpSocket 对象的所有信号与槽的连接。tcpSocket->close();      //它会向对端发送 FIN 数据包,并等待对端的确认,完成 TCP 连接的正常关闭过程。//fin回调 已调用 tcpSocket->deleteLater(); //它不会立即删除对象,而是将其标记为待删除状态,等到当前事件循环结束后再执行删除操作。}if(tcpServer->isListening()){tcpServer->close();//不调用 deleteLater 为了下次再次开启ui->startServer->setEnabled(true);ui->stopServer->setEnabled(false);ui->plainTextEdit->clear();}}void MainWindow::on_sendmsg_clicked()
{QString msg =ui->lineEdit->text();ui->lineEdit->clear();ui->plainTextEdit->appendPlainText("[out]:"+msg);tcpSocket->write(msg.toUtf8()+'\n');}

TCP客户端代码

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);setWindowTitle("tcp通信客户端");QHostInfo info =QHostInfo::fromName(QHostInfo::localHostName());QList<QHostAddress> addrs =info.addresses();if(!addrs.empty()){foreach(const QHostAddress& addr ,addrs){if(addr.protocol() == QAbstractSocket::IPv4Protocol){ui->serverip->addItem(addr.toString());}}}client = new QTcpSocket();//当 socket 成功连接到服务器时,会发射 connected() 信号。connect(client,&QTcpSocket::connected,this,[this](){ui->plainTextEdit->appendPlainText("**********已经连接到服务器端**********");ui->plainTextEdit->appendPlainText("服务器端ip:"+client->peerAddress().toString());ui->plainTextEdit->appendPlainText("服务器端port:"+QString::number(client->peerPort()));ui->tcpconnect->setEnabled(false);ui->tcpclose->setEnabled(true);});//当 socket 与服务器断开连接时,会发射 disconnected() 信号。connect(client,&QTcpSocket::disconnected,this,[this](){ui->plainTextEdit->appendPlainText("**********已断开与服务器端的连接**********");client->close();ui->tcpconnect->setEnabled(true);ui->tcpclose->setEnabled(false);});//当 socket 有新的数据可读时,会发射 readyRead() 信号。connect(client,&QTcpSocket::readyRead,this,[this](){while(client->canReadLine()){ui->plainTextEdit->appendPlainText("[in]:"+client->readLine());}});}MainWindow::~MainWindow()
{delete ui;
}\void MainWindow::on_send_clicked()
{QString msg=ui->lineEdit->text();ui->plainTextEdit->appendPlainText("[out]:"+msg);ui->lineEdit->clear();client->write(msg.toUtf8()+'\n');}void MainWindow::on_tcpconnect_clicked()
{QString ip=ui->serverip->currentText();quint16 port =ui->serverport->value();client->connectToHost(ip,port);if (!client->waitForConnected(3000)) {// 5s 连接超时处理逻辑ui->plainTextEdit->appendPlainText("连接超时,请检查服务器ip和port是否正确.");}}void MainWindow::on_tcpclose_clicked()
{if(client->state() ==QAbstractSocket::ConnectedState) client->disconnectFromHost(); //这里首先检查 tcpclient 对象的当前连接状态。ui->tcpconnect->setEnabled(true);ui->tcpclose->setEnabled(false);}
// 在应用程序退出或客户端断开连接时 点关闭窗口
void MainWindow::closeEvent(QCloseEvent* event) {// 1. 断开与服务器的连接if (client->state() == QAbstractSocket::ConnectedState) {client->disconnectFromHost();}qDebug()<<"closeEvent";// 2. 释放 QTcpSocket 对象client->deleteLater();// 允许窗口关闭event->accept();
}

 总结

server端

  1. TCP 服务器的创建和启停:

    • 在构造函数中创建 QTcpServer 对象,并连接 newConnection() 信号到 ConnectCallback 函数。
    • on_startServer_clicked() 函数中,监听指定的 IP 和端口,启动 TCP 服务器。
    • on_stopServer_clicked() 函数中,关闭 TCP 服务器,断开所有客户端连接。
    • 在主窗口关闭时,调用 on_stopServer_clicked() 函数关闭服务器。
  2. 客户端连接的处理:

    • 在 ConnectCallback 函数中,获取新连接的 QTcpSocket 对象。
    • 连接客户端连接成功、数据可读、错误、状态变更、断开连接等信号。
    • 在信号处理函数中,输出连接信息并处理数据收发。
  3. 数据收发和协议处理:

    • 连接 readyRead() 信号,处理客户端发送的数据。
    • 使用 tcpSocket->readLine() 读取并解析数据,输出到 UI 界面。
    • on_sendmsg_clicked() 函数中,通过 tcpSocket->write() 将消息发送给客户端。
  4. 异常处理:

    • 连接 error() 信号,处理套接字错误。
    • 连接 stateChanged() 信号,监控连接状态变更。
  5. 生命周期管理:

    • 在主窗口析构函数中,关闭 TCP 服务器并释放资源。
    • 在客户端断开连接时,释放 QTcpSocket 对象。
      • 如果需要立即关闭连接而不管数据完整性,使用abort()。

      • 如果需要负责任关闭保证数据完整,则使用close()。

通过学习这段代码,我们可以掌握以下 Qt TCP 编程的关键点:

  1. 如何创建 TCP 服务器并监听端口。
  2. 如何处理新的客户端连接,并与之进行数据通信。
  3. 如何处理连接错误和状态变更。
  4. 如何优雅地关闭服务器并释放资源。

客户端

  1. TCP 客户端的创建和连接:

    • 在构造函数中创建 QTcpSocket 对象 client
    • 连接 connected() 信号,处理与服务器成功连接的情况。
    • on_tcpconnect_clicked() 函数中,调用 connectToHost() 连接到服务器。
    • 使用 waitForConnected() 处理连接超时的情况。
  2. 数据收发和协议处理:

    • 连接 readyRead() 信号,处理服务器发送的数据。
    • 使用 client->readLine() 读取并解析数据,输出到 UI 界面。
    • on_send_clicked() 函数中,通过 client->write() 将消息发送给服务器。
  3. 连接状态管理:

    • 连接 disconnected() 信号,处理与服务器的断开连接。
    • 在 closeEvent() 中,检查连接状态并断开连接。
    • 更新 UI 按钮的状态,反映当前的连接状态。
  4. 异常处理:

    • 在连接超时的情况下,输出错误提示信息。
    • 在 closeEvent() 中,释放 QTcpSocket 对象。

通过学习这段代码,我们可以掌握以下 Qt TCP 客户端编程的关键点:

  1. 如何创建 TCP 客户端并连接到服务器。
  2. 如何处理数据的收发,并按照约定的协议进行解析。
  3. 如何管理连接状态,处理连接成功、断开等情况。
  4. 如何优雅地关闭连接并释放资源。

    

最后附上源代码链接
对您有帮助的话,帮忙点个star

35-tcpSocket-client · jbjnb/Qt demo - 码云 - 开源中国 (gitee.com)

35-tcpSocket-server · jbjnb/Qt demo - 码云 - 开源中国 (gitee.com)

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

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

相关文章

利用Python的sympy包求解一元多次方程

一元1次方程 import sympy as sp # 导入sympy包 x sp.Symbol(x) # 定义符号变量 f 2*x -8 # 定义要求解的一元1次方程 x sp.solve(f) # 调用solve函数求解方程 x[4]一元2次方程 import sympy as sp # 导入sympy包 x sp.Symbol(x) # 定义符号变量 f …

网络安全合规建设

网络安全合规建设 一、法律安全需求基本合规&#xff08;1&#xff09;《网络安全法》重要节点等级保护政策核心变化 二、安全需求 业务刚需&#xff08;1&#xff09;内忧&#xff08;2&#xff09;外患 三、解决方法&#xff08;1&#xff09;总安全战略目标图&#xff08;2&…

广汇汽车:救得起来吗?

五折奔驰、六折宝马...BBA们“腰斩式”大降价后正在引发连锁反应。 国内第二大汽车经销商——广汇汽车&#xff0c;还好吗&#xff1f; 受新能源品牌冲击&#xff0c;近年来奔驰、宝马等豪华燃油品牌销量低迷&#xff0c;纷纷开启降价模式&#xff0c;首当其冲的就是以广汇汽车…

QT TCP多线程网络通信

学习目标&#xff1a; TCP网络通信编程 学习前置环境 运行环境:qt creator 4.12 QT TCP网络通信编程-CSDN博客 Qt 线程 QThread类详解-CSDN博客 学习内容 使用多线程技术实现服务端计数器 核心代码 客户端 客户端&#xff1a;负责连接服务端&#xff0c;每次连接次数1。…

从零开始做题:MP3

题目 给出一个mp3文件 解题 右键->selection->save selection->另存为xxx.png即可 8750d5109208213f E:\逐鹿\MISC\tools\MP3Stego_1_1_19\MP3Stego>.\decode -X cipher.mp3 MP3StegoEncoder 1.1.19 See README file for copyright info Input file cipher.mp3…

53-5 内网代理7 - CS上线不出网主机

靶场搭建: 这里就用之前内网代理的靶场,把web服务器这台虚拟机关闭掉,用剩下的3台加kali 各个虚拟机的网络情况 kali - 可以连接外网win2008(之前的FTP服务器) 可以连接外网 win 7(之前的办公电脑) 不出网主机 - 无法连接外网win2012 克隆机(之前的域控) - 无法连接…

视频压缩文件太大了怎么缩小?3个压缩方法分享

视频压缩文件太大了怎么缩小&#xff1f;当视频压缩文件过大时&#xff0c;缩小其大小不仅能节省宝贵的存储空间&#xff0c;还能显著提升文件传输速度&#xff0c;特别是在网络条件有限的情况下。通过专业的视频压缩软件&#xff0c;可以有效减少文件体积&#xff0c;使视频内…

python库(9):prettytable库快速实现ASCII表格

下面介绍一个快速制作ASCII表格库——prettytable&#xff0c;可以方便地制作简单表格。 1 安装prettytable pip install -i https://pypi.tuna.tsinghua.edu.cn/simple prettytable 结果如下&#xff1a; 2 代码实例 from prettytable import PrettyTable table PrettyTa…

【Python系列】深入解析 Python 中的 JSON 处理工具

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

coco数据集格式计算mAP的python脚本

目录 背景说明COCOeval 计算mAPtxt文件转换为coco json 格式自定义数据集标注 背景说明 在完成YOLOv5模型移植&#xff0c;运行在板端后&#xff0c;通常需要衡量板端运行的mAP。 一般需要两个步骤 步骤一&#xff1a;在板端批量运行得到目标检测结果&#xff0c;可保存为yol…

【Django项目】基于Python+Django+MySQL的音乐网站系统项目

功能介绍 首页&#xff1a;歌曲分类、歌曲搜索、热门歌曲、热门下载、新歌推荐 歌曲排行&#xff1a;歌曲分类、分页功能 用户板块&#xff1a;用户登陆/注册、播放历史 歌曲详情&#xff1a;歌曲播放、当前播放列表、歌曲点评、歌曲播放插件、下载歌曲 系统后台&#xff1a;歌…

MySQL体系架构解析

1.MySQL体系架构 1.1.MySQL的分支与变种 MySQL变种有好几个,主要有三个久经考验的主流变种:Percona Server,MariaDB和 Drizzle。它们都有活跃的用户社区和一些商业支持,均由独立的服务供应商支持。同时还有几个优秀的开源关系数据库,值得我们了解一下。 1.1.1.Drizzle …

L2 LangGraph_Components

参考自https://www.deeplearning.ai/short-courses/ai-agents-in-langgraph&#xff0c;以下为代码的实现。 这里用LangGraph把L1的ReAct_Agent实现&#xff0c;可以看出用LangGraph流程化了很多。 LangGraph Components import os from dotenv import load_dotenv, find_do…

2024年高压电工证考试题库及高压电工试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年高压电工证考试题库及高压电工试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大纲随机出的高压…

开源项目有哪些机遇与挑战

目录 1.概述 2.开源项目的发展趋势 2.1. 开源项目的发展现状 2.2. 开源社区的活跃度 2.3. 开源项目在技术创新中的作用 3.参与开源的经验分享 3.1. 选择开源项目 3.2. 理解项目结构和文档 3.3. 贡献代码 3.4. 与开源社区的合作 3.5. 学习和成长 4.开源项目的挑战 …

内裤洗衣机到底值不值得买?五大高质量靠谱内衣洗衣机值得拥有

市场上出现了内衣洗衣机&#xff0c;这种洗衣机比市面上的普通洗衣机的清洁力好&#xff0c;还具有除菌功能&#xff0c;在清洗完内衣裤的过程中&#xff0c;可以将衣物上的细菌去除掉&#xff0c;但市面上的内衣品牌众多&#xff0c;什么样的牌子才好用呢&#xff1f;作为一位…

前端简历:如何写项目经历(经验)找出细节点和重难点,轻松应对面试?

&#xff08;下面内容&#xff1a;我将结合我的实际项目带大家进行每一步骤的梳理&#xff09; 项目经历-堂食外送点餐 2022年2月-2022年5月 项目描述&#xff1a;该平台提供外送订餐服务&#xff0c;用户可以在手机中轻松地浏览菜品、下单、支付、编辑地址、填写个人信息等…

手撸俄罗斯方块——游戏设计

手撸俄罗斯方块——游戏设计 概述 上一章我们介绍俄罗斯方块的基本信息&#xff0c;包括坐标点和方块的基本概念&#xff0c;这一章节我们继续介绍如何完成后续的游戏设计。 组成游戏的基本要素 俄罗斯方块作为一个 2D 的平面游戏&#xff0c;我们可以将整个参与元素做如下…

简过网:工程专业最吃香的6个证书,你考了几个了?

工程专业最吃香的6个证书&#xff0c;你考了几个了&#xff1f;我们一起来看看吧&#xff01; 1、二级建造师 报考条件&#xff1a;工程类大专及以上学历/从事相关职业 考试时间&#xff1a;3月报名、6月考试 就业前景&#xff1a;建筑设计院、房产开发公司、施工单位 2、一…

如何管理一百个ai专家智能体——ai调度系统设计

前言 如果你用过openai的chatgpt服务&#xff0c;你肯定知道一个叫做GPTs的智能体商店&#xff0c;里面提供了大量的来自官方和个人制作的专门针对某个领域的gpt助手。比如&#xff0c;你想让gpt帮忙写文章&#xff0c;并且要能够写得好&#xff0c;你就可以在商店中搜索相关的…