《十六》QT TCP协议工作原理和实战

        Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍如何运用QTcpSocket组件实现基于TCP的网络通信功能。

   QTcpSocketQTcpServer是Qt中用于实现基于TCP(Transmission Control Protocol)通信的两个关键类。TCP是一种面向连接的协议,它提供可靠的、双向的、面向字节流的通信。这两个类允许Qt应用程序在网络上建立客户端和服务器之间的连接。

以下是QTcpSocket类的一些常用函数:

函数描述
QTcpSocket()构造函数,创建一个新的QTcpSocket对象。
~QTcpSocket()析构函数,释放QTcpSocket对象及其资源。
void connectToHost(const QString &hostName, quint16 port)尝试与指定主机名和端口建立连接。
void disconnectFromHost()断开与主机的连接。
QAbstractSocket::SocketState state() const返回套接字的当前状态。
QHostAddress peerAddress() const返回与套接字连接的远程主机的地址。
quint16 peerPort() const返回与套接字连接的远程主机的端口。
QAbstractSocket::SocketError error() const返回套接字的当前错误代码。
qint64 write(const char *data, qint64 maxSize)将数据写入套接字,返回实际写入的字节数。
qint64 read(char *data, qint64 maxSize)从套接字读取数据,返回实际读取的字节数。
void readyRead()当套接字有可供读取的新数据时发出信号。
void bytesWritten(qint64 bytes)当套接字已经写入指定字节数的数据时发出信号。
void error(QAbstractSocket::SocketError socketError)当套接字发生错误时发出信号。

以下是QTcpServer类的一些常用函数及其简要解释:

函数   

描述
QTcpServer()  构造函数,创建一个新的QTcpServer对象。
~QTcpServer()   析构函数,释放QTcpServer对象及其资源。
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)    开始监听指定的地址和端口。
void close()     停止监听并关闭服务器。
bool isListening() const  返回服务器是否正在监听连接。
QList<QTcpSocket*> pendingConnections()   返回等待处理的挂起连接的列表。
virtual void incomingConnection(qintptr socketDescriptor)     当有新连接时调用,可以在子类中实现以处理新连接。
void maxPendingConnections() const   返回允许的最大挂起连接数。
void setMaxPendingConnections(int numConnections)   设置允许的最大挂起连接数。
QNetworkProxy proxy() const   返回服务器的代理设置。
void setProxy(const QNetworkProxy &networkProxy)   设置服务器的代理设置。
QAbstractSocket::SocketError serverError() const     返回服务器的当前错误代码。
QString errorString() const    返回服务器的错误消息字符串。
void pauseAccepting()    暂停接受新连接,但保持现有连接。
void resumeAccepting()    恢复接受新连接。
void close()    关闭服务器。

如上这些只是常用函数的简要描述,详细的函数说明和用法可以参考Qt官方文档或相关文档。

1 通信的流程

1.1 服务端流程

在使用TCP通信时同样需要导入Qt+=network模块,并在头文件中引入QTcpServerQTcpSocket两个模块,当有了模块的支持,接着就是侦听套接字,此处可通过调用server.listen来实现侦听,此函数原型如下:

bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0
);

这个函数用于开始在指定的地址和端口上监听连接。它的参数包括:

  1.     address:一个QHostAddress对象,指定要监听的主机地址。默认为QHostAddress::Any,表示监听所有可用的网络接口。
  2.     port:一个quint16类型的端口号,指定要监听的端口。如果设置为0,系统将选择一个可用的未使用端口。

函数返回一个bool值,表示是否成功开始监听。如果成功返回true,否则返回false,并且可以通过调用errorString()获取错误消息。

紧随套接字侦听其后,通过使用一个waitForNewConnection等待新的连接到达。它的原型如下:

bool QTcpServer::waitForNewConnection(int msec = 0, bool *timedOut = nullptr
);

该函数在服务器接受新连接之前会一直阻塞。参数包括:

  • msec:等待连接的超时时间(以毫秒为单位)。如果设置为0(默认值),则表示无限期等待,直到有新连接到达。
  • timedOut:一个可选的布尔指针,用于指示等待是否超时。如果传递了此参数,并且等待时间达到了指定的超时时间,*timedOut将被设置为true,否则为false。如果不关心超时,可以将此参数设置为nullptr。

函数返回一个布尔值,表示是否成功等待新连接。如果在超时时间内有新连接到达,返回true,否则返回false。如果等待超时,可以通过检查timedOut参数来确定。如果函数返回false,可以通过调用errorString()获取错误消息。

