【QT】UDP

目录

核心API

示例:回显服务器 

服务器端编写:

第一步:创建出socket对象

第二步: 连接信号槽

 第三步:绑定端口号

 第四步:编写信号槽所绑定方法

 第五步:编写第四步中处理请求的方法

客户端编写:

界面设计

代码编写

发送按钮槽函数 

处理响应函数

完整代码:

测试结果:


注意:

        使用Qt网络编程的API,需要先在 .pro文件中添加 network模块! !

QT       += core gui network

核心API

主要的类有两个  QUdpSocket 和 QNetworkDatagram,upd传输的是数据报,QNetworkDatagram就是qt中udp传输的内容;

QUdpSocket 表示⼀个 UDP 的 socket ⽂件.

名称类型说明对应原生API
bind(const QHostAddress&, quint16)
方法
绑定指定的端⼝号.
bind
receiveDatagram()
方法
返回QNetworkDatagram读取⼀个 UDP 数据报
recvfrom
writeDatagram(const QNetworkDatagram&)
方法
发送⼀个 UDP 数据报
sendto
readyRead
信号
在收到数据并准备就绪后触发
⽆ (类似于 IO 多路复⽤的通 知机制)
QNetworkDatagram 表示⼀个 UDP 数据报
名称类型说明对应原生API
QNetworkDatagram(const QByteArray&, const QHostAddress& , quint16 )
构造函
通过 QByteArray , ⽬标 IP 地址, ⽬标端⼝号 构造⼀个 UDP 数据报. 通常⽤于发送数据时.
data()
方法
获取数据报内部持有的数据. 返回
QByteArray
senderAddress()
方法
获取数据报中包含的对端的 IP 地
址.
senderPort()
方法
获取数据报中包含的对端的端⼝号.
;无

在编写udp相关代码时,注意事项:

  •  一定要先连接信号槽,再绑定端口号,一旦绑定端口了,意味着请求就可以被收到了,如果在完成绑定之后,在连接信号槽之前,有客户端把请求发过来了,此时就可能读不到这样的请求。
  •  一个端口号只能被一个socket绑定。 
  •  socket->errorString() 本质上也是对系统的errno机制进行封装

示例:回显服务器 

服务器端编写:

第一步:创建出socket对象

socket = new QUdpSocket(this);

第二步: 连接信号槽

connect(socket , &QUdpSocket::readyRead , this , &Widget::processRequest);

 第三步:绑定端口号

这里记得判断是否绑定成功,如果端口号被其他socket所绑定,我们就不能在绑定该端口号!

    bool ret = socket->bind(QHostAddress::Any,9090);

    if(!ret)

    {

        //绑定失败

        QMessageBox::critical(this,"服务器启动出错",socket->errorString());

        return;

    }

 第四步:编写信号槽所绑定方法

void Widget::processRequest()

{

    //1.读取请求并解析

    const QNetworkDatagram& requestDatagram = socket->receiveDatagram();

    QString request = requestDatagram.data(); //这里data()返回的是QByte数据

    //2.根据请求计算响应(由于这里仅仅是回显服务器,响应不需要计算,就是请求本身)

    const QString& response = process(request);

    //3.把响应写回客户端,响应数据包(数据是啥,客户端ip地址,客户端端口)

    QNetworkDatagram responseDatagram(response.toUtf8(),requestDatagram.senderAddress(),requestDatagram.senderPort());

    //response.toUtf8() 取出QString内部的字节数组

    socket->writeDatagram(responseDatagram);

    //4.把这次交互的信息,显示到界面上

    QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort())+

            "] req:" + request + ", resp:" + response;

    ui->listWidget->addItem(log);

}

 第五步:编写第四步中处理请求的方法

QString Widget::process(const QString &request)

{

    //请求处理过程,由于当前是回显服务i,响应和请求完全一样,不需要进行处理

    return request;

}

完整代码如下: 

