从零开发B/S机架应用程序(三)-UDP

网络通信

Qt 网络之UDP

UDP是一个轻量级的,不可靠的,面向数据报的无连接协议 。
我们现在几乎每个人都使用的腾讯QQ,其聊天时就是使用UDP协议进行消息发送的 。就像QQ那样,当有很多用户,发送的大部分都是短消息,要求能及时响应,并且对安全性要求不是很高的情况下使用UDP协议。

UDP通信客户端与服务器端的区别

通常来讲,客户端是不需要绑定端口号的,而服务器端是需要绑定监听的端口号。其
从socket通信的角度来看,UDP通信属于帧传输,TCP则是流传输,在帧传输过程中对于消息的次序和到达情况没有需求,所以UDP属于不可靠传输,不需要确认和排序。这样在客户端和服务器端的实现上就没有太大的差别了。

我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。

UDP协议没有连接的过程,所以它的通信效果高;但也正因为如此,它的可靠性不如TCP协议高。

枚举 QHostAddress


QHostAddress::Null         //空地址对象,相当于QHostAddress()。
QHostAddress::LocalHost     //IPv4本地主机地址,相当于QHostAddress(“127.0.0.1”)。
QHostAddress::LocalHostIPv6 //IPv6本地主机地址,相当于 QHostAddress(“::1”)。
QHostAddress::Broadcast  //Pv4广播地址,相当于QHostAddress(“255.255.255.255”)。
QHostAddress::AnyIPv4   //IPv4 any-address,相当于QHostAddress(“0.0.0.0”)。与该地址绑定的socket将只监听IPv4接口。
QHostAddress::AnyIPv6   //IPv6 any-address,相当于QHostAddress(“::”)。与该地址绑定的socket将只监听IPv4接口。
QHostAddress::Any       //双any-address栈,与该地址绑定的socket将侦听IPv4和IPv6接口。

QByteArray

QByteArray类提供了字节数组,包含于QByteArray头文件中
QByteArray可以存储raw bytes和传统的8-bits的字符串,都是以’\0’结尾

QDataStream

Qt提供了非常方便的写文件操作QDataStream,可以使用流式操作来对数据进行读写
QDataStream是数据流,相当于数据管道,屏蔽了数据转换过程。可以连接到一个设备上,这个设备可以是socket, file,或buffer.

大端与小端

为什么会有大小端模式之分呢?这是因为在计算机系统中,我们是以字节为单位的,每个地址单元都对应着一个字节,一个字节为8bit
- 大端模式,是指数据的低位保存在内存的高地址中,而数据的高位,保存在内存的低地址中;
- 小端模式,是指数据的低位保存在内存的低地址中,而数据的高位保存在内存的高地址中。

16bit宽的数0x1234在Little-endian(小端)模式(假设从地址0x4000开始存放)为:

内存地址  0x4000    0x4001
存放内容  0x34      0x12

16bit宽的数0x1234在BIG-endian(大端)模式

内存地址  0x4000    0x4001
存放内容  0x12      0x34

数在据的表达方式,实际上是大端序更符合人们的阅读方式。

文件操作

· QFlie:访问本地文件或者嵌入资源;
· QTemporaryFile:创建和访问本地文件系统的临时文件;
· QBuffer:读写QByteArray;
· QProcess:运行外部程序,处理进程间通讯;
· QTcpSocket:TCP协议网络数据传输;
· QUdpSocket:传输 UDP 报文;
· QSslSocket:使用 SSL/TLS 传输数据;
· QFileDevice:Qt5新增加的类,提供了有关文件操作的通用实现。

QProcess、QTcpSocket、QUdpSoctet和QSslSocket是顺序访问设备。所谓“顺序访问”,是指它们的数据只能访问一遍:从头走到尾,从第一个字节开始访问,直到最后一个字节,中途不能返回去读取上一个字节;QFile、QTemporaryFile和QBuffer是随机访问设备,可以访问任意位置任意次数,还可以使用QIODevice::seek()函数来重新定位文件访问位置指针。

QIODevice

QIODevice类是所有输入输出IO类的基础类,为IO类提供了统一的调用接口,因此我们称QIODevice类以及其派生类为IO类。

为设备提供了一个共同的实现和抽象接口,它支持读取和写入像QFile、QBuffer和QTcpSocket等以块为单位的数据。
QIODevice是抽象类,不能被实例化(instantiated),但是利用它定义的接口来提供独立于设备的I / O功能是普遍的。


枚举值
描述
QIODevice::NotOpen      //未打开
QIODevice::ReadOnly     //以只读方式打开
QIODevice::WriteOnly    //以只写方式打开
QIODevice::ReadWrite    //以读写方式打开
QIODevice::Append       //以追加的方式打开,新增加的内容将被追加到文件末尾
QIODevice::Truncate     //以重写的方式打开,在写入新的数据时会将游标设置在文件开头
QIODevice::Text         //在读取时,将行结束符转换成 \n;在写入时,将行结束符转换成本地格式,例如 Win32 平台上是 \r\n
QIODevice::Unbuffered   //缓存

服务端源码

cpp文件

#include "widget.h"
#include "ui_widget.h"
#include <QDateTime>
#include <QIODevice>
#include <QByteArray>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    connect(&timer,SIGNAL(timeout()),this,SLOT(onSendDatagram()));

}