套接字的接收会使用nextPendingConnection()函数来实现,nextPendingConnection 是 QTcpServer 类的成员函数,用于获取下一个已接受的连接的套接字(QTcpSocket)。它的原型如下:

QTcpSocket *QTcpServer::nextPendingConnection();

函数返回一个指向新连接套接字的指针。如果没有已接受的连接,则返回 nullptr。

使用这个函数,你可以在服务器接受连接之后获取相应的套接字,以便进行数据传输和通信。一般来说,在收到 newConnection 信号后,你可以调用这个函数来获取新连接的套接字。

当有了套接字以后,就可以通过QTcpServer指针判断对应的套接字状态,一般套接字的状态被定义在QAbstractSocket类内。以下是QAbstractSocket类中定义的一些状态及其对应的标志:

状态标志描述
UnconnectedState未连接状态,套接字没有连接到远程主机。
HostLookupState正在查找主机地址状态,套接字正在解析主机名。
ConnectingState连接中状态,套接字正在尝试与远程主机建立连接。
ConnectedState已连接状态,套接字已经成功连接到远程主机。
BoundState已绑定状态,套接字已经与地址和端口绑定。
ClosingState关闭中状态,套接字正在关闭连接。
ListeningState监听中状态,用于QTcpServer,表示服务器正在监听连接。

这些状态反映了套接字在不同阶段的连接和通信状态。在实际使用中,可以通过调用state()函数获取当前套接字的状态,并根据需要处理相应的状态。例如,可以使用信号和槽机制来捕获状态变化,以便在连接建立或断开时执行相应的操作。

当套接字被连接后则可以通过socket->write()方法向上线客户端发送一个字符串,此处我们以发送lyshark例,发送时需要向write()中传入两个参数。其原型如下:

qint64 QTcpSocket::write(const char *data, qint64 maxSize);

该函数接受两个参数:

  • data:指向要写入套接字的数据的指针。
  • maxSize:要写入的数据的最大字节数。

函数返回实际写入的字节数,如果发生错误,则返回 -1。在写入数据之后,可以使用 bytesWritten 信号来获取写入的字节数。此外,你也可以使用 waitForBytesWritten 函数来阻塞等待直到所有数据都被写入。 

1.2 客户端流程

客户端的流程与服务端基本保持一致,唯一的区别在于将server.listen更换为socket.connectToHost连接到对应的主机,QTcpSocketconnectToHost 函数的原型如下:

void QTcpSocket::connectToHost(
const QString &hostName, 
quint16 port, 
OpenMode openMode = ReadWrite
);
  • hostName:远程主机的主机名或IP地址。
  • port:要连接的端口号。
  • openMode:套接字的打开模式,默认为 ReadWrite

函数用于初始化与指定远程主机和端口的连接。在实际使用中,你可以通过调用这个函数来发起与目标主机的连接尝试。

读取数据时可以使用readAll函数来实现,socket.readAll() 是 QTcpSocket 类的成员函数,用于读取所有可用的数据并返回一个 QByteArray 对象。其函数函数原型如下:

QByteArray QTcpSocket::readAll();

 该函数返回一个包含从套接字中读取的所有数据的 QByteArray 对象。通常,你可以通过这个函数来获取已经到达的所有数据,然后对这些数据进行进一步的处理。

2 图形化应用

2.1 服务端流程

        与命令行版本的网络通信不同,图形化部分需要使用信号与槽函数进行绑定,所有的通信流程都是基于信号的,对于服务端而言我们需要导入QTcpServerQtNetworkQTcpSocket模块,并新增四个槽函数分别对应四个信号;

信号      槽函数 描述
connected()  onClientConnected()    当 tcpSocket 成功连接到远程主机时触发,执行 onClientConnected() 函数。
disconnected()  onClientDisconnected()    当 tcpSocket 断开连接时触发,执行 onClientDisconnected() 函数。

stateChanged

(QAbstractSocket::SocketState)  

 onSocketStateChange

(QAbstractSocket::SocketState) 

   当 tcpSocket 的状态发生变化时触发,执行 onSocketStateChange() 函数,传递新的状态。
readyRead()    onSocketReadyRead()tcpSocket 有可读取的新数据时触发,执行 onSocketReadyRead() 函数。

