参考up主:社长_嵌入式
一:学到的重点
1 serve->listen(host,8888)
主机的地址 QHostAddress host("192.168.26.5"); 8888设置主机的端口号
在 Qt 里,serve->listen()
通常是 QTcpServer
类对象调用 listen()
函数。QTcpServer
类的作用是创建一个 TCP 服务器,它可以监听特定的地址和端口,等待客户端的连接请求。
2 &QTcpServer::newConnection
newConnection
:这是 QTcpServer
类中定义的一个信号。当有新的客户端连接到服务器时,QTcpServer
对象就会发出 newConnection
信号。
3 serve->hasPendingConnections()
- 功能:调用
QTcpServer
类的hasPendingConnections()
函数,用于检查服务器是否有等待处理的客户端连接请求。 - 返回值:若存在待处理的连接,返回
true
;反之则返回false
。 - 逻辑:若有等待处理的连接,执行
if
语句块里的代码。
4 QTcpSocket *connect = serve->nextPendingConnection();
- 功能:调用
QTcpServer
类的nextPendingConnection()
函数,其作用是从等待处理的连接队列中取出下一个连接,并返回一个指向QTcpSocket
对象的指针。这个QTcpSocket
对象代表了与客户端的连接,可用于和客户端进行数据的读写操作。 - 变量:
connect
是一个指向QTcpSocket
对象的指针,借助它就能对与客户端的连接进行操作。 - 注意事项:需要注意的是,
connect
是 C++ 的关键字,使用它作为变量名会引发编译错误,建议使用其他名称,例如socket
。
5 QNetworkInterface::allAddresses();
得到主机上的所有地址
6 QTcpSocket *temp=qobject_cast<QTcpSocket*>(sender());
在完成串口的时候也用到了(主要找到信号的发出者)
在代码 QTcpSocket *temp = qobject_cast<QTcpSocket*>(sender());
中:
sender()
会返回发出当前信号的对象的指针。qobject_cast<QTcpSocket*>(sender())
会尝试把这个指针转换为QTcpSocket*
类型。如果发出信号的对象确实是QTcpSocket
类型或者是其子类,就会返回指向该对象的QTcpSocket*
指针;若不是,就会返回nullptr
。- 最后把转换后的指针赋值给
temp
,这样在后续代码中就可以使用temp
指针来操作这个QTcpSocket
对象了,比如读取数据、发送数据等。
二 网络调试助手(服务端)
1 完成界面设计
2 创建服务对象和实现简单通讯
这段代码的主要作用是创建一个 TCP 服务器,监听指定的 IP 地址(192.168.26.5
)和端口号(8888
),当有新的客户端连接到服务器时,输出客户端的 IP 地址和端口号。它利用了 Qt 的 QTcpServer
类和信号与槽机制,实现了简单的服务器端通信功能。不过需要注意的是,代码中的 QTcpSocket *connect
变量名使用了 C++ 关键字 connect
,建议将其改为其他名称,避免编译错误。
#include "widget.h"
#include "ui_widget.h"
#include"QTcpSocket"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//初始化网络服务对象
serve=new QTcpServer(this);
//主机的地址
QHostAddress host("192.168.26.5");
//检查是否监听成功 同时设置主机的地址和主机的端口号
if(!serve->listen(host,8888)){
qDebug()<<"listen Error";
return;
}
//建立连接
connect(serve,&QTcpServer::newConnection,this,&Widget::on_newClient_connect);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_newClient_connect()
{
qDebug()<<"IN NewConnect to Clicent";
//检查服务器是否有等待处理的客户端连接请求。
if(serve->hasPendingConnections()){
//调用 QTcpServer 类的 nextPendingConnection() 函数,其作用是从等待处理的连接队列中取出下一个连接,
//并返回一个指向 QTcpSocket 对象的指针。这个 QTcpSocket 对象代表了与客户端的连接,可用于和客户端进行数据的读写操作
QTcpSocket *connect=serve->nextPendingConnection();
qDebug()<<"Client address"<<connect->peerAddress()<<"Clinet port"<<connect->peerPort();
}
}
3 结合ui界面上的按钮实现上面功能
#include "widget.h"
#include "ui_widget.h"
#include"QTcpSocket"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//初始化网络服务对象
serve=new QTcpServer(this);
//建立连接
connect(serve,&QTcpServer::newConnection,this,&Widget::on_newClient_connect);
//监听过过后才可以选择
ui->btn_stop->setEnabled(false);
ui->btn_break->setEnabled(false);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_newClient_connect()
{
qDebug()<<"IN NewConnect to Clicent";
//检查服务器是否有等待处理的客户端连接请求。
if(serve->hasPendingConnections()){
//调用 QTcpServer 类的 nextPendingConnection() 函数,其作用是从等待处理的连接队列中取出下一个连接,
//并返回一个指向 QTcpSocket 对象的指针。这个 QTcpSocket 对象代表了与客户端的连接,可用于和客户端进行数据的读写操作
QTcpSocket *connect=serve->nextPendingConnection();
//在接收text上显示客户端的信息
ui->textEdit_rec->setText("客户端的地址:"+connect->peerAddress().toString()+"\n客户端端口号:"+QString::number(connect->peerPort()));
}
}
void Widget::on_btn_start_clicked()
{
//serve->listen(QHostAddress::Any,8888); QHostAddress::Any自动获取当前主机地址
//主机的地址
QHostAddress host("192.168.26.5");
//检查是否监听成功 同时设置主机的地址和主机的端口号
if(!serve->listen(host,ui->lineEdit_port->text().toInt())){
qDebug()<<"listen Error";
return;
}
//监听按钮不可选其他可选
ui->btn_start->setEnabled(false);
ui->btn_stop->setEnabled(true);
ui->btn_break->setEnabled(true);
}
4 服务端自动刷新IPV4地址
//创建存储能ip的列表
//QNetworkInterface::allAddresses();得到主机上的所有地址
QList <QHostAddress> list_add=QNetworkInterface::allAddresses();
for(QHostAddress add : list_add){
//是否符合IPV4的格式符合就加入
if(add.protocol()==QAbstractSocket::IPv4Protocol){
ui->comboBox_IP->addItem(add.toString());
}
}
5 服务端接收数据
void Widget::on_receive()
{
//因为connection是局部变量 所以要得到信号的发出者connection应该这样做
QTcpSocket *temp=qobject_cast<QTcpSocket*>(sender());
//接收客户端的数据
QByteArray arr= temp->readAll();
//服务端显示数据 并转化为utf-8
ui->textEdit_rec->append(QString::fromUtf8(arr));
}
6 实现接收数据的时候显示时间
//设置时间对象调用QDateTime::currentDateTime()获取当前时间
QDateTime CurrentTime=QDateTime::currentDateTime();
//在你给出的代码里,toString 是 QDateTime 类的一个成员函数,其作用是把 QDateTime 对象转换为特定格式的字符串。
QString time_str=CurrentTime.toString("yyyy-MM-dd hh:mm:ss");
ui->textEdit_rec->append(time_str+"\n"+QString::fromUtf8(arr));
7 服务端发送数据
//发送按钮功能实现
void Widget::on_pushButton_sent_clicked()
{
//得到输入的文字
// QString sent_str=ui->textEdit_sent->toPlainText();
// if(!sent_str.isEmpty()){
// for(QTcpSocket *tcp:List_Tcp){
// //已经连接
// if(tcp->state()==QAbstractSocket::ConnectedState){
// tcp->write(sent_str.toUtf8());
// //在 Qt 的网络编程中,socket->flush();
// //是 QTcpSocket 类对象调用的一个成员函数,其主要作用是强制将输出缓冲区中的数据立即发送到网络中。
// tcp->flush();
// }
// }
// }
//还可以通过
// 这行代码把 serve 对象及其所有子对象中找到的 QTcpSocket 对象的指针存储在 stocket 列表中;
//stocket存储着多个客户端都可以进行接收服务端放的数据
QList<QTcpSocket*> stocket= serve->findChildren<QTcpSocket*>();
for(QTcpSocket *tcp:stocket){
tcp->write(ui->textEdit_sent->toPlainText().toUtf8());
}
}
8 服务端选择特点的客户端(自定义控件实现)
将该控件提升为MyComBoBox
断开连接的时候会出现端口号0 利用tcp!=nullptr&&tcp->peerPort()!=0可以解决 (没有根本解决 只是页面上不显示其实还存在在)根本解决在断开的时候删除connection
//刷新连接的端口
void Widget::on_refresh()
{
//先刷新
ui->comboBox_choose->clear();
// 这行代码把 serve 对象及其所有子对象中找到的 QTcpSocket 对象的指针存储在 stocket 列表中;
QList<QTcpSocket*> sto=serve->findChildren<QTcpSocket*>();
for(QTcpSocket *tcp:sto){
//显示端口号
//断开连接的时候会出现端口号0 利用tcp!=nullptr&&tcp->peerPort()!=0可以解决 (没有根本解决 只是页面上不显示其实还存在在)
//根本解决在断开的时候删除connection
if(tcp!=nullptr&&tcp->peerPort()!=0){
ui->comboBox_choose->addItem(QString::number(tcp->peerPort()));
}
}
ui->comboBox_choose->addItem("ALL");
}
根本解决在断开的时候删除connection
9 中止和监听按钮实现
//中止
void Widget::on_btn_stop_clicked()
{
//每一个都要停止
QList<QTcpSocket*> sto=serve->findChildren<QTcpSocket*>();
for(QTcpSocket *tcp:sto){
tcp->close();
}
//最后服务端也要停止
serve->close();
ui->btn_stop->setEnabled(false);
ui->btn_break->setEnabled(false);
ui->btn_start->setEnabled(true);
}
//中断
void Widget::on_btn_break_clicked()
{
//中断先停止
on_btn_stop_clicked();
//删除服务端
serve->deleteLater();
//关闭窗口
this->close();
}