Qt实现UDP通信的示例代码

 更新时间:2022年11月30日 08:36:57   作者:音视频开发老舅  
UDP是一个轻量级、不可靠、面向数据报的、无连接的传输层协议,多用于可靠性要求不严格,不是非常重要的传输,如直播、视频会议等等。本文将通过Qt实现UDP通信,感兴趣的可以了解一下

设想有如下场景:若干的客户端与服务器端建立连接,建立连接后,服务器端随机发送字符串给客户端,客户端打印输出。该节案例使用TCP编程。

服务器端-单线程

头文件

#pragma once
//
//tcp服务端-单线程处理客户端连接
#include <QAbstractSocket>
#include <QObject>
 
class QTcpServer;
class SimpleTcpSocketServerDemo : public QObject
{ 
   
    Q_OBJECT
 
public:
    SimpleTcpSocketServerDemo();
 
private slots:
    void sendData();
    void displayError(QAbstractSocket::SocketError socketError);
 
private:
    QStringList m_oData;
    QTcpServer *m_pTcpServer;
};
 
void testSimpleTcpSocketServerDemo();

源文件

#include "SimpleTcpSocketServerDemo.h"
#include <assert.h>
#include <QTcpServer>
#include <QTcpSocket>
#include <QDebug>
#include <QDataStream>
SimpleTcpSocketServerDemo::SimpleTcpSocketServerDemo()
{ 
 
//初始换原始数据
m_oData << tr("You've been leading a dog's life. Stay off the furniture.")
<< tr("You've got to think about tomorrow.")
<< tr("You will be surprised by a loud noise.")
<< tr("You will feel hungry again in another hour.")
<< tr("You might have mail.")
<< tr("You cannot kill time without injuring eternity.")
<< tr("Computers are not intelligent. They only think they are.");
//1. 创建TCP对象
m_pTcpServer = new QTcpServer(this);
//2. 新连接、错误信号
connect(m_pTcpServer, &QTcpServer::newConnection, this, &SimpleTcpSocketServerDemo::sendData);
connect(m_pTcpServer, &QTcpServer::acceptError, this, &SimpleTcpSocketServerDemo::displayError);
//3. 启动服务端
if (!m_pTcpServer->listen(QHostAddress::Any, 8888))
{ 
 
qDebug() << "m_pTcpServer->listen() error";
assert(false);
}
}
void SimpleTcpSocketServerDemo::sendData()
{ 
 
//获取服务端数据
QString sWriteData = m_oData.at(qrand() % m_oData.size());
//获取与客户端通信的socket
QTcpSocket* pClientConnection = m_pTcpServer->nextPendingConnection();
//从客户端读数据
QString sReadData = pClientConnection->readAll();
qDebug() << "SimpleTcpSocketServerDemo::readDataFromClient " << pClientConnection;
//与客户端写数据
qDebug() << "SimpleTcpSocketServerDemo::writeDataToClient " << sWriteData;
pClientConnection->write(sWriteData.toUtf8());
// //与客户端断开连接
// connect(pClientConnection, &QTcpSocket::disconnected, this, &SimpleTcpSocketServerDemo::deleteLater);
// pClientConnection->disconnectFromHost();
}
void SimpleTcpSocketServerDemo::displayError(QAbstractSocket::SocketError socketError)
{ 
 
qDebug() << "SimpleTcpSocketServerDemo::displayError " << socketError;
}
void testSimpleTcpSocketServerDemo()
{ 
 
//这样写会内存泄漏,如此写方便测试。
SimpleTcpSocketServerDemo* pSimpleTcpSocketServer = new SimpleTcpSocketServerDemo;
}

客户端

头文件

#pragma once
//
//客户端
#include <QObject>
#include <QAbstractSocket>
#include <QRunnable>
#include <QThreadPool>
class QTcpSocket;
class SimpleTcpSocketClientDemo : public QObject
{ 
 
Q_OBJECT
public:
SimpleTcpSocketClientDemo();
private slots:
void connected();
void readyRead();
void error(QAbstractSocket::SocketError socketError);
private:
QTcpSocket* m_pTcpSocket;
};
class ClientRunnable : public QRunnable
{ 
 
public:
void run();
};
void testSimpleTcpSocketClientDemo();

源文件