在程序入口处我们通过new QTcpServer(this)新建TCP套接字类,并通过connect()连接到初始化槽函数上,当程序运行后会首先触发newConnection信号,执行newconect()槽函数。

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QString strip=GetLocalIpAddress();//QMessageBox::information(this,"数据",strip,QMessageBox::Yes);ui->comboBox_ip->addItem(strip);tcpserver=new QTcpServer(this);connect(tcpserver,SIGNAL(newConnection()),this,SLOT(newconect()));}

而在槽函数newconect()中,通过nextPendingConnection新建一个套接字,并绑定其他四个槽函数,这里的槽函数功能各不相同,将其对应的信号绑定到对应槽函数上即可;

void MainWindow::newconect()
{// 创建新套接字tcpsocked=tcpserver->nextPendingConnection();// 连接触发信号connect(tcpsocked,SIGNAL(connected()),this,SLOT(clientconnect()));clientconnect();// 关闭触发信号connect(tcpsocked,SIGNAL(disconnected()),this,SLOT(clientdisconnect()));// 状态改变触发信号connect(tcpsocked,SIGNAL(stateChanged(QAbstraSocket::SocketState)),this,SLOT(OnSocketStateChanged(tcpsocked->state())));// 读入数据触发信号connect(tcpsocked,SIGNAL(readyRead()),this,SLOT(socketreaddata()));
}

当读者点击侦听时则直接调用tcpserver->listen实现对本地IP的函数实现,如下所示;端口的侦听功能,停止侦听则是调用tcpServer->close


void MainWindow::on_pushButton_Start_clicked()
{QString ip=ui->comboBox_ip->currentText();quint16 port=ui->spinBox_port->value();QHostAddress address(ip);tcpserver->listen(address,port);ui->plainTextEdit_DispMsg->appendPlainText("$$$$$$$$开始监听$$$$$$$$");ui->plainTextEdit_DispMsg->appendPlainText("$$$$$$$$服务器地址:"+tcpserver->serverAddress().toString());ui->plainTextEdit_DispMsg->appendPlainText("$$$$$$$$服务器端口:"+QString::number(tcpserver->serverPort()));ui->pushButton_Start->setEnabled(false);ui->pushButton_Stop->setEnabled(true);}
void MainWindow::on_pushButton_Stop_clicked()
{if(tcpserver->isListening()){tcpserver->close();ui->pushButton_Start->setEnabled(true);ui->pushButton_Stop->setEnabled(false);}
}

对于读取数据可以通过canReadLine()函数判断行,并通过tcpClient->readLine()逐行读入数据,相对应的发送数据可通过调用tcpsocket->write函数实现,在发送之前需要将其转换为QByteArray类型的字符串格式,如下所示;

void MainWindow::socketreaddata()
{//读取数据while(tcpsocked->canReadLine()){ui->plainTextEdit_DispMsg->appendPlainText("[in]"+tcpsocked->readLine());}
}void MainWindow::on_pushButton_InputMesg_clicked()
{QString strmsg=ui->lineEdit->text();ui->plainTextEdit_DispMsg->appendPlainText("[out]:"+strmsg);ui->lineEdit->clear();QByteArray str=strmsg.toUtf8();str.append("\n");tcpsocked->write(str);
}
2.2 客户端流程

对于客户端而言同样需要绑定四个信号并对应到特定的槽函数上,其初始化部分与服务端保持一致,唯一不同的是客户端使用connectToHost函数链接到服务端上,断开连接时使用的是disconnectFromHost函数,如下所示;

void MainWindow::on_pushButton_connect_clicked()
{QString addr=ui->comboBox->currentText();quint16 port=ui->spinBox->value();tcpclient->connectToHost(addr,port);
}void MainWindow::on_pushButton_disconnect_clicked()
{if(tcpclient->state()==QAbstractSocket::ConnectedState){tcpclient->disconnectFromHost();}
}

此处的读取数据与服务端保持一致,发送数据时则是通过tcpClient->write(str)函数直接传递给客户端,代码如下所示;

void MainWindow::socketreaddata()
{qDebug()<<"hahahah";while(tcpclient->canReadLine()){ui->plainTextEdit->appendPlainText("[in]:"+tcpclient->readLine());}
}void MainWindow::on_pushButton_send_clicked()
{QString strmsg=ui->lineEdit->text();ui->plainTextEdit->appendPlainText("[out]:"+strmsg);ui->lineEdit->clear();QByteArray str=strmsg.toUtf8();str.append('\n');tcpclient->write(str);
}

