• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

QT Creator学习记录二上位机和下位机网口UDP通信

武飞扬头像
竹湮
帮助1

UI界面图

学新通

1. 工程文件及头文件添加代码

工程文件xxx.pro中添加:

  1.  
    #网口通信
  2.  
    QT = network

头文件xxx.h中添加:

  1.  
    //UDP
  2.  
    #include <QUdpSocket>
  3.  
    #include <QNetworkDatagram>

2. 接收数据

(1) uSocket初始化

  1.  
    MainWindow::MainWindow(QWidget *parent)
  2.  
    : QMainWindow(parent)
  3.  
    , ui(new Ui::MainWindow)
  4.  
    {
  5.  
    ui->setupUi(this);
  6.  
     
  7.  
     
  8.  
    /* UDP -注意逻辑- */
  9.  
    //创建QUdpSocket对象
  10.  
    uSocket = new QUdpSocket(this);
  11.  
    //绑定本地IP和端口号,IPLocal与PortLocal为自定义的全局变量
  12.  
    uSocket->bind(QHostAddress(IPLocal),PortLocal);
  13.  
    //默认关闭
  14.  
    uSocket->close();
  15.  
    //收到数据时,会触发readyRead()信号,自定义readPendingDatagrams()进行读取数据
  16.  
    connect(uSocket,&QUdpSocket::readyRead, this,&MainWindow::UDP_DataReceived);
  17.  
     
  18.  
    }
学新通
  1.  
    /* UDP网口 ↓*/
  2.  
    //默认本地端口号
  3.  
    quint16 PortLocal =8089;
  4.  
    //默认本地IP地址
  5.  
    QString IPLocal ="192.168.10.200";

QT官方文档:bind函数的定义和所需参数, 咱直接传个IP地址和端口号就行

  1.  
    // ### Qt6: make the first one virtual
  2.  
    bool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform);
  3.  
    bool bind(quint16 port = 0, BindMode mode = DefaultForPlatform);

问题记录:关于QUdpSocket数据类型的 bind () 与 close () 与 open ()

之前将IP与端口号绑定后,正常接收数据,关闭网口再打开发现收不到数据了😂

后面发现UDP关闭后,再打开需要重新绑定,即便uSocket对象一直存在。

另外open的时候注意,记得写open方式,QT官方文档:

virtual bool open(OpenMode mode);
  1.  
    public:
  2.  
    enum OpenModeFlag {
  3.  
    NotOpen = 0x0000,
  4.  
    ReadOnly = 0x0001,
  5.  
    WriteOnly = 0x0002,
  6.  
    ReadWrite = ReadOnly | WriteOnly,
  7.  
    Append = 0x0004,
  8.  
    Truncate = 0x0008,
  9.  
    Text = 0x0010,
  10.  
    Unbuffered = 0x0020
  11.  
    };
  12.  
    Q_DECLARE_FLAGS(OpenMode, OpenModeFlag)

而且,记得传参数时加上QIODevice::    (我之前没加,一直尝试,不知道哪出了问题,坑。)

uSocket->open(QIODevice::ReadWrite);

结论:close后open要重新bind

(2)UDP接收数据

  1.  
    //UDP接收
  2.  
    void MainWindow::UDP_DataReceived(){
  3.  
     
  4.  
     
  5.  
    //while循环中读取数据,只要有数据,就一直读取并处理。
  6.  
    while (uSocket->hasPendingDatagrams()) {
  7.  
     
  8.  
    QNetworkDatagram datagram = uSocket->receiveDatagram();
  9.  
    UDP_DataHex.append(datagram.data().toHex());
  10.  
     
  11.  
    //报文头判断,true则继续,false则将数据丢弃
  12.  
    if(UDP_DataHex.at(0)=='f'||UDP_DataHex.at(1)=='f'){
  13.  
    count =1;
  14.  
    //qDebug()<<"UDP test count:"<< count;
  15.  
    if(count==10){
  16.  
     
  17.  
    DataRecevied_Hex=UDP_DataHex;
  18.  
    //数据模拟(连通单片机时应注释掉)
  19.  
    DataRecevied_Hex="ffb27f02d9022600e114";
  20.  
     
  21.  
    //校验位判断,true则输出,false则将数据丢弃
  22.  
    if(Parity_DataReceived()){
  23.  
    ui->IPDestination->setText(datagram.senderAddress().toString());
  24.  
    ui->PortDestination->setText(QString::number(datagram.senderPort()));
  25.  
    ui->UDP_DataReceived->append(Line75Percent TimeStamp() DataFactory(DataRecevied_Hex));
  26.  
     
  27.  
    //AngleCurrent();
  28.  
     
  29.  
    }else{}
  30.  
    //UDP_DataHex数据输出后,清空内容,并将计数器归零
  31.  
    count=0;
  32.  
    UDP_DataHex="";
  33.  
    }else{}
  34.  
    }else{
  35.  
    //UDP_DataHex头两个字符不是ff,则清空内容,并将计数器归零
  36.  
    count=0;
  37.  
    UDP_DataHex="";
  38.  
    }
  39.  
     
  40.  
    }
  41.  
     
  42.  
    }
  43.  
     
学新通

3. 接收数据校验