#include "SimpleTcpSocketClientDemo.h"
#include <QTcpSocket>
#include <QDebug>
SimpleTcpSocketClientDemo::SimpleTcpSocketClientDemo()
{ 
 
//1. 创建TCP套接字对象
m_pTcpSocket = new QTcpSocket(this);
//2. 已连接、数据可读、失败信号连接
connect(m_pTcpSocket, &QTcpSocket::connected, this, &SimpleTcpSocketClientDemo::connected);
connect(m_pTcpSocket, &QIODevice::readyRead, this, &SimpleTcpSocketClientDemo::readyRead);
typedef void (QAbstractSocket::*QAbstractSocketErrorSignal)(QAbstractSocket::SocketError);
connect(m_pTcpSocket, static_cast<QAbstractSocketErrorSignal>(&QTcpSocket::error), this, &SimpleTcpSocketClientDemo::error);
//3. 与服务器端建立连接
m_pTcpSocket->connectToHost("127.0.0.1", 8888);
//4. 同步处理-等待数据可读
m_pTcpSocket->waitForReadyRead();
}
void SimpleTcpSocketClientDemo::readyRead()
{ 
 
qDebug() << "SimpleTcpSocketClientDemo::readyRead " << m_pTcpSocket->readAll();
}
void SimpleTcpSocketClientDemo::connected()
{ 
 
qDebug() << "SimpleTcpSocketClientDemo::connected successfully";
}
void SimpleTcpSocketClientDemo::error(QAbstractSocket::SocketError socketError)
{ 
 
qDebug() << "SimpleTcpSocketClientDemo::error " << socketError;
}
void ClientRunnable::run()
{ 
 
//这样写会内存泄漏,如此写方便测试。
SimpleTcpSocketClientDemo* pSimpleTcpSocketClient = new SimpleTcpSocketClientDemo;
}
#define CLINET_COUNT 2000 //客户端的数量
void testSimpleTcpSocketClientDemo()
{ 
 
QTime oTime;
oTime.start();
//同步线程池的方式模拟多个客户端与服务器端交互
for (int nIndex = 0; nIndex < CLINET_COUNT; ++nIndex)
{ 
 
ClientRunnable* pRunnable = new ClientRunnable;
pRunnable->setAutoDelete(false);
QThreadPool::globalInstance()->start(pRunnable);
}
QThreadPool::globalInstance()->waitForDone(30 * 1000);
qDebug() << "connect count: " << CLINET_COUNT << "total time: " << (double)oTime.elapsed() / double(1000) << "s";
}

测试结果-单线程

服务器端

SimpleTcpSocketServerDemo::readDataFromClient  QTcpSocket(0x2f27f308)
SimpleTcpSocketServerDemo::writeDataToClient  "You will feel hungry again in another hour."
SimpleTcpSocketServerDemo::readDataFromClient  QTcpSocket(0x2eb61cf0)
SimpleTcpSocketServerDemo::writeDataToClient  "You might have mail."
.........

客户端

SimpleTcpSocketClientDemo::connected  successfully
SimpleTcpSocketClientDemo::readyRead  "You might have mail."
SimpleTcpSocketClientDemo::connected  successfully
SimpleTcpSocketClientDemo::readyRead  "You will feel hungry again in another hour."
.........
connect count:  2000 total time:  3.926 s

通过测试输出,可以看到服务器端与客户端建立了正确的连接并且数据交换。

– 实际测试数据:2000个连接,耗时4s左右,CPU使用率10%左右。

通过阅读服务器端,发现单线程处理客户端的连接效率较低。服务器端可修改为多线程处理客户端连接,代码如下:

服务器端-多线程

头文件

#pragma once
//
//服务器端-多线程处理客户端连接
#include <QTcpServer>
#include <QThread>
class MultiThreadTcpSocketServerDemo : public QTcpServer
{ 
 
public:
MultiThreadTcpSocketServerDemo();
//This virtual function is called by QTcpServer when a new connection is available. 
//The socketDescriptor argument is the native socket descriptor for the accepted connection.
virtual void incomingConnection(qintptr handle);
private:
QStringList m_oData;
};
//处理线程
class ServerHandleThread : public QThread
{ 
 
Q_OBJECT
public:
ServerHandleThread(qintptr handle, const QString& sWriteData);
virtual void run();
private:
qintptr m_nHandle;
QString m_sWriteData;
};
void testMultiThreadTcpSocketServerDemo();

//This virtual function is called by QTcpServer when a new connection is available. //The socketDescriptor argument is the native socket descriptor for the accepted connection. virtual void incomingConnection(qintptr handle); //该虚函数是重点

源文件

