使用Qt的UDP通信协议,实现局域网通信功能(仿组播软件版本--简单)
使用Qt - udp通信方式,构建局域网聊天通信软件
1.简介效果
在之前的文章中,介绍了TCP协议在Qt中的应用实现方式传送门,TCP通信方式是面向对象,可靠的连接服务。而与之对应的是不可靠,无连接的UDP通信协议,两者是TCP/IP协议簇中较为常用的两者通信方式。
之前的博文中,实现了利用Qt调用TCP协议的局域网通信聊天,而这篇博文将介绍UDP协议在Qt中的简单运用,其想要达到的效果如下。
使用UDP就和TCP不同的是,UDP协议只管发,实用于及时通信领域,它只管发送,不管对方是否收到,但因为其传输效率高效,消耗资源小,所以尝尝结合于TCP协议一同使用在通信连接中。我们最常用的就是QQ,QQ采用TCP连接来保持在线状态,使其连接至上层的腾讯服务器,而聊天发送消息功能大都采用UDP方式,当UDP协议不能正常转发的时候,就会采用TCP协议进行发送,所以,TCP和UDP协议常常一同使用。
2.项目设计
1)流程图
UDP协议流程图,如下图所示,经此流程图后,我们可以清晰的知道,UDP是如何通信工作,并进行调用。
UDP协议没有明确的客户端和服务端可言,只要绑定了对应的IP地址和端口号,就可以进行通信,如很多的组播软件,可以使绑定同一个IP。使得多个端口号的客户在此IP下进行通信和数据传输。
2)项目构建
新建为widget项目,构建如下所示项目树
3)界面构建
两个界面设计构建如下
- 界面一 - secondwdiget
- 界面二 - widget
使用行编辑框和文本编辑框构建简单的界面如上所示。
4)代码设计
和Qt使用TCP一样,想要在Qt中使用网络编程的包。需要在.pro工程文件里面添加如下代码
QT += network
a.widget.h
首先还是需要添加Qt中封装好的UDP的头文件
#include <QUdpSocket>
之后对其进行初始化声明,并定义一个处理消息队列的槽函数,用以在UDP通信套接字连接后发送消息,之后便是重写事件过滤器用以响应键盘Enter键的输入,具体代码如下
public:
void dealMsg(); //槽函数,处理对方发过来的数据
protected:
bool eventFilter(QObject *target, QEvent *event);//事件过滤器
private:
QUdpSocket *udpSocket;
b.widget.cpp
在执行文件中,对构建函数中的udpSocket分配动态空间,当连接好之后,自动并触发连接的槽函数。
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->resize(450,300);
this->setWindowTitle("服务器端口:8888");
udpSocket = new QUdpSocket(this);
//绑定端口号
udpSocket->bind(8888);
//udpSocket->bind(QHostAddress::AnyIPv4,8888); //必须是主机的ipv4类通信方式,以及本机的ip地址
//当对方发送数据过来。自动触发readyRead()
connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::dealMsg);
//键盘Enter键设置
ui->ButtonSend->setFocus();
ui->ButtonSend->setDefault(true);
ui->textEditShow->installEventFilter(this);//设置完后自动调用其eventFilter函数
}
之后便是对自定义处理消息队列的槽函数进行函数实现
void Widget::dealMsg()
{
//读取对方发送的内容
char buf[1024] = {0}; //内容
QHostAddress cliAddr; //对方地址
quint16 port; //对方端口
qint64 len = udpSocket->readDatagram(buf,sizeof(buf),&cliAddr,&port);
//添加时间
QTime cur_time = QTime::currentTime();
QString time_info = cur_time.toString("hh:mm:ss");
if(len >0)
{
//获取到文本格式化 如 [192.168.1.1 : 8888]aaaa 的格式
QString str = QString("[%1:%2] %3 [%4]")
.arg(cliAddr.toString())
.arg(port)
.arg(buf)
.arg(time_info);
//ui->textEditShow->setText(str);
ui->textEditShow->append(str);
}
}
然后便是对事件过滤器的函数实现
bool Widget::eventFilter(QObject *target, QEvent *event)
{
if(target == ui->textEditShow) //对象为需要发送的焦点
{
if(event->type() == QEvent::KeyPress)//回车键
{
QKeyEvent *k = static_cast<QKeyEvent *>(event);
if(k->key() == Qt::Key_Return)
{
on_ButtonSend_clicked();
return true;
}
}
}
return QWidget::eventFilter(target,event);
}
最后是点击发送按钮,获取编辑框中的内容,使用通信套接字,将其发送给绑定特定端口号的一方
void Widget::on_ButtonSend_clicked()
{
//获取对方的ip和端口
QString ip = ui->lineEditIp->text();
qint16 port = ui->lineEditPort->text().toInt();
//获取编辑区内容
QString str = ui->textEditShow->toPlainText();
//给指定的ip发送数据
udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
ui->textEditShow->clear();
}
c.secondwidget.h
添加socket头文件方法和声明,以及消息队列处理函数,和键盘响应函数同上
public:
void dealMsg2(); //槽函数,处理对方发过来的数据
protected:
bool eventFilter(QObject *target, QEvent *event);//事件过滤器
private:
QUdpSocket *udpSocket;
d.secondwidget.cpp
构造函数和第一个界面有一点点不同,如下
SecondWidget::SecondWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::SecondWidget)
{
ui->setupUi(this);
this->resize(450,300);
this->setWindowTitle("服务器端口2:9999");
udpSocket2 = new QUdpSocket(this);
//绑定
udpSocket2->bind(9999);
connect(udpSocket2,&QUdpSocket::readyRead,this,&SecondWidget::dealMsg2);
ui->ButtonSend2->setFocus();
ui->ButtonSend2->setDefault(true);
//ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
ui->textEditShow2->installEventFilter(this);//设置完后自动调用其eventFilter函数
}
其他的函数实现方式和第一个界面相同,这类不再赘述。待正确编译之后,即可实现这样的效果(????????)
- 注意:Qt中UDP通信只需要在同一个ip下(需要绑定自己电脑上的ip,使用ipconfig命令查看该电脑的ipv4的地址),绑定对应的端口号。即可进行通信连接
3.源代码
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
void dealMsg(); //槽函数,处理对方发过来的数据
protected:
bool eventFilter(QObject *target, QEvent *event);//事件过滤器
private slots:
void on_ButtonSend_clicked();
void on_ButtonClose_clicked();
private:
Ui::Widget *ui;
private:
QUdpSocket *udpSocket;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
#include <QDebug>
#include <QTime>
#include <QKeyEvent>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->resize(450,300);
this->setWindowTitle("服务器端口:8888");
udpSocket = new QUdpSocket(this);
//绑定端口号
udpSocket->bind(8888);
//udpSocket->bind(QHostAddress::AnyIPv4,8888); //必须是主机的ipv4类通信方式,以及本机的ip地址
//当对方发送数据过来。自动触发readyRead()
connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::dealMsg);
ui->ButtonSend->setFocus();
ui->ButtonSend->setDefault(true);
ui->textEditShow->installEventFilter(this);//设置完后自动调用其eventFilter函数
}
Widget::~Widget()
{
delete ui;
}
void Widget::dealMsg()
{
//读取对方发送的内容
char buf[1024] = {0}; //内容
QHostAddress cliAddr; //对方地址
quint16 port; //对方端口
qint64 len = udpSocket->readDatagram(buf,sizeof(buf),&cliAddr,&port);
QTime cur_time = QTime::currentTime();
QString time_info = cur_time.toString("hh:mm:ss");
if(len >0)
{
//获取到文本格式化 [192.168.1.1 : 8888]aaaa
QString str = QString("[%1:%2] %3 [%4]")
.arg(cliAddr.toString())
.arg(port)
.arg(buf)
.arg(time_info);
//ui->textEditShow->setText(str);
ui->textEditShow->append(str);
}
}
bool Widget::eventFilter(QObject *target, QEvent *event)
{
if(target == ui->textEditShow)
{
if(event->type() == QEvent::KeyPress)//回车键
{
QKeyEvent *k = static_cast<QKeyEvent *>(event);
if(k->key() == Qt::Key_Return)
{
on_ButtonSend_clicked();
return true;
}
}
}
return QWidget::eventFilter(target,event);
}
void Widget::on_ButtonSend_clicked()
{
//获取对方的ip和端口
QString ip = ui->lineEditIp->text();
qint16 port = ui->lineEditPort->text().toInt();
//获取编辑区内容
QString str = ui->textEditShow->toPlainText();
//给指定的ip发送数据
udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
ui->textEditShow->clear();
}
void Widget::on_ButtonClose_clicked()
{
udpSocket->disconnectFromHost();
udpSocket->close();
qDebug() << "主服务器连接断开!";
this->close();
}
secondwidget.h
#ifndef SECONDWIDGET_H
#define SECONDWIDGET_H
#include <QWidget>
#include <QUdpSocket>
namespace Ui {
class SecondWidget;
}
class SecondWidget : public QWidget
{
Q_OBJECT
public:
explicit SecondWidget(QWidget *parent = nullptr);
~SecondWidget();
void dealMsg2(); //消息队列处理
protected:
bool eventFilter(QObject *target, QEvent *event);//事件过滤器
private slots:
void on_ButtonSend2_clicked();
void on_ButtonClose2_clicked();
private:
Ui::SecondWidget *ui;
private:
QUdpSocket *udpSocket2;
};
#endif // SECONDWIDGET_H
secondwidget.cpp
#include "secondwidget.h"
#include "ui_secondwidget.h"
#include <QHostAddress>
#include <QDebug>
#include <QTime>
#include <QKeyEvent>
SecondWidget::SecondWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::SecondWidget)
{
ui->setupUi(this);
this->resize(450,300);
this->setWindowTitle("服务器端口2:9999");
udpSocket2 = new QUdpSocket(this);
//绑定
udpSocket2->bind(9999);
connect(udpSocket2,&QUdpSocket::readyRead,this,&SecondWidget::dealMsg2);
ui->ButtonSend2->setFocus();
ui->ButtonSend2->setDefault(true);
//ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
ui->textEditShow2->installEventFilter(this);//设置完后自动调用其eventFilter函数
}
SecondWidget::~SecondWidget()
{
delete ui;
}
void SecondWidget::dealMsg2()
{
//读取对方发送的内容
char buf[1024] = {0}; //内容
QHostAddress cliAddr; //对方地址
quint16 port; //对方端口
qint64 len = udpSocket2->readDatagram(buf,sizeof(buf),&cliAddr,&port);
QTime cur_time = QTime::currentTime();
QString time_info = cur_time.toString("hh:mm:ss");
if(len >0)
{
//获取到文本格式化 [192.68.1.144 : 8888]aaaa
QString str = QString("[%1:%2] %3 [%4]")
.arg(cliAddr.toString())
.arg(port)
.arg(buf)
.arg(time_info);
//ui->textEditShow2->setText(str);
ui->textEditShow2->append(str);
}
}
bool SecondWidget::eventFilter(QObject *target, QEvent *event)
{
if(target == ui->textEditShow2)
{
if(event->type() == QEvent::KeyPress)//回车键
{
QKeyEvent *k = static_cast<QKeyEvent *>(event);
if(k->key() == Qt::Key_Return)
{
on_ButtonSend2_clicked();
return true;
}
}
}
return QWidget::eventFilter(target,event);
}
void SecondWidget::on_ButtonSend2_clicked()
{
//获取对方的ip和端口
QString ip = ui->lineEditIp2->text();
qint16 port = ui->lineEditPort2->text().toInt();
//获取编辑区内容
QString str = ui->textEditShow2->toPlainText();
//给指定的ip发送数据
udpSocket2->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
ui->textEditShow2->clear();
}
void SecondWidget::on_ButtonClose2_clicked()
{
udpSocket2->disconnectFromHost();
udpSocket2->close();
qDebug() << "断开连接!";
this->close();
}
如果感兴趣想要源文件的,请移至下载(????????????)源代码文件下载
上一篇: 怎么根据参数来获取相应的变量
下一篇: magento学习,该如何解决