将接受到的数据进行校验和,如何最后接收到的数据最后两位与计算后所得相同,返回true。

  1.  
    bool MainWindow::Parity_DataReceived(){
  2.  
     
  3.  
    bool judge;
  4.  
     
  5.  
    //校验位计算与转换
  6.  
    int ache=0;
  7.  
    //↓除去校验位本身的长度
  8.  
    for(int i=0;i<DataRecevied_Hex.size()-2; i =2){
  9.  
     
  10.  
    ache = HexToDec(DataRecevied_Hex.at(i)).toInt()*16 HexToDec(DataRecevied_Hex.at(i 1)).toInt();
  11.  
    //qDebug() << "test"<< ache;
  12.  
     
  13.  
    }
  14.  
    QString Parity =QString::number(ache, 16);
  15.  
     
  16.  
    if(Parity.size()==1){
  17.  
    Parity.prepend("000");
  18.  
    }
  19.  
    else if(Parity.size()==2){
  20.  
    Parity.prepend("00");
  21.  
    }
  22.  
    else if(Parity.size()==3){
  23.  
    Parity.prepend("0");
  24.  
    }
  25.  
     
  26.  
    Parity.remove(0,2);
  27.  
    QString str =DataRecevied_Hex.at(18);
  28.  
    str.append(DataRecevied_Hex.at(19));
  29.  
    if(Parity==str){
  30.  
    qDebug() << "校验成功,校验位为:" << Parity;
  31.  
    return judge = true;
  32.  
    }else{
  33.  
    ui->UDP_DataReceived->append("<font color=\"#B22222\">数据校验失败,接收校验位为:" str " 应为:" Parity);
  34.  
    qDebug() << "校验失败,数据校验位为:" << str << "计算校验位为:" << Parity;
  35.  
    return judge = false;
  36.  
    }
  37.  
    }
学新通

4. UDP数据发送

  1.  
     
  2.  
    //最终数据发送[判断是 串口发送 or UDP发送]
  3.  
    void MainWindow::SendMode(){
  4.  
     
  5.  
    QByteArray dataByte =QByteArray::fromHex(DataSend_Hex.toLatin1());
  6.  
     
  7.  
    //发送方式判断
  8.  
    if(serial->isOpen()){
  9.  
    //串口写入
  10.  
    qDebug() << "串口发送数据:"<< dataByte;
  11.  
    serial->write(dataByte);
  12.  
    ui->Serial_DataSend->append(Line75Percent TimeStamp() "<font color=\"#05BDFF\">" DataFactory(DataSend_Hex));
  13.  
    }else if(uSocket->isOpen()){
  14.  
    qDebug() << "UDP发送数据:";
  15.  
     
  16.  
    //读取用户输入的IP地址与端口号
  17.  
    QString IPDestination = ui->IPDestination->text();
  18.  
    quint16 PortDestination = ui->PortDestination->text().toInt();
  19.  
     
  20.  
    qDebug() << "IPDestination:"<<IPDestination;
  21.  
    qDebug() << "PortDestination:"<<PortDestination;
  22.  
     
  23.  
    //发送数据
  24.  
    uSocket->writeDatagram(dataByte,QHostAddress(IPDestination),PortDestination);
  25.  
     
  26.  
    //控件增加
  27.  
    ui->UDP_DataSend->append(Line75Percent TimeStamp() "<font color=\"#05BDFF\">" DataFactory(DataSend_Hex));
  28.  
    }else{
  29.  
    qDebug() << "请打开串口或UDP";
  30.  
    }
  31.  
     
  32.  
    };
学新通

数据发送用的函数QT官方文档:

qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &host, quint16 port);

writeDatagram(数据,QHostAddress(IP地址),端口)

5. 控件QLineEdit输入限制

实际应用时,应该限制用户所输入的内容(如只能输入IP地址):

(1)方法一:正则表达式

首先这种方法点号是不驻留的,其次输入时无法跳过点号完全删除其他数字

例如255.123.255.255,是没办法直接删除123的,必须将后面内容全部删除,才能更改123

ui->IPLocal->setValidator(new QRegExpValidator(QRegExp("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b")));

(2)方法二: setInputMask函数

ui->IPLocal->setInputMask("000.000.000.000; ");

官方文档内容: 

void setInputMask(const QString &inputMask);

其中"000.000.000.000; "的含义就是限制IP地址输入,这种方法点号会驻留就如UI界面中显示的一样,无法删除,但不存在方法一的问题。学新通

6. 用户更改IP地址

首先判断输入是否为空,不为空再判断IP地址格式是否正确

  1.  
    //本地IP地址与端口号更改
  2.  
    void MainWindow::on_UDP_IPChange_clicked(){
  3.  
     
  4.  
    if(ui->IPLocal->text()!=""&&IsIP(ui->IPLocal->text())){
  5.  
    IPLocal = ui->IPLocal->text();
  6.  
    }
  7.  
    if(ui->PortLocal->text()!=""){
  8.  
    PortLocal = ui->PortLocal->text().toInt();
  9.  
    }
  10.  
    uSocket->close();
  11.  
    uSocket->bind(QHostAddress(IPLocal),PortLocal);
  12.  
    uSocket->open(QIODevice::ReadWrite);
  13.  
    qDebug() <<"IP地址更改test:" IPLocal " " PortLocal;
  14.  
    };
  15.  
     
学新通

 QRegExp为QT的正则表达式,我直接趴下来的,具体表达式的含义可以自行查阅。

  1.  
    //是否为IP地址判断
  2.  
    bool MainWindow::IsIP(QString currentIP)
  3.  
    {
  4.  
    QRegExp rxp("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b");
  5.  
    if(!rxp.exactMatch(currentIP))
  6.  
    {
  7.  
    QMessageBox::information(this, tr("错误"), tr("IP地址错误"));
  8.  
    return false;
  9.  
    }
  10.  
    return true;
  11.  
    }

7. 总结

相比串口通信,UDP参数不多,主要就四个,本机IP、本机端口、目标IP、目标端口。

经过串口的各种数据转换洗礼,这里反而轻松不少,只是要注意UDP接收数据容易出现问题,要想办法验证数据头和校验位。(其实保险起见,串口也应该验证一下的,(╯▔皿▔)╯再说吧)

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfcfjj
系列文章
更多 icon
同类精品
更多 icon
继续加载