#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QNetworkDatagram>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //创建出这个对象
    socket = new QUdpSocket(this);

    //设置窗口标题
    this->setWindowTitle("服务器");

    //连接信号槽
    connect(socket,&QUdpSocket::readyRead,this,&Widget::processRequest);

    //绑定端口号
    bool ret = socket->bind(QHostAddress::Any,9090);
    if(!ret)
    {
        //绑定失败
        QMessageBox::critical(this,"服务器启动出错",socket->errorString());
        return;
    }

}

Widget::~Widget()
{
    delete ui;
}

void Widget::processRequest()
{
    //1.读取请求并解析
    const QNetworkDatagram& requestDatagram = socket->receiveDatagram();
    QString request = requestDatagram.data();

    //2.根据请求计算响应(由于这里仅仅是回显服务器,响应不需要计算,就是请求本身)
    const QString& response = process(request);

    //3.把响应写回客户端,响应数据包(数据是啥,客户端ip地址,客户端端口)
    QNetworkDatagram responseDatagram(response.toUtf8(),requestDatagram.senderAddress(),requestDatagram.senderPort());
    //response.toUtf8() 取出QString内部的字节数组
    socket->writeDatagram(responseDatagram);
    //4.把这次交互的信息,显示到界面上
    QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort())+
            "] req:" + request + ", resp:" + response;

    ui->listWidget->addItem(log);
}

QString Widget::process(const QString &request)
{
    //请求处理过程,由于当前是回显服务i,响应和请求完全一样,不需要进行处理
    return request;
}




客户端编写:

界面设计

我们客户端简单设计成如下:

效果如下:

端口号本质上是一个2字节的无符号整数  。

代码编写

首先我们写定义两个常量,描述服务器的地址和端口:

const QString& SERVER_IP = "127.0.0.1";
const quint16 SERVER_PORT = 9090;

发送按钮槽函数 

void Widget::on_pushButton_clicked()

{

    //1.获取输入框的内容

    const QString& text = ui->lineEdit->text();

    //2. 构造UDP请求数据,这里ip我们是QString,参数识别不出来,需要我们进行转换

    QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP),SERVER_PORT);

    //3. 发送请求数据

    socket->writeDatagram(requestDatagram);

    //4. 把发送的请求数据也添加到列表框中

    ui->listWidget->addItem("客户端说:" + text);

    //5. 把输入框的内容也清空一下,方便下次输入

    ui->lineEdit->setText("");

}

处理响应函数

void Widget::processHandle()

{

    //通过这个函数来处理收到的响应

    //1.读取到响应的数据

    const QNetworkDatagram& responseDatagram = socket->receiveDatagram();

    //这里不用换引用是因为.data()返回的是QByte类型,涉及类型转换

    QString response =responseDatagram.data();

    //2. 把响应数据显示到界面上

    ui->listWidget->addItem("服务器说:"+response);

}

完整代码:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
    void processHandle();
private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QUdpSocket* socket;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QNetworkDatagram>
const QString& SERVER_IP = "127.0.0.1";
const quint16 SERVER_PORT = 9090;
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    socket = new QUdpSocket(this);

    //修改窗口标题,区分这是一个客户端程序
    this->setWindowTitle("客户端");

    //通过信号槽,来处理服务器返回的数据
    connect(socket,&QUdpSocket::readyRead,this,&Widget::processHandle);

}
Widget::~Widget()
{
    delete ui;
}


void Widget::processHandle()
{
    //通过这个函数来处理收到的响应
    //1.读取到响应的数据
    const QNetworkDatagram& responseDatagram = socket->receiveDatagram();
    QString response =responseDatagram.data();
    //2. 把响应数据显示到界面上
    ui->listWidget->addItem("服务器说:"+response);
}

void Widget::on_pushButton_clicked()
{
    //1.获取输入框的内容
    const QString& text = ui->lineEdit->text();
    //2. 构造UDP请求数据
    QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP),SERVER_PORT);
    //3. 发送请求数据
    socket->writeDatagram(requestDatagram);
    //4. 把发送的请求数据也添加到列表框中
    ui->listWidget->addItem("客户端说:" + text);
    //5. 把输入框的内容也清空一下,方便下次输入
    ui->lineEdit->setText("");
}

测试结果:

要想开启多个客户端,我们只需要找到项目对应的文件中,运行.exe文件即可 

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值