找回密码
 立即注册
收起左侧

第36篇 网络(六)UDP

22
回复
34691
查看
[复制链接]
累计签到:1568 天
连续签到:1 天
来源: 2013-9-5 11:38:50 显示全部楼层 |阅读模式
UDP

版权声明

该文章原创于Qter开源社区(www.qter.org),作者yafeilinux,转载请注明出处!



导语

这一节讲述UDP编程的知识。UDP(UserDatagram Protocol即用户数据报协议)是一个轻量级的,不可靠的,面向数据报的无连接协议。对于UDP我们不再进行过多介绍,如果你对UDP不是很了解,而且不知道它有什么用,那么这里就举个简单的例子:我们现在几乎每个人都使用的腾讯QQ,其聊天时就是使用UDP协议进行消息发送的。就像QQ那样,当有很多用户,发送的大部分都是短消息,要求能及时响应,并且对安全性要求不是很高的情况下使用UDP协议。
    在Qt中提供了QUdpSocket 类来进行UDP数据报(datagrams)的发送和接收。这里我们还要了解一个名词Socket,也就是常说的“套接字”。 Socket简单地说,就是一个IP地址加一个port端口。因为我们要传输数据,就要知道往哪个机子上传送,而IP地址确定了一台主机,但是这台机子上可能运行着各种各样的网络程序,我们要往哪个程序中发送呢?这时就要使用一个端口来指定UDP程序。所以说,Socket指明了数据报传输的路径。
下面我们将编写两个程序,一个用来发送数据报,可以叫做客户端;另一个用来接收数据报,可以叫做服务器端,它们均应用UDP协议。这样也就构成了所谓的C/S(客户端/服务器)编程模型。我们会在编写程序的过程中讲解一些相关的网络知识。




环境:Windows Xp + Qt 4.8.5+QtCreator 2.8.0




目录


一、发送端(客户端)
二、接收端(服务器端)





正文



一、发送端(客户端)


1.新建Qt Gui应用。
项目名为udpSender,基类选择QWidget,类名为Widget。完成后在udpSender.pro文件中添加一行代码:QT += network,并保存该文件。


2.widget.ui文件中,往界面上添加一个Push Button,更改其显示文本为“开始广播”,然后进入其单击事件槽函数。


3.我们在widget.h文件中更改。
添加头文件:#include <QtNetwork>
添加private私有对象:QUdpSocket *sender;


4.我们在widget.cpp中进行更改。
在构造函数中添加:sender = new QUdpSocket(this);
更改“开始广播”按钮的单击事件槽函数:
void Widget::on_pushButton_clicked() // 开始广播
{
    QByteArray datagram = "hello world!";
    sender->writeDatagram(datagram.data(),datagram.size(),
                          QHostAddress::Broadcast,45454);
}


这里定义了一个QByteArray类型的数据报datagram,其内容为“hello world!”。然后我们使用QUdpSocket类的writeDatagram()函数来发送数据报,这个函数有四个参数,分别是数据报的内容,数据报的大小,主机地址和端口号。对于数据报的大小,它根据平台的不同而不同,但是这里建议不要超过512字节。这里使用了广播地址QHostAddress::Broadcast,这样就可以同时给网络中所有的主机发送数据报了。对于端口号,它是可以随意指定的,但是一般1024以下的端口号通常属于保留端口号,所以我们最好使用大于1024的端口,最大为65535。我们这里使用了45454这个端口号,一定要注意,在下面要讲的服务器程序中,也要使用相同的端口号。

5.发送端就这么简单,下面可以先运行程序。




二、接收端(服务器端)


1.新建Qt Gui 应用
工程名为udpReceiver,基类选择QWidget,类名为Widget。完成后在udpSender.pro文件中添加一行代码:QT += network,并保存该文件。
此时工程文件列表中应包含两个项目,如下图。





2.我们在udpReceiver项目中的widget.ui文件中,向界面上添加一个Label部件,更改其显示文本为“等待接收数据!”,效果如下。



3.我们在udpReceiver工程中的widget.h文件中更改。
添加头文件:#include <QtNetwork>
添加private私有对象:QUdpSocket *receiver;
添加私有槽函数:
private slots:
void processPendingDatagram();


4.我们在udpReceiver工程中的widget.cpp文件中更改。
在构造函数中:
receiver = new QUdpSocket(this);
receiver->bind(45454,QUdpSocket::ShareAddress);
connect(receiver,SIGNAL(readyRead()),
this,SLOT(processPendingDatagram()));

