Qt网络编程2-基于UDP编程
1 UDP概述:
用户数据报协议(User data Protocol)是一种简单轻量级,不可靠,面向数据,无连接的传输层协议.可用在可靠性不是十分重要的场合.如短消息 广播信息等
适合应用的情况有一下几种:
- 网络数据大多为短消息
- 拥有大量的客户端
- 对数据安全性无特殊要求
- 网络负担非常重,但对响应速度要求高.
2 UDP工作原理
UDP客户端向UDP服务器发送一定长度的请求报文,报文大小限制和各系统的协议实现有关,但不能超过其下层规定的64kB,UDP服务器同样以报文的形式做出响应.但如果服务器为收到请求,UDP客户端是不会进行重发的.因此报文的传输不可靠.
3 UDP的编程模型
3.1 客户端的编程模型
1 初始化套接字
2 向服务器发送数据
3 接收服务器发送的数据
4 关闭套接字
3.2 服务器的编程模型
1 .初始化套接字
2 绑定ip地址.端口号
3 接收数据,阻塞等待直到收到客户数据
4 处理请求,发送数据
4 Qt中的UDP编程
Qt中的UDP编程是通过QUdpSocket实现的.使用这个类的方法是先绑定Ip地址和端口号,使用bind函数,然后调用writeDatagram()和readDatagram/receiveDatagram完成数据的传输.
4.1 相关成员函数介绍
1)bind()
//绑定ip地址和端口号
bool QAbstractSocket::bind(const QHostAddress &address, quint16 port = 0)
2)writeDatagram()
//向指定ip地址和端口号的主机中发送数据
qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)
发送的数据超过512个字节.通常是被阻止的,即使成功发送,也会被ip层分裂.
3 )readDatagram
如果maxSize的值太小,数据报中剩余的部分会被丢弃.为了避免这种情况,可以调用pendingDatagramSize()返回数据包中第一个待接收的数据报的大小.
qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr)
4 )hasPendingDatagrams()
判断是否有等待读取的数据报
如果至少有一个数据报等待被读取,返回true.
bool QUdpSocket::hasPendingDatagrams() const
5 )pendingDatagramSize()
返回第一个未读取数据报的大小,如果没有数据报 函数返回-1
qint64 QUdpSocket::pendingDatagramSize() const
5 UDP广播程序
接收端源码:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget),isStarted(false),port(8888)
{
ui->setupUi(this);
udpSocket = new QUdpSocket(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_startBtn_clicked()
{
if(!isStarted){
isStarted = true;
ui->startBtn->setText("停止接收");
ui->rcvlineEdit->setEnabled(false);
port = ui->rcvlineEdit->text().toShort();
if(udpSocket->bind(port) == false)
qDebug() << "绑定端口号失败";
connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::receiveMessage);
}
else{
isStarted = false;
ui->startBtn->setText("开始接收");
ui->rcvlineEdit->setEnabled(true);
udpSocket->close();
}
}
void Widget::receiveMessage(void){
//判断是否有等待读取的数据报
if(udpSocket->hasPendingDatagrams()){
QByteArray buf;
//获取数据包的大小
buf.resize(udpSocket->pendingDatagramSize());
//读取数据包 data 数据转换为char*
udpSocket->readDatagram(buf.data(),buf.size());
//qDebug() << buf.data();
ui->listWidget->addItem(buf);
//滚动到最底部
ui->listWidget->scrollToBottom();
}
}
发送端源码
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget),isStarted(false)
{
setWindowTitle("广播接收端");
udpSocket = new QUdpSocket(this);
timer = new QTimer(this);
ui->setupUi(this);
//定时器到时发送timeout信号
connect(timer,&QTimer::timeout,this,&Widget::sendMessage);
}
Widget::~Widget()
{
delete ui;
}
//开始广播
void Widget::on_startBtn_clicked()
{
if(!isStarted){
isStarted = true;
ui->startBtn->setText("停止广播");
ui->msgLineEdit->setEnabled(false);
ui->portLineEdit->setEnabled(false);
//开始定时器 ,每个1秒广播一次
timer->start(1000);
}
else{
isStarted=false;
ui->startBtn->setText("开始广播");
ui->msgLineEdit->setEnabled(true);
ui->portLineEdit->setEnabled(true);
timer->stop();
}
}
void Widget::sendMessage(void){
quint16 port = ui->portLineEdit->text().toShort();
QString msg = ui->msgLineEdit->text();
//发送消息 toUtf8转换为QByteArray 字符数组 不要把msg.lenth()加进去 否则会导致部分数据接收不完整
qint64 len = udpSocket->writeDatagram(msg.toUtf8(),QHostAddress::Broadcast,port);
}
6 运行结果