运行环境:win10
QT版本:5.11.3
最近在研究QT如何将局域网的设备IP显示在列表中,然后通过双击打开,调用web浏览器访问一个固定的网址。因为这里是特定的设备,并且是局域网中的,那么我们可以通过设计一个协议来处理这个问题。什么协议最适合这样的场景呢?熟悉网络协议的童鞋一直知道UDP有个广播地址。当广播一条消息时,处于统一局域网中的UDP的客户端或者是服务器都能收到这条消息。知道大概的方向后,下面就是开始撸代码了:
首先,先打开QT Creator,新建一个工程,暂且名字为qt_iot_dev_find。建立好工程后,我们打开工程中mainwindows.ui,进行页面布局。这里的话,涉及的业务比较简单,就三个控件,页面布局如下:
因为这个工程要涉及到网络编程,所以我们要打开工程文件qt_iot_dev_find.pro,在第8行添加一行代码:
QT += network
添加完了之后,我们打开mainwindow.h头文件,修改头文件如下:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QUdpSocket>//添加头文件
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void list_init();//列表初始化
void udp_server_init();//upd服务器初始化
void iot_dev_ip_item_add(QString ip,QString data);//将获取到的ip和数据,加入到列表中
QUdpSocket *udp_server;
private:
Ui::MainWindow *ui;
//添加槽函数
private slots:
void udp_receive_data_handler();//自己添加的slot,在收到upd消息后,会自动调用该函数
void on_search_dev_pushButton_clicked();//该函数是自动添加的,按键连接的槽函数
};
#endif // MAINWINDOW_H
然后打开mainwindow.cpp,修改cpp文件如下:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QListWidgetItem> //添加头文件,我们是一个item一个item的插入
#define UDP_SERVER_PORT 12345
#define VALID_DATA "ping_request" //有效数据.当前udp服务器认的有效数据,客户端只需要发这个,就可以显示在列表中
#define MAX_NUM 5 //显示最大为5,这个随意更改
static unsigned char g_connect_count = 0;//连接数统计
static QString ip_all[MAX_NUM][32];//用来缓存连接的ip
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
list_init();
udp_server_init();
}
MainWindow::~MainWindow()
{
delete ui;
}
//列表初始化
void MainWindow::list_init()
{
//初始化列表头,暂定为ip_addr和data
QListWidgetItem *list_head = new QListWidgetItem(" ip_addr \t data",ui->dev_infor_listWidget);
ui->dev_infor_listWidget->insertItem(1,list_head);
//
}
//Upd服务器初始化
void MainWindow::udp_server_init()
{
//创建一个udp server对象
udp_server = new QUdpSocket(this);
//绑定端口号为12345
udp_server->bind(UDP_SERVER_PORT,QUdpSocket::ShareAddress);
//连接到slots,有客户端的消息到来时,会自动调用udp_receive_data_handler
connect(udp_server,SIGNAL(readyRead()),this,SLOT(udp_receive_data_handler()));
}
void MainWindow::udp_receive_data_handler()
{
QHostAddress udp_client_address;//声明一个QHostAddress对象
unsigned short int udp_client_port = 0;//客户端端口号
QByteArray udp_datagram;//upd client 发来的数据,里面包含内容和大小
while( udp_server->hasPendingDatagrams() )
{
udp_datagram.resize( udp_server->pendingDatagramSize());
udp_server->readDatagram(udp_datagram.data(),udp_datagram.size(), &udp_client_address, &udp_client_port);
//比较数据是否为有效数据
if(strcmp(udp_datagram.data(),VALID_DATA) == 0)
{
//注意这里的ip显示的格式是这样的,我们需要转换一下"::ffff:192.168.xxx.xxx" ==> "192.168.xxx.xxx"
QString ip_addr_temp = udp_client_address.toString();
QString ip_addr;
int i = 0;
//填充ip地址
for(i = 7; ip_addr_temp[i] != '\0'; i ++ )
{
ip_addr[i-7] = ip_addr_temp[i];
}
//插入列表
iot_dev_ip_item_add(ip_addr + QString(":")+ QString().number(udp_client_port),udp_datagram.data());
}
//非法数据,不处理
}
}
//将获取到的ip,加入到列表中
void MainWindow::iot_dev_ip_item_add(QString ip,QString data)
{
//大于最大连接数,则不进行插入
if(g_connect_count >= MAX_NUM)
return ;
//相同IP和端口,只显示一个,这个值是不会重复的
for(unsigned char i = 0; i < MAX_NUM; i ++)
{
if(ip == *ip_all[i])
{
//这里添加调试信息打印
qDebug() << "*****************************";
qDebug() << "i = " << i;
qDebug() << "buff:client_addr:" << *ip_all[i];
qDebug() << "current client_addr:" << ip;
return;
}
}
//没有找到相同的,插入
ip_all[g_connect_count][0] = ip;
g_connect_count = g_connect_count + 1;
QListWidgetItem *list_iot_dev = new QListWidgetItem(ip + QString("\t") + data,ui->dev_infor_listWidget);
ui->dev_infor_listWidget->insertItem(1,list_iot_dev);
}
void MainWindow::on_search_dev_pushButton_clicked()
{
for(unsigned char i = 0; i < g_connect_count; i ++)
{
ip_all[i][0] = "";//清空缓冲区
}
g_connect_count = 0;
ui->dev_infor_listWidget->clear();
list_init();//重新初始化
}
测试情况如下:(测试客户端使用通信猫,后续的话,我会将客户端这部分的代码进行补充,现在还没有写好)
运行程序,效果如下:
打开通信猫软件,选择udp,然后设置广播的方式:
设计的知识归纳:
1.QT UDP的使用
2.QListWidget的使用
3.QListWidgetItem的使用
使用例子都在源代码里了。好好码一次,加深理解即可~~~~
如果这篇文章,对你有帮助,就点个赞再走哈~~~