#include "MultiThreadTcpSocketServerDemo.h"
#include <QDebug>
#include <QTcpSocket>
MultiThreadTcpSocketServerDemo::MultiThreadTcpSocketServerDemo()
{ 
 
//初始换原始数据
m_oData << tr("You've been leading a dog's life. Stay off the furniture.")
<< tr("You've got to think about tomorrow.")
<< tr("You will be surprised by a loud noise.")
<< tr("You will feel hungry again in another hour.")
<< tr("You might have mail.")
<< tr("You cannot kill time without injuring eternity.")
<< tr("Computers are not intelligent. They only think they are.");
}
void MultiThreadTcpSocketServerDemo::incomingConnection(qintptr handle)
{ 
 
//获取服务端数据
QString sWriteData = m_oData.at(qrand() % m_oData.size());
qDebug() << "MultiThreadTcpSocketServerDemo::incomingConnection" << handle;
ServerHandleThread* pThread = new ServerHandleThread(handle, sWriteData);
connect(pThread, &ServerHandleThread::finished, pThread, &ServerHandleThread::deleteLater);
pThread->start();
}
ServerHandleThread::ServerHandleThread(qintptr handle, const QString& sWriteData)
:m_sWriteData(sWriteData), m_nHandle(handle)
{ 
 
}
void ServerHandleThread::run()
{ 
 
//1. 建立与客户端通信的TCP套接字
QTcpSocket oTcpSocket;
if (!oTcpSocket.setSocketDescriptor(m_nHandle))
{ 
 
qDebug() << "oTcpSocket.setSocketDescriptor error";
return;
}
//2. 向客户端写数据
qDebug() << "MultiThreadTcpSocketServerDemo::readDataFromClient" << &oTcpSocket;
qDebug() << "MultiThreadTcpSocketServerDemo::writeDataToClient" << m_sWriteData;
oTcpSocket.write(m_sWriteData.toUtf8());
oTcpSocket.disconnectFromHost();
oTcpSocket.waitForDisconnected();
}
void testMultiThreadTcpSocketServerDemo()
{ 
 
//1. 建立服务器端套接字
MultiThreadTcpSocketServerDemo* m_pTcpServer = new MultiThreadTcpSocketServerDemo();
//2. 启动服务端
if (!m_pTcpServer->listen(QHostAddress::Any, 8888))
{ 
 
qDebug() << "m_pTcpServer->listen() error";
}
}

测试结果-多线程

客户端

SimpleTcpSocketClientDemo::connected  successfully
SimpleTcpSocketClientDemo::readyRead  "You might have mail."
SimpleTcpSocketClientDemo::connected  successfully
SimpleTcpSocketClientDemo::readyRead  "You will feel hungry again in another hour."
.........
connect count:  2000 total time:  6.403 s

– 实际测试数据:2000个连接,耗时6.5s左右,CPU使用率20%左右。

可见服务器端采用多线程可充分利用CPU,但是频繁的切换线程也会性能下降(耗时)。

通过本案例的代码实现可以了解TCP服务器端/客户端编程的基本思路。并且验证了服务器端单线程和多线程的效率对比。 在windows中,可通过IOCP提高服务期端的效率,后面会详细讲解。

到此这篇关于Qt实现UDP通信的示例代码的文章就介绍到这了,更多相关Qt UDP通信内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++中cin的用法详细

    C++中cin的用法详细

    这篇文章主要介绍了C++中cin的用法详细,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-12-12
  • C++深入探究哈希表如何封装出unordered_set和unordered_map

    C++深入探究哈希表如何封装出unordered_set和unordered_map

    哈希表是一种根据关键码去寻找值的数据映射结构,该结构通过把关键码映射的位置去寻找存放值的地方,说起来可能感觉有点复杂,我想我举个例子你就会明白了,最典型的的例子就是字典
    2022-06-06
  • 使用C++一步步实现俄罗斯方块

    使用C++一步步实现俄罗斯方块

    本文给大家分享的是作者在使用C++制作俄罗斯方块的时候的思路分析以及开发准备和实验原理,都是些基础的知识储备,希望大家能够喜欢,具体的代码我们下一节再分享给大家
    2017-12-12
  • C++中拷贝构造函数的总结详解

    C++中拷贝构造函数的总结详解

    深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝
    2013-09-09
  • C语言中宏定义的妙用方法

    C语言中宏定义的妙用方法

    今天小编就为大家分享一篇关于C语言中宏定义的妙用方法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-12-12
  • C++string底层框架模拟实现代码

    C++string底层框架模拟实现代码

    本节文章主要说明浅拷贝和深拷贝的优缺点,以及仿写string类的逻辑并分析实现过程,对C++string底层框架模拟实现代码感兴趣的朋友一起看看吧
    2021-11-11
  • C/C++中static,const,inline三种关键字详细总结

    C/C++中static,const,inline三种关键字详细总结

    以下是对C/C++中static,const,inline的三种关键字进行了详细的分析介绍,需要的朋友可以过来参考下
    2013-09-09
  • 详解C++ 参数的三种传递方式和应用场景

    详解C++ 参数的三种传递方式和应用场景

    这篇文章主要介绍C++ 参数的三种传递方式和应用场景,C++ 参数的三种传递方式分别是值传递、指针传递和引用传递,感兴趣的同学可以参考阅读下
    2023-06-06
  • 浅析STL中的常用算法

    浅析STL中的常用算法

    以下是对STL中的常用算法进行了详细的介绍,需要的朋友可以过来参考下,希望对大家有所帮助
    2013-09-09
  • c语言获取用户输入字符串是scanf和gets的区别详解

    c语言获取用户输入字符串是scanf和gets的区别详解

    今天小编就为大家分享一篇c语言获取用户输入字符串是scanf和gets的区别详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07

最新评论