首页 > 编程语言 >QT UDP通信聊天程序(单播、广播、组播)

QT UDP通信聊天程序(单播、广播、组播)

时间:2022-08-29 09:57:18浏览次数:80  
标签:UDP 组播 QT void 端口 Widget ui udpSocket

QT UDP通信(单播、广播、组播)

  日期:2021-03-26     浏览:126    评论:0     核心提示:1.QUdpSocketUDP是轻量的、不可靠的、面向数据报、无连接的协议,它可以用于对可靠性要求不高的场合,与TCP通信不同,无需建立持久的socket连接。QUdpSocket用于实现UDP通信,与QTcpSocket主要区别是,QUdpSocket以数据报传输数据,而不是以连续的数据流。发送数据使用writeDatagram(),数据报的长度一般少于512字节,每个数据报包含发送者和接收者的IP地址和端口等信息。要进行UDP通信,首先需要bind函数绑定一个端口,用于接收传入的手机不能。当

1.QUdpSocket

UDP是轻量的、不可靠的、面向数据报、无连接的协议,它可以用于对可靠性要求不高的场合,与TCP通信不同,无需建立持久的socket连接。

QUdpSocket用于实现UDP通信,与QTcpSocket主要区别是,QUdpSocket以数据报传输数据,而不是以连续的数据流。发送数据使用writeDatagram(),数据报的长度一般少于512字节,每个数据报包含发送者和接收者的IP地址和端口等信息。

要进行UDP通信,首先需要bind函数绑定一个端口,用于接收传入的手机不能。当有数据报传入时会发射readyRead()信号,使用readDatagram()来读取接收到的数据报。

UDP消息传送有单播、广播、组播三种模式。

  • 单播:一个UDP客户端发出的数据报只发送到另一个指定地址和端口的UDP客户端,是一对一的数据传输。
  • 广播:一个UDP客户端发出的数据报,在同一网络范围内其他所有的UDP客户端都可以收到。
  • 组播:也称多播,UDP客户端加入到另一个组播IP地址指定的多播组,成员向组播地址发送的数据报组内成员都可以接收到,类似于QQ群功能。

在单播、广播和多播下,UDP程序都是对等的,不像TCP那样分为客户端和服务器端。单播和广播的实现方式基本相同,只是数据报的目标IP地址设置不同,多播模式需要加入多播组,实现方式有较大差异。

2.单播/广播

本机运行两个实例需要绑定不同的端口,例如实例A绑定端口2000,实例B绑定端口3000,实例A向实例B发送数据时,需要指定实例B所在主机的IP地址、绑定端口作为目标地址和目标端口,这样实例B才能接收到数据报。如果在不同的计算机运行,则可以使用相同的端口。

  • 要实现数据接收,必须先使用QUdpSocket::bind()绑定一个端口,用于监听传入的数据报。解除绑定则使用abort()函数。
  • writeDatagram()函数向一个目标用户发送消息时,需要指定目标地址和端口。
  • 在广播消息时,只需要将目标地址更换为一个特殊地址,即广播地址QHostAddress::Broadcast,一般为255.255.255.255
  • 发送的数据报是QByteArray类型的字节数组,数据报的长度一般不超过512字节,可以是文本,也可以是二进制数据。
  • 接收到数据报后会发射readyRead()信号。
#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 = 0);
    ~Widget();

private slots:
    void on_btnBind_clicked();

    void on_btnUnBind_clicked();

    void on_btnClear_clicked();

    void on_btnQuit_clicked();

    void on_btnSend_clicked();

    void on_btnBroadcast_clicked();

    void on_readyRead();
private:
    Ui::Widget *ui;

private:
    QUdpSocket *m_udpSocket = nullptr;
};

#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QHostInfo>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);


    //本地主机名
    QString hostName = QHostInfo::localHostName();

    //本机IP地址
    QHostInfo hostInfo = QHostInfo::fromName(hostName);

    //IP地址列表
    QList<QHostAddress> addrList = hostInfo.addresses();
    for(int i=0;i<addrList.count();i++)
    {
        QHostAddress host = addrList.at(i);

        if(QAbstractSocket::IPv4Protocol == host.protocol())
        {
            QString ip = host.toString();
            ui->comboBox->addItem(ip);
        }
    }

    m_udpSocket = new QUdpSocket(this);
    connect(m_udpSocket,&QUdpSocket::readyRead,this,&Widget::on_readyRead);

}

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

void Widget::on_btnBind_clicked()
{
    //本机UDP端口
    qint16 port = ui->spinBindPort->value();

    if(m_udpSocket->bind(port))
    {
        ui->plainTextEdit->appendPlainText("**已成功绑定");
        ui->plainTextEdit->appendPlainText("**绑定端口: "+QString::number(m_udpSocket->localPort()));
        ui->btnBind->setEnabled(false);
        ui->btnUnBind->setEnabled(true);
    }
    else
    {
         ui->plainTextEdit->appendPlainText("**绑定失败");
    }
}