Widget::~Widget()
{
    delete ui;
}
double Widget::temperature() const
{
    return -20.0 + (2.0 * std::rand() / (RAND_MAX + 1.0));
}

double Widget::humidity() const
{
    return 20.0 + (2.0 * std::rand() / (RAND_MAX + 1.0));
}

double Widget::altitude() const
{
    return 7000 + (100.0 * std::rand() / (RAND_MAX + 1.0));
}

void Widget::onSendDatagram()
{
    //用于存放发送的数据报
    QByteArray datagram;
    //QIODevice::WriteOnly 以只写方式打开
    QDataStream out(&datagram, QIODevice::WriteOnly);
    //设置数据序列化格式的版本号,建议看到版本
    out.setVersion(QDataStream::Qt_5_6);

    out<<QDateTime::currentDateTime() <<temperature() << humidity() <<altitude();

    //UDP广播
    //发送者把数据发送到端口号,需要接受者绑定该端口号
    mUdpSocket.writeDatagram(datagram,QHostAddress::Broadcast ,5824);

    //发送本机
    mUdpSocket.writeDatagram(datagram,QHostAddress::LocalHost ,5824);

    //指定IP
    QHostAddress serverAddress = QHostAddress("192.168.86.12");
    //datagram.size()大小,不包含'\0' ,qstrlen()包含'\0'
    //datagram.data()所存储的数据对象,返回一个指针
    mUdpSocket.writeDatagram(datagram.data(), datagram.size(),serverAddress, 5824);
}


void Widget::on_pushButton_2_clicked()
{
     timer.start(1000);
}

void Widget::on_pushButton_clicked()
{
    this->close();
}

UDP接收端

接收端源码

#include "widgetclient.h"
#include "ui_widgetclient.h"
#include <QGridLayout>
#include <QTimer>
#include <QDateTime>
WidgetClient::WidgetClient(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::WidgetClient)
{
    ui->setupUi(this);
    //此处的bind是个重载函数,连接本机的port端口,采用ShareAddress模式(即允许其它的服务连接到相同的地址和端口,特别是
    //用在多客户端监听同一个服务器端口等时特别有效),和ReuseAddressHint模式(重新连接服务器)
    mUdpSocket.bind(5824, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
    //readyRead()信号是每当有新的数据来临时就被触发
    connect(&mUdpSocket,SIGNAL(readyRead()),SLOT(onProcessPendingDatagrams()));

    mDataLabel = new QLabel(tr("Data:"));
    mTimeLabel = new QLabel(tr("Time:"));
    mTemperatureLabel = new QLabel(tr("Temperature:"));
    mhumidtyLabel = new QLabel(tr("Hunidity:"));
    mAltitudeLabel = new QLabel(tr("Altitude:"));

    mDateLineEdit = new QLineEdit;
    mTimeLineEdit = new QLineEdit;
    mTemperatureLineEdit = new QLineEdit;
    mHunidityLineEdit = new QLineEdit;
    mAltitudeLineEdit = new QLineEdit;

    mDateLineEdit->setReadOnly(true);
    mTimeLineEdit->setReadOnly(true);
    mTemperatureLineEdit->setReadOnly(true);
    mHunidityLineEdit->setReadOnly(true);
    mAltitudeLineEdit->setReadOnly(true);

    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->addWidget(mDataLabel,0,0);
    mainLayout->addWidget(mDateLineEdit,0,1);
    mainLayout->addWidget(mTimeLabel,1,0);
    mainLayout->addWidget(mTimeLineEdit,1,1);
    mainLayout->addWidget(mTemperatureLabel,2,0);
    mainLayout->addWidget(mTemperatureLineEdit,2,1);
    mainLayout->addWidget(mhumidtyLabel,3,0);
    mainLayout->addWidget(mHunidityLineEdit,3,1);
    mainLayout->addWidget(mAltitudeLabel,4,0);
    mainLayout->addWidget(mAltitudeLineEdit,4,1);

    setLayout(mainLayout);
    setWindowTitle(tr("Weather Station"));
}

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

void WidgetClient::onProcessPendingDatagrams()
{
    QByteArray datagram;

    while(mUdpSocket.hasPendingDatagrams()) //是否有数据包等待读取
    {
        //pendingDatagramSize为返回第一个在等待读取报文的size,resize函数是把datagram的size归一化到参数size的大小一样
        datagram.resize(mUdpSocket.pendingDatagramSize()); //
        //将读取到的不大于datagram.size()大小数据输入到datagram.data()中,datagram.data()返回的是一个字节数组中存储
        //数据位置的指针
        mUdpSocket.readDatagram(datagram.data(), datagram.size());
    }

    QDateTime dateTime;
    double temperature;
    double humidity;
    double altitude;

    QDataStream in(&datagram,QIODevice::ReadOnly); //从datagram中读取数据
    in.setVersion(QDataStream::Qt_5_6);
    //因为其属性为只读,所以是输入,值得注意的接收顺序与发送一致
    in>>dateTime >> temperature >>humidity >>altitude;

    mDateLineEdit->setText(dateTime.date().toString());
    mTimeLineEdit->setText(dateTime.time().toString());
    mTemperatureLineEdit->setText(tr("%1 C").arg(temperature));
    mHunidityLineEdit->setText(tr("%1%").arg(humidity));
    mAltitudeLineEdit->setText(tr("%1 m").arg(altitude));
}

下载地址
http://download.csdn.net/download/osean_li/10112271

效果图
这里写图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值