精英盒子 -> 程序设计 -> Qt的UDP功能调研-QUdpSocket数据收发 [打印本页]

jybox 2012-01-16 16:32

Qt的UDP功能调研-QUdpSocket数据收发

下面是一个简单的教程,实现用Qt的QUdpSocket进行数据收发
ps..我们这里以Qt Creator向导中的基于Widget的窗口程序为基础。在QtCreator中可以如下图创建一个基于Widget的窗口程序
[attachment=233]
[attachment=232]
首先,QUdpSocket属于QtNetwork模块,要确认工程文件(.pro)中有下面的语句
  1. QT += core gui network
然后我们在Qt Designer里面设计一下ui,布局和控件名称大致如下图
[attachment=234]
然后编辑widget.h
首先先包含QUdpSocket的头文件
  1. #include
然后添加1个私有成员和4个槽
  1. class Widget : public QWidget
  2. {
  3. Q_OBJECT
  4. public:
  5. explicit Widget(QWidget *parent = 0);
  6. ~Widget();
  7. QUdpSocket *conn;//连接对象
  8. private slots:
  9. void onData();//收到新数据
  10. void onError();//发生错误
  11. void on_DoBind_clicked();//监听按钮
  12. void on_DoSend_clicked();//发送按钮
  13. private:
  14. Ui::Widget *ui;
  15. };
然后写widget.cpp中的首先代码
我们需要包含两个头文件,分别是字符集类和桌面类:
  1. #include
  2. #include
然后是构造函数,完成了设置默认编码(如果你的源文件不是utf8,请自己改一下)、设置窗口标题、将窗口移动到桌面中间
  1. Widget::Widget(QWidget *parent):QWidget(parent),ui(new Ui::Widget),conn(0)
  2. {
  3. ui->setupUi(this);
  4. QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
  5. QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
  6. QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
  7. this->setWindowTitle(tr("UDP消息发送Demo"));
  8. QDesktopWidget* desktop = QApplication::desktop();
  9. move((desktop->width() - this->width())/2, (desktop->height() - this->height())/2);
  10. }
收到数据的槽,实现了读取数据报,并显示到文本框上
  1. void Widget::onData()
  2. {
  3. while(conn->hasPendingDatagrams())//如果还有数据报
  4. {
  5. QByteArray data;
  6. data.resize(conn->pendingDatagramSize());//将字节数组调整为和下一个数据报相通的大小
  7. QHostAddress sender;//等下存储发送者ip地址
  8. quint16 senderPort;//等下存储发送者端口
  9. conn->readDatagram(data.data(), data.size(),&sender, &senderPort);//读取数据
  10. //显示到文本框
  11. ui->MsgArea->append(tr("收到来自%1:%2的消息 %3").arg(sender.toString()).arg(QString::number(senderPort)).arg(QString(data)));
  12. }
  13. }
遇到错误的槽,直接把错误显示到文本框
  1. void Widget::onError()
  2. {
  3. ui->MsgArea->append(tr("发生错误 %1").arg(conn->errorString()));
  4. }
监听按钮的槽,实现了监听指定端口,如果正在监听,则销毁之前的连接对象,并重新监听新的端口
  1. void Widget::on_DoBind_clicked()
  2. {
  3. if(conn)//如果连接存在,先断开连接
  4. {
  5. delete conn;
  6. conn=0;
  7. }
  8. conn=new QUdpSocket;//生成连接对象
  9. conn->bind(ui->PortInput->value());//监听端口
  10. connect(conn,SIGNAL(readyRead()),this,SLOT(onData()));//收到数据时,这个信号会发射
  11. connect(conn,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(onError()));//遇到错误时,这个信号会发射
  12. ui->MsgArea->append(tr("正在监听 %1").arg(ui->PortInput->value()));//显示提示消息
  13. }