我们在构造函数中将receiver绑定到45454端口,这个端口就是上面发送端设置的端口,二者必须一样才能保证接收到数据报。这里使用了绑定模式QUdpSocket::ShareAddress,它表明其他服务也可以绑定到这个端口上。因为当receiver发现有数据报到达时就会发出readyRead()信号,所以将其和数据报处理函数相关联。

数据报处理槽函数实现如下:


void Widget::processPendingDatagram() //处理等待的数据报
{
    while(receiver->hasPendingDatagrams())  //拥有等待的数据报
    {
       QByteArray datagram; //拥于存放接收的数据报
//让datagram的大小为等待处理的数据报的大小,这样才能接收到完整的数据
       datagram.resize(receiver->pendingDatagramSize());
       //接收数据报,将其存放到datagram中
       receiver->readDatagram(datagram.data(),datagram.size());
       //将数据报内容显示出来
       ui->label->setText(datagram);
    }
}



5.我们在项目列表中udpReceiver项目上点击鼠标右键,在弹出的菜单上选择run菜单来运行该工程。如下图所示。





6.第一次运行该程序时,系统可能会提示警告,我们选择“解除阻止”。
注意,如果是在linux下,你可能还需要关闭防火墙。


7.我们同时再运行udpSender程序。然后点击其上的“发送广播”按钮,这时会在udpReceiver上显示数据报的内容。效果如下。






结语


可以看到,UDP的应用是很简单的。我们只需要在发送端执行writeDatagram()函数进行数据报的发送,然后在接收端绑定端口,并关联readyRead()信号和数据报处理函数即可。
    下一节我们讲述TCP的应用。





涉及到的源码:



上一篇:   第35篇 网络(五)获取本机网络信息

下一篇:第37篇 网络(七)TCP(一)

返回:系列教程目录



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

点评

yafei老师,外网也可以进行通信吗?  发表于 2016-11-28 13:18
回复

使用道具 举报

尚未签到

2014-3-21 15:06:13 显示全部楼层
yafei 老师,我现在对套接字有点不明白,数据的读和写怎么感觉还有顺序啊?就是"<<"或“>>”后面的内容有先后吗?对那本开发实例精解上的“局域网聊天工具”这一章我修改一下先后顺序信息就输出有问题了,不知道怎么理解。
回复 支持 反对

使用道具 举报

累计签到:1568 天
连续签到:1 天
2014-3-22 10:13:54 显示全部楼层
wangxiangjun88 发表于 2014-3-21 15:06
yafei 老师,我现在对套接字有点不明白,数据的读和写怎么感觉还有顺序啊?就是" ...

是有顺序了。
回复 支持 反对

使用道具 举报

尚未签到

2014-3-23 15:42:36 显示全部楼层
yafeilinux 发表于 2014-3-22 10:13
是有顺序了。

是读和写的顺序必须一致吗?
回复 支持 反对

使用道具 举报

累计签到:1 天
连续签到:1 天
2014-4-27 17:08:17 显示全部楼层
感觉一3少了发送button 的槽定义呢?
回复 支持 反对

使用道具 举报

累计签到:1568 天
连续签到:1 天
2014-4-28 17:00:34 显示全部楼层
wangxiangjun88 发表于 2014-3-23 15:42
是读和写的顺序必须一致吗?

是啊。就像进管道一样,先进去的就是先出来。
回复 支持 反对

使用道具 举报

累计签到:1568 天
连续签到:1 天
2014-4-28 17:01:53 显示全部楼层
yitakabe 发表于 2014-4-27 17:08
感觉一3少了发送button 的槽定义呢?
往界面上添加一个Push Button,更改其显示文本为“开始广播”


这个是在设计模式拖放上去的,会自动定义,不需要手动写代码。
回复 支持 反对

使用道具 举报

累计签到:1 天
连续签到:1 天
2014-4-29 09:29:07 显示全部楼层
yafeilinux 发表于 2014-4-28 17:01
这个是在设计模式拖放上去的,会自动定义,不需要手动写代码。

恩恩 ,后来知道啦 谢谢啦
回复 支持 反对

使用道具 举报

累计签到:14 天
连续签到:1 天
2014-8-19 15:18:05 显示全部楼层
本帖最后由 11yichengming 于 2014-8-19 15:21 编辑