void Widget::on_btnUnBind_clicked()
{
    //解除绑定
    m_udpSocket->abort();
    ui->btnBind->setEnabled(true);
    ui->btnUnBind->setEnabled(false);
    ui->plainTextEdit->appendPlainText("**已解除绑定");
}

void Widget::on_btnClear_clicked()
{
    ui->plainTextEdit->clear();
}

void Widget::on_btnQuit_clicked()
{

}

void Widget::on_btnSend_clicked()//单播
{
    //目标IP
    QString dstIp = ui->comboBox->currentText();

    QHostAddress dstAddr(dstIp);

    //目标端口
    quint16 dstPort = ui->spinDstPort->value();

    QString msg = ui->lineEdit->text();
    QByteArray str = msg.toUtf8();

    //发出数据报
    m_udpSocket->writeDatagram(str,dstAddr,dstPort);

    ui->plainTextEdit->appendPlainText("[out] "+msg);
}

void Widget::on_btnBroadcast_clicked()//广播
{
    quint16 dstPort = ui->spinDstPort->value();

    QString msg = ui->lineEdit->text();
    QByteArray str = msg.toUtf8();

    //发出数据报
    m_udpSocket->writeDatagram(str,QHostAddress::Broadcast,dstPort);

    ui->plainTextEdit->appendPlainText("[Broadcast] "+msg);
}

void Widget::on_readyRead()
{
    //是否还有待读取的传入数据报
    while(m_udpSocket->hasPendingDatagrams())
    {
        QByteArray data;

        //返回待读取的数据报的字节数
        data.resize(m_udpSocket->pendingDatagramSize());

        QHostAddress peerAddr;

        quint16 peerPort;

        //读取数据报的内容
        m_udpSocket->readDatagram(data.data(),data.size(),&peerAddr,&peerPort);

        QString str = data.data();

        QString peer = "[From ] +"+peerAddr.toString()+":"+QString::number(peerPort)+"] ";

        ui->plainTextEdit->appendPlainText(peer+str);
    }
}

3.组播

组播是主机之间“一对一组”的通信模式,当多个客户端加入由一个组播地址定义的多播组之后,客户端向组播地址和端口发送数据报,组内成员都可以接收到,类似QQ群。

UDP组播必须使用一个组播地址。组播地址是D类IP地址,有特定的地址段。多播组可以是永久的也可以是临时的。永久多播组保持不变的是它的IP地址,组内成员结构可以发生变化。

  • 224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其他地址供路由协议使用
  • 224.0.1.0~224.0.1.255是公用组播地址,可以用于internet
  • 224.0.2.0~238.255.255.255为用户可用的组播地址(临时组地址),全网范围内有效
  • 239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效

若在家庭或办公室局域网内测试UDP组播功能,可以使用组播地址范围是239.0.0.0~239.255.255.255

joinMulticastGroup()函数使主机加入一个多播组,leaveMulticastGroup()函数使主机离开一个多播组。

UDP组播的特点是使用组播地址,其他的端口绑定、数据报收发功能与单播UDP完全相同。

#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 = 0);
    ~Widget();

private slots:
    void on_btnClear_clicked();

    void on_btnSend_clicked();

    void on_readyRead();
    void on_btnJoin_clicked();

    void on_btnExit_clicked();

private:
    Ui::Widget *ui;

private:
    QUdpSocket *m_udpSocket = nullptr;

    QHostAddress groupAddr; //组播地址
};

#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QHostInfo>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    //本地主机名
    QString hostName = QHostInfo::localHostName();

    m_udpSocket = new QUdpSocket(this);

    //socket QAbstractSocket::MulticastTtlOption值为1,MulticastTtlOption是
    //组播的数据的生存期,数据报没跨1个路由就会减1.表示多播数据报只能在同一路由下的局域网内传播
    m_udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);
    connect(m_udpSocket,&QUdpSocket::readyRead,this,&Widget::on_readyRead);

}

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


void Widget::on_btnClear_clicked()
{
    ui->plainTextEdit->clear();
}

void Widget::on_btnSend_clicked()//组播
{
    //目标端口
    quint16 groupPort = ui->spinBindPort->value();

    QString msg = ui->lineEdit->text();
    QByteArray str = msg.toUtf8();

    //发出数据报
    m_udpSocket->writeDatagram(str,groupAddr,groupPort);

    ui->plainTextEdit->appendPlainText("[multicast] "+msg);
}

