网络通信
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
效果图