为什么我在局域网内的两台电脑上分别运行发送和接收,实现不了应有的接收数据功能呢?确定两台电脑都连在了同一个路由器上面了(用了飞秋测试可以通信)。我运行《Qt及Qt Quick开发实战精解》第五章的局域网聊天的程序时,也不能实现局域网聊天功能,获取的IP地址不是局域网的Ip地址,这个有影响吗?不知道怎么回事?
回复 支持 反对

使用道具 举报

累计签到:1568 天
连续签到:1 天
2014-8-27 21:09:26 显示全部楼层
11yichengming 发表于 2014-8-19 15:18
为什么我在局域网内的两台电脑上分别运行发送和接收,实现不了应有的接收数据功能呢?确定两台电脑都连在了 ...

手动设置一下IP地址,测试可以通信吗?
回复 支持 反对

使用道具 举报

累计签到:14 天
连续签到:1 天
2014-8-28 14:29:53 显示全部楼层
yafeilinux 发表于 2014-8-27 21:09
手动设置一下IP地址,测试可以通信吗?

不能。   
    if((sender->writeDatagram(datagram,datagram.size(),QHostAddress("192.168.1.255"),45454)) == -1){
        //qDebug()<<"send failed";
    }
这样吗?
回复 支持 反对

使用道具 举报

尚未签到

2015-3-9 11:54:38 显示全部楼层
你好,请教一下,
我的笔记本有多个网卡(有线网卡、无线网卡、虚拟机网卡)
当把网线拔掉,用无线网卡上网,在用udp发送数据的时候只有虚拟机网卡的ip地址往外发数据,无线网卡的ip不发数据,请问怎么做到使用无线网卡的ip发送数据?(前提:不能不虚拟机的网卡禁用)
回复 支持 反对

使用道具 举报

累计签到:1568 天
连续签到:1 天
2015-3-12 14:01:47 显示全部楼层
旷性怡情 发表于 2015-3-9 11:54
你好,请教一下,
我的笔记本有多个网卡(有线网卡、无线网卡、虚拟机网卡)
当把网线拔掉,用无线网卡上网 ...

使用udp以前先检测下自己的网络环境,确认使用哪个IP。
回复 支持 反对

使用道具 举报

累计签到:6 天
连续签到:1 天
2016-7-13 09:11:44 显示全部楼层
yafei 老师,接收端可以设置为自由获取端口吗?就是不指定端口号,可以由系统自动选择。
在一些UDP测试软件里面有这个功能。
回复 支持 反对

使用道具 举报

累计签到:2 天
连续签到:1 天
2016-7-19 13:42:32 显示全部楼层
点击  开始广播   按钮  为什么会产生异常,我用的是qt5.6


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复 支持 反对

使用道具 举报

累计签到:1568 天
连续签到:1 天
2016-12-2 10:56:16 显示全部楼层
vip_life 发表于 2016-7-19 13:42
点击  开始广播   按钮  为什么会产生异常,我用的是qt5.6

该程序是基于Qt 4.8的!
回复 支持 反对

使用道具 举报

累计签到:2 天
连续签到:1 天
2017-3-6 17:12:38 显示全部楼层
一点开始广播 我的服务器端就未响应
回复 支持 反对

使用道具 举报

累计签到:2 天
连续签到:1 天
2017-8-10 16:57:48 显示全部楼层
yafeilinux 发表于 2014-8-27 21:09
手动设置一下IP地址,测试可以通信吗?

我也是遇到这个问题,代码和你的一样, 局域网内的两台机子一直连接不上,但是本机就可以发送接收,啥情况呀
回复 支持 反对

使用道具 举报

累计签到:1568 天
连续签到:1 天
2017-8-14 17:14:39 显示全部楼层
LesterChen 发表于 2017-8-10 16:57
我也是遇到这个问题,代码和你的一样, 局域网内的两台机子一直连接不上,但是本机就可以发送接收,啥情 ...

网络没什么限制吧,比如防火墙什么的
回复 支持 反对

使用道具 举报

累计签到:2 天
连续签到:1 天
2017-8-14 19:35:59 显示全部楼层
yafeilinux 发表于 2017-8-14 17:14
网络没什么限制吧,比如防火墙什么的

都没有,估计是需要你说的那种方式(指定IP)
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了