void Widget::on_readyRead()
{
    while(m_udpSocket->hasPendingDatagrams())
    {
        QByteArray data;
        data.resize(m_udpSocket->pendingDatagramSize());

        QHostAddress peerAddr;

        quint16 peerPort;

        m_udpSocket->readDatagram(data.data(),data.size(),&peerAddr,&peerPort);

        QString str = data.data();

        QString peer = "[From ] +"+peerAddr.toString()+":"+QString::number(peerPort)+"] ";

        ui->plainTextEdit->appendPlainText(peer+str);
    }
}

void Widget::on_btnJoin_clicked()
{
    QString IP = ui->comboBox->currentText();
    groupAddr = QHostAddress(IP);

    quint16 groupPort = ui->spinBindPort->value();
    //加入组播之前,必须先绑定端口,端口为多播组统一的一个端口。
    if(m_udpSocket->bind(QHostAddress::AnyIPv4,groupPort,QUdpSocket::ShareAddress))
    {
        //加入组播
        m_udpSocket->joinMulticastGroup(groupAddr);

        ui->plainTextEdit->appendPlainText("**加入组播成功");
        ui->plainTextEdit->appendPlainText("**组播IP: "+ IP);
        ui->plainTextEdit->appendPlainText("**绑定端口: "+QString::number(groupPort));
        ui->btnJoin->setEnabled(false);
        ui->btnExit->setEnabled(true);
        ui->comboBox->setEnabled(false);
    }
    else
    {
        ui->plainTextEdit->appendPlainText("**绑定端口失败");
    }
}

void Widget::on_btnExit_clicked()
{
    //退出组播
    m_udpSocket->leaveMulticastGroup(groupAddr);

    //解除绑定
    m_udpSocket->abort();

    ui->btnJoin->setEnabled(true);

    ui->btnExit->setEnabled(false);
    ui->comboBox->setEnabled(true);

    ui->plainTextEdit->appendPlainText("**已退出组播");
}

 

  标签: 本文链接:https://blog.csdn.net/wzz953200463/article/details/115101133

标签:UDP,组播,QT,void,端口,Widget,ui,udpSocket
From: https://www.cnblogs.com/managechina/p/16634864.html

相关文章

  • qt之网络协议
    tcp://--------------widget.h:#ifndefWIDGET_H#defineWIDGET_H#include<QWidget>#include<QTcpServer>//服务器#include<QTcpSocket>//套接字发送的......
  • 【Qt6.2.4】qml 实现登录注册及显示详情demo
    参考https://www.bilibili.com/video/BV1dS4y1u7vN?spm_id_from=333.999.0.0(很棒的教程)环境环境版本windows10QT6.2.4QtCreator8.0.1(Community......
  • qt 线程和数据库
    qt使用数据库:1.使用的编译器是MinGM或MSVxx系统:一般MinGM在include环节没有问题,MSV的话有可能需要自己添加一下2.安装qt的时候没有勾选上qsqldatabase,那么需要自己下......
  • 好好回答下 TCP 和 UDP 的区别
    写了这么多篇关于TCP和UDP的文章,还没有好好聊过这两个协议的区别,这篇文章我们就来开诚布公的谈一谈。关于TCP和UDP,想必大家都看过一张这样的图。有一个小姑娘在......
  • ubuntu22.04+qt6.2安装步骤
    chmod+xqt-unified-linux-x64-4.4.1-online.runsudoaptinstallvimsudoaptinstallnet-toolssudoaptinstallopenssh-serversudoapt-getinstallbuild-essen......
  • 关于qtableview开发过程中的一些记录
    使用QTableWidget刷新数据后,经常会自动展示为table首行。为了显示刷新数据前所在的位置,解决办法如下:     先记住滚动条位置,刷新数据后,再重置滚动条位置。伪代码如......
  • 路由器功能----组播
    1.IGMP监听(IGMPSnooping)能够帮助交换机了解哪些主机请求接受特定多播应用的流量以及这些主机都连接在哪些交换机端口上2.多播路由协议:PIM-SM(ProtocolIndependent......
  • QT使用HTTP下载来实现程序下载自动安装退出,同时读取JSON更新信息。
    最近在用QT开发一套免费的HelpDesk系统,参考了网上的方法,实现了程序自动下载更新和程序自动退出再安装新程序,为了感谢网页的无私分享,自己也特地分享给大家,希望可以帮助到大......
  • Qt QWidget绘制圆角注意事项
    1、产生黑边painter.setPen(Qt::NoPen);//不设置画笔即可 2、背景不透明this->setAttribute(Qt::WA_TranslucentBackground,true); 3、大致代码this->setA......
  • Udp通信
    多发多收clientpackageClientDemo;importjava.net.DatagramPacket;importjava.net.DatagramSocket;importjava.net.InetAddress;importjava.util.Scanner;p......