发送按钮的槽,检查连接对象是否存在,不存在窗体,然后发送数据报并提示在文本框上

  1. void Widget::on_DoSend_clicked()
  2. {
  3. if(!conn)//如果不存在连接对象,则先创建一个
  4. conn=new QUdpSocket;
  5. ui->MsgArea->append(tr("向%1:%2发送 %3").arg(ui->IpInput->text()).arg(ui->PeerPort->value()).arg(ui->MsgInput->text()));
  6. QByteArray data;
  7. data.append(ui->MsgInput->text());
  8. conn->writeDatagram(data,QHostAddress(ui->IpInput->text()),ui->PeerPort->value());//法师数据报
  9. }
代码到这里结束了,
源代码托管(这里可以下载到压缩包):https://github.com/jybox/MyWorks/tree/master/UDP-Chat
截图:
[attachment=235]
[attachment=236]
大体上,上下两部分是分开的,即监听部分和发送部分是可以单独工作的,中间的文本框是共用的


jybox 2012-01-16 17:01
高亮还是没折腾好,被phpwind吞了好多,大家可以去代码托管去看

whtsky 2012-01-16 17:12
#include怎么都没了。。

jybox 2012-01-16 18:34
whtsky:#include怎么都没了。。 (2012-01-16 17:12) 

phpwind吞了

whtsky 2012-01-16 18:47

mason 2012-01-16 20:20

墨阐 2012-01-16 21:33
  1. #include\<windows.h\>

输入测试

墨阐 2012-01-16 21:34
  1. #include<windows.h>

输入测试2

墨阐 2012-01-16 21:35
jybox:phpwind吞了 (2012-01-16 18:34) 

  1. #include<windows.h>

肿么我的没吞

jybox 2012-01-16 23:14
墨阐:#include<windows.h>
肿么我的没吞 (2012-01-16 21:35) 

你的木高亮

abreto 2012-01-20 13:08
用coolcode

jybox 2012-01-30 12:23
phpwave:[表情] 有编译好的程序没?
对象党[表情]  (2012-01-29 20:47) 

自己下载源码编译去

scxyscxy 2012-01-30 14:57
马克,回家看

su 2012-02-02 23:37
第一次发帖,请教一下关于网络的
  1. QUdpSocket * reciver;
    reciver=new QUdpSocket();
    reciver->bind(Port);
    QHostAddress address;//记录发信人的IP
    quint16 port;
    QByteArray dataGrams;
    int size=0;
    while(reciver->hasPendingDatagrams())
    {
    size=reciver->pendingDatagramSize();
    dataGrams.resize(size);
    reciver->readDatagram(dataGrams.data(),dataGrams.size(),&address,&port);
    }
以上代码,关键是那个address,他记录的应该是发信息的人的IP吧,在一般情况下我收到信息再向那个地址回发一条信息对方可以收到,可是当对方用了路由器之后我就只能收到对方的信息,却发不回去,求解决。。。


jybox 2012-02-03 02:17
su:第一次发帖,请教一下关于网络的
QUdpSocket * reciver;
reciver=new QUdpSocket();
reciver->bind(Port);
QHostAddress address;//记录发信人的IP
....... (2012-02-02 23:37) 

首先,虽说那个是发信人的地址,但是发信人未必在监听端口阿...


如果对方确实在监听,那么就是网络是否通畅的问题了

路由器相当于一个nat(具体请百度),使内网几台电脑共用一个ip,这个过程中,端口会经过一个翻译的机制,反正就是很复杂啦。(关于这部分,你可以搜索“nat穿越”)

解决办法就是在路由器里设置端口映射(也叫端口转发)、DMZ主机

su 2012-02-03 12:48
0fan:[表情]苍天啊、、、保佑我说对了吧、、、
 (2012-02-03 01:08) 

折腾了一上午,发现端口映射的方法可以完美解决我另外一个问题,让别人可以通过ip地址访问我电脑上写的网页,谢咯

su 2012-02-03 12:53
jybox:首先,虽说那个是发信人的地址,但是发信人未必在监听端口阿...
如果对方确实在监听,那么就是网络是否通畅的问题了
....... (2012-02-03 02:17) 

NAT穿透,哎~~原来经过NAT后的不只是IP地址变了,连端口号都变了,学习了。。。




Powered by phpwind v8.7 Code ©2003-2011 phpwind
Time 0.051032 second(s),query:5 Gzip enabled