运行后,服务端启用侦听等待客户端连接,客户端连接后,双方则可以实现数据的收发功能,由于采用了信号机制,两者的收发并不会阻断可同时进行,如下图所示;

具体ui文件布局和头文件没有放出来,就是定义一些量和函数等

完整代码:客户端:

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);tcpclient=new QTcpSocket(this);QString strip=getlocalip();ui->comboBox->addItem(strip);connect(tcpclient,SIGNAL(connected()),this,SLOT(connectfunc()));connect(tcpclient,SIGNAL(disconnected()),this,SLOT(disconnectfunc()));connect(tcpclient,SIGNAL(stateChanged(QAbstraSocket::SocketState)),this,SLOT(OnSocketStateChanged(tcpclient->state())));connect(tcpclient,SIGNAL(readyRead()),this,SLOT(socketreaddata()));}MainWindow::~MainWindow()
{delete ui;
}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;
}void MainWindow::closeEvent(QCloseEvent *event)
{if(tcpclient->state()==QAbstractSocket::ConnectedState){tcpclient->disconnectFromHost();}event->accept();
}void MainWindow::connectfunc()
{ui->plainTextEdit->appendPlainText("********连接到服务器端********");ui->plainTextEdit->appendPlainText("********peer address:"+tcpclient->peerAddress().toString());ui->plainTextEdit->appendPlainText("********peer port:"+QString::number(tcpclient->peerPort()));ui->pushButton_connect->setEnabled(false);ui->pushButton_disconnect->setEnabled(true);
}void MainWindow::disconnectfunc()
{ui->plainTextEdit->appendPlainText("********已断开与服务器之间连接********");ui->pushButton_connect->setEnabled(true);ui->pushButton_disconnect->setEnabled(false);
}void MainWindow::socketreaddata()
{qDebug()<<"hahahah";while(tcpclient->canReadLine()){ui->plainTextEdit->appendPlainText("[in]:"+tcpclient->readLine());}
}void MainWindow::on_pushButton_connect_clicked()
{QString addr=ui->comboBox->currentText();quint16 port=ui->spinBox->value();tcpclient->connectToHost(addr,port);
}void MainWindow::on_pushButton_disconnect_clicked()
{if(tcpclient->state()==QAbstractSocket::ConnectedState){tcpclient->disconnectFromHost();}
}void MainWindow::on_pushButton_send_clicked()
{QString strmsg=ui->lineEdit->text();ui->plainTextEdit->appendPlainText("[out]:"+strmsg);ui->lineEdit->clear();QByteArray str=strmsg.toUtf8();str.append('\n');tcpclient->write(str);
}

服务器端:

#include "mainwindow.h"
#include "ui_mainwindow.h"#include<QMessageBox>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QString strip=GetLocalIpAddress();//QMessageBox::information(this,"数据",strip,QMessageBox::Yes);ui->comboBox_ip->addItem(strip);tcpserver=new QTcpServer(this);connect(tcpserver,SIGNAL(newConnection()),this,SLOT(newconect()));}MainWindow::~MainWindow()
{delete ui;
}QString MainWindow::GetLocalIpAddress()
{QString hostname=QHostInfo::localHostName();QHostInfo hostinfo=QHostInfo::fromName(hostname);QString localip="";QList<QHostAddress> addresslist=hostinfo.addresses();if(!addresslist.isEmpty()){for(int i=0;i<addresslist.count();i++){QHostAddress addrhost=addresslist.at(i);if(QAbstractSocket::IPv4Protocol==addrhost.protocol()){localip=addrhost.toString();break;}}}return localip;}void MainWindow::clientconnect()
{//客户端连接ui->plainTextEdit_DispMsg->appendPlainText("***********客户端socket链接***********");ui->plainTextEdit_DispMsg->appendPlainText("***********peer address:"+tcpsocked->peerAddress().toString());ui->plainTextEdit_DispMsg->appendPlainText("***********peer port:"+QString::number(tcpsocked->peerPort()));}void MainWindow::clientdisconnect()
{//客户端断开连接ui->plainTextEdit_DispMsg->appendPlainText("***********客户端断开连接***********");tcpsocked->deleteLater();
}void MainWindow::socketreaddata()
{//读取数据while(tcpsocked->canReadLine()){ui->plainTextEdit_DispMsg->appendPlainText("[in]"+tcpsocked->readLine());}
}void MainWindow::newconect()
{tcpsocked=tcpserver->nextPendingConnection();connect(tcpsocked,SIGNAL(connected()),this,SLOT(clientconnect()));clientconnect();connect(tcpsocked,SIGNAL(disconnected()),this,SLOT(clientdisconnect()));connect(tcpsocked,SIGNAL(stateChanged(QAbstraSocket::SocketState)),this,SLOT(OnSocketStateChanged(tcpsocked->state())));connect(tcpsocked,SIGNAL(readyRead()),this,SLOT(socketreaddata()));
}void MainWindow::on_pushButton_Start_clicked()
{QString ip=ui->comboBox_ip->currentText();quint16 port=ui->spinBox_port->value();QHostAddress address(ip);tcpserver->listen(address,port);ui->plainTextEdit_DispMsg->appendPlainText("$$$$$$$$开始监听$$$$$$$$");ui->plainTextEdit_DispMsg->appendPlainText("$$$$$$$$服务器地址:"+tcpserver->serverAddress().toString());ui->plainTextEdit_DispMsg->appendPlainText("$$$$$$$$服务器端口:"+QString::number(tcpserver->serverPort()));ui->pushButton_Start->setEnabled(false);ui->pushButton_Stop->setEnabled(true);}void MainWindow::on_pushButton_Stop_clicked()
{if(tcpserver->isListening()){tcpserver->close();ui->pushButton_Start->setEnabled(true);ui->pushButton_Stop->setEnabled(false);}
}void MainWindow::on_pushButton_InputMesg_clicked()
{QString strmsg=ui->lineEdit->text();ui->plainTextEdit_DispMsg->appendPlainText("[out]:"+strmsg);ui->lineEdit->clear();QByteArray str=strmsg.toUtf8();str.append("\n");tcpsocked->write(str);
}void MainWindow::closeEvent(QCloseEvent *event)
{if(tcpserver->isListening()){tcpserver->close();}event->accept();
}

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

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

相关文章

FIFO Generate IP核使用——异步复位

FIFO Generator IP核提供了一个复位输入&#xff0c;当该输入被激活时&#xff0c;它会复位所有的计数器和输出寄存器。对于块RAM或分布式RAM实现&#xff0c;复位FIFO并不是必需的&#xff0c;可以在FIFO中禁用复位引脚。共有两种复位类型选项&#xff1a;异步复位和同步复位。…

初识Linux -- Linux的背景和发展史介绍

点赞关注不迷路&#xff01;&#xff0c;本节涉及初识Linux&#xff0c;主要为背景介绍和xshell登录主机。 1.Linux背景 1.1 发展史 Linux从哪里来&#xff1f;它是怎么发展的&#xff1f;在这里简要介绍Linux的发展史。 要说Linux&#xff0c;还得从UNIX说起。 1.2 UNIX发…

毕业设计uniapp+vue有机农产品商城系统 销售统计图 微信小程序

本人在网上找了一下这方面的数据发现农村中的信心普及率很是低农民们都不是怎么会用手机顶多就是打打电话发发短信&#xff0c;平时不太会上网更不会想到通过网络手段去卖出自己的劳作成果—农产品&#xff0c;这无疑大大浪费了农民的劳动成果和国家资源也大大打击了人们的生产…

《QT实用小工具·五十五》带有标签、下划线的Material Design风格输入框

1、概述 源码放在文章末尾 该项目实现了一个带有标签动画、焦点动画、正确提示、错误警告的单行输入框控件。下面是demo演示&#xff1a; 项目部分代码如下所示&#xff1a; #ifndef LABELEDEDIT_H #define LABELEDEDIT_H#include <QObject> #include <QWidget>…

搜索算法系列之四(斐波那契)

以下算法被验证过&#xff0c;如有什么问题或有补充的欢迎留言。 前言 斐波那契数列&#xff0c;又称黄金分割数列&#xff0c;是由意大利数学家&#xff08;Leonardo Fibonacci&#xff09;在1202年提出的。这个数列的递推关系是F(0)1&#xff0c;F(1)1&#xff0c;F(n)F(n-…

在Android中,如何通过Kotlin协程处理多个API调用

在Android中&#xff0c;如何通过Kotlin协程处理多个API调用 在Android开发中&#xff0c;如何使用Kotlin协程处理多个API调用的示例呢&#xff1f;假设我们已经对Kotlin协程有了一定的了解&#xff0c;包括定义、简单用例和示例等。现在&#xff0c;让我们来看一些真实的Andr…

[Java、Android面试]_22_APP启动流程(中频问答)

欢迎查看合集&#xff1a; Java、Android面试高频系列文章合集 本人今年参加了很多面试&#xff0c;也有幸拿到了一些大厂的offer&#xff0c;整理了众多面试资料&#xff0c;后续还会分享众多面试资料。 整理成了面试系列&#xff0c;由于时间有限&#xff0c;每天整理一点&am…

LVS/NAT工作模式介绍及配置

1.1 LVS/NAT模式工作原理 LVS&#xff08;Linux Virtual Server&#xff09;的网络地址转换&#xff08;NAT&#xff09;模式是一种在网络层&#xff08;第四层&#xff09;实现负载均衡的方法。在NAT模式中&#xff0c;Director Server&#xff08;DS&#xff09;充当所有服务…

Python 全栈系列241 GFGo Lite迭代

说明 随着整个算网开发逐渐深入&#xff0c;各个组件、微服务的数量、深度在不断增加。由于算网是个人项目&#xff0c;我一直按照MVP(Minimum Viable Product )的原则在推进。由于最初的时候对架构、算法和业务的理解并没有那么深刻&#xff0c;所以MVP的内容还是在不断变化&…

【高阶数据结构】并查集

并查集 并查集1、概念2、根据人找编号 / 根据编号找人&#xff08;简单介绍一下并查集&#xff09;&#xff08;1&#xff09;代码展示&#xff08;2&#xff09;调试结果 3、并查集操作和演示题目&#xff08;1&#xff09;并查集操作i、思路ii、总体代码 &#xff08;2&#…

Tokitsukaze and Average of Substring

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 前缀和。 开一个int类型的前缀和数组pre[30][N]&#xff08;pre[i][j]表示某字符转成的数字 i 在一段区间的前缀个数。因为字母表有‘a’~z…

【Unity 组件思想-预制体】

【Unity 组件思想-预制体】 预制体&#xff08;Prefab&#xff09;是Unity中一种特殊的组件 特点和用途&#xff1a; 重用性&#xff1a; 预制体允许开发者创建可重复使用的自定义游戏对象。这意味着你可以创建一个预制体&#xff0c;然后在场景中多次实例化它&#xff0c;…

猿人学第七题-动态字体-随风漂移

前言&#xff1a;该题主要是考对fontTools.ttLib.TTFont的操作&#xff0c;另外就是对字典互相映射的操作 一、woff文件存储 from fontTools.ttLib import TTFont #pip install fontTools def save_woff(response):woff response[woff]woff_file base64.b64decode(woff.enc…

Java Jackson-jr 库是干什么用的

Jackson-jr 是一个轻量级的Java JSON 处理库。这个库被设计用来替代 Jackson 的复杂性。对比 Jackson 的复杂 API&#xff0c;Jackson-jr 的启动速度更快&#xff0c;包大小更小。 虽然Jackson databind&#xff08;如ObjectMapper&#xff09;是通用数据绑定的良好选择&#…

uniapp动态设置Tabbar

一套小程序及app可能会有多个用户角色&#xff0c;多者能看到的内容应该是不一样的。 实现原理 舍弃uniapp原生的tabbar&#xff0c;使用uView插件下的u-tabbar导航插件来实现。介绍 | uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架uView UI&#xff0c;是…

JavaScript百炼成仙自学笔记——2

一、循环遍历&#xff1a; 方式一 for(var i0;i<10;i){console.log(i); }方式二 var i 0; while(i < 100){console.log(i);i; }细看代码就是 先定义变量i&#xff0c;再执行{}中的代码&#xff0c;最后改循环变量的值 二、遍历 什么事遍历&#xff1f; 什么时候会用…

CMakeLists.txt语法规则:部分常用命令说明四

一. 简介 前面几篇文章学习了CMakeLists.txt语法中前面几篇文章学习了CMakeLists.txt语法中部分常用命令。文章如下&#xff1a; CMakeLists.txt语法规则&#xff1a;部分常用命令说明一-CSDN博客 CMakeLists.txt语法规则&#xff1a;部分常用命令说明二-CSDN博客 CMakeLi…

基于springboot+vue+Mysql的自习室预订系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

谈谈Tcpserver开启多线程并发处理遇到的问题!

最近在学习最基础的socket网络编程&#xff0c;在Tcpserver开启多线程并发处理时遇到了一些问题&#xff01; 说明 在linux以及Windows的共享文件夹进行编写的&#xff0c;所以代码中有的部分使用 #ifdef WIN64 ... #else ... #endif 进入正题&#xff01;&#xff01;&…