Qt:基于TCP和UDP的局域网P2P(局域网)通讯封装

时间:2021-02-01 21:23:46

封装了一个类,可以进行在局域网进行P2P通讯(仅局域网可用)

也就是说,假设局域网中有10台电脑,那么从本机发出的数据,将依次派发到这10台电脑(目前的设计中包括自己这台)

在使用方面,构造的时候给端口和一些参数,然后只需要管send槽和accepted信号就可以了

特性/原理介绍:

1.UDP搜索

2.TCP通讯(短连接)

3.自带心跳包,自动维护可用ip

4.TCP工作线程为单独的线程,稳定

5.完全P2P,无需服务器

注意:

1.一台电脑只能使用单开,多开无法监听端口,就无法使用

2.用到了C++11语法,所以请务必开启11模式,不然会编译报错

3.使用前请在pro文件中加入

QT += network concurrent

CONFIG += c++11

上源码:

Jason_LanSocket.h

  1. #ifndef __JasonQt_LanSocket_h__
  2. #define __JasonQt_LanSocket_h__
  3. // Qt lib import
  4. #include <QMap>
  5. #include <QTcpSocket>
  6. #include <QTcpServer>
  7. #include <QUdpSocket>
  8. #include <QNetworkAddressEntry>
  9. #include <QtConcurrent>
  10. class JasonQt_LanSocket_TcpListen: public QTcpServer
  11. {
  12. Q_OBJECT
  13. public:
  14. void incomingConnection(qintptr socketDescriptor);
  15. signals:
  16. void newConnect(const qintptr socketDescriptor);
  17. };
  18. class JasonQt_LanSocket: public QObject
  19. {
  20. Q_OBJECT
  21. private:
  22. quint16 m_udpPort;
  23. quint16 m_tcpPort;
  24. quint16 m_pingInterval;
  25. quint16 m_pingTimeout;
  26. QTimer m_timerPing;
  27. QUdpSocket m_udpListen;
  28. JasonQt_LanSocket_TcpListen m_tcpListen;
  29. QThreadPool m_threadPool;
  30. QNetworkAddressEntry m_NetworkAddressEntry;
  31. QMap<quint32, qint64> m_availableIp;
  32. public:
  33. JasonQt_LanSocket(const quint16 &udpPort, const quint16 &tcpPort,
  34. const int &pingInterval = 1000, const int &pingTimeout = 10000,
  35. const quint8 &threadPoolCount = 20);
  36. bool begin(void);
  37. static QNetworkAddressEntry getNetworkAddressEntry(void);
  38. public slots:
  39. void send(const QByteArray &data);
  40. void ping(void);
  41. private slots:
  42. void udpNewConnect(void);
  43. void tcpNewConnect(const qintptr &socketDescriptor);
  44. signals:
  45. void accepted(const QHostAddress address, const QByteArray data);
  46. void newConnect(const QHostAddress address);
  47. void disConnect(const QHostAddress address);
  48. void sendSucceed(const QHostAddress address);
  49. };
  50. #endif//__JasonQt_LanSocket_h__

Jason_LanSocket.cpp

  1. #include "JasonQt_LanSocket.h"
  2. // JasonQt_LanSocket_TcpListen
  3. void JasonQt_LanSocket_TcpListen::incomingConnection(qintptr socketDescriptor)
  4. {
  5. emit newConnect(socketDescriptor);
  6. }
  7. // JasonQt_LanSocket
  8. JasonQt_LanSocket::JasonQt_LanSocket(const quint16 &udpPort, const quint16 &tcpPort,
  9. const int &pingInterval, const int &pingTimeout,
  10. const quint8 &threadPoolCount):
  11. m_udpPort(udpPort),
  12. m_tcpPort(tcpPort),
  13. m_pingInterval(pingInterval),
  14. m_pingTimeout(pingTimeout)
  15. {
  16. connect(&m_timerPing, SIGNAL(timeout()), this, SLOT(ping()));
  17. connect(&m_udpListen, SIGNAL(readyRead()), this, SLOT(udpNewConnect()));
  18. connect(&m_tcpListen, SIGNAL(newConnect(qintptr)), this, SLOT(tcpNewConnect(qintptr)));
  19. m_threadPool.setMaxThreadCount(threadPoolCount);
  20. }
  21. bool JasonQt_LanSocket::begin(void)
  22. {
  23. m_NetworkAddressEntry = getNetworkAddressEntry();
  24. if(!m_NetworkAddressEntry.ip().toIPv4Address() || !m_udpListen.bind(QHostAddress::Any, m_udpPort) || !m_tcpListen.listen(QHostAddress::Any, m_tcpPort))
  25. {
  26. m_timerPing.stop();
  27. return false;
  28. }
  29. m_timerPing.start(m_pingInterval);
  30. return true;
  31. }
  32. QNetworkAddressEntry JasonQt_LanSocket::getNetworkAddressEntry(void)
  33. {
  34. auto allInterfaces = QNetworkInterface::allInterfaces();
  35. // Scan en0
  36. for(const auto &interface: allInterfaces)
  37. {
  38. if(interface.name().indexOf("en0") != -1)
  39. {
  40. for(const auto &entry: interface.addressEntries())
  41. {
  42. if(entry.ip().toIPv4Address())
  43. {
  44. return entry;
  45. }
  46. }
  47. }
  48. }
  49. // Scan other
  50. for(const auto &interface: allInterfaces)
  51. {
  52. for(const auto &entry: interface.addressEntries())
  53. {
  54. if(entry.ip().toIPv4Address())
  55. {
  56. if(entry.ip().toString().indexOf("10.0.") == 0)
  57. {
  58. return entry;
  59. }
  60. else if(entry.ip().toString().indexOf("192.168.") == 0)
  61. {
  62. return entry;
  63. }
  64. }
  65. }
  66. }
  67. return QNetworkAddressEntry();
  68. }
  69. void JasonQt_LanSocket::send(const QByteArray &data)
  70. {
  71. for(auto it = m_availableIp.begin(); it != m_availableIp.end(); it++)
  72. {
  73. QtConcurrent::run(&m_threadPool, [=](const QHostAddress &address, const QByteArray &data)
  74. {
  75. auto socket = new QTcpSocket;
  76. socket->connectToHost(address, m_tcpPort);
  77. if(!socket->waitForConnected(5000))     { socket->deleteLater(); return; }
  78. socket->write(QString::number(data.size()).toLatin1());
  79. if(!socket->waitForBytesWritten(5000))  { socket->deleteLater(); return; }
  80. if(!socket->waitForReadyRead(5000))     { socket->deleteLater(); return; }
  81. if(socket->readAll() != "OK")           { socket->deleteLater(); return; }
  82. socket->write(data);
  83. if(!socket->waitForBytesWritten(5000))  { socket->deleteLater(); return; }
  84. socket->waitForReadyRead(5000);
  85. emit sendSucceed(address);
  86. QTimer::singleShot(5000, socket, SLOT(deleteLater()));
  87. }, QHostAddress(it.key()), data);
  88. }
  89. }
  90. void JasonQt_LanSocket::ping(void)
  91. {
  92. auto &¤tTime = QDateTime::currentDateTime().toMSecsSinceEpoch();
  93. for(auto it = m_availableIp.begin(); it != m_availableIp.end(); it++)
  94. {
  95. if((currentTime - it.value()) > m_pingTimeout)
  96. {
  97. emit disConnect(QHostAddress(it.key()));
  98. m_availableIp.erase(it);
  99. it = m_availableIp.begin();
  100. }
  101. }
  102. QJsonObject data;
  103. data.insert("Type", "Ping");
  104. data.insert("Ip", QString::number(m_NetworkAddressEntry.ip().toIPv4Address()));
  105. auto socket = new QUdpSocket;
  106. socket->writeDatagram(QJsonDocument(data).toJson(), m_NetworkAddressEntry.broadcast(), m_udpPort);
  107. QTimer::singleShot(1000, socket, SLOT(deleteLater()));
  108. }
  109. void JasonQt_LanSocket::udpNewConnect(void)
  110. {
  111. while(m_udpListen.hasPendingDatagrams())
  112. {
  113. QByteArray datagram;
  114. datagram.resize(m_udpListen.pendingDatagramSize());
  115. m_udpListen.readDatagram(datagram.data(), datagram.size());
  116. QJsonObject data = QJsonDocument::fromJson(datagram).object();
  117. if(data.contains("Type") && (data.value("Type").toString() == "Ping") && data.contains("Ip"))
  118. {
  119. if(m_availableIp.find(data.value("Ip").toString().toUInt()) == m_availableIp.end())
  120. {
  121. emit newConnect(QHostAddress(data.value("Ip").toString().toUInt()));
  122. }
  123. m_availableIp[data.value("Ip").toString().toUInt()] = QDateTime::currentDateTime().toMSecsSinceEpoch();
  124. }
  125. }
  126. }
  127. void JasonQt_LanSocket::tcpNewConnect(const qintptr &socketDescriptor)
  128. {
  129. QtConcurrent::run(&m_threadPool, [=](const qintptr &socketDescriptor)
  130. {
  131. auto socket = new QTcpSocket;
  132. int psckageSize = -1;
  133. QByteArray buf;
  134. if(!socket->setSocketDescriptor(socketDescriptor)) { socket->deleteLater(); return; }
  135. if(!socket->waitForConnected(5000)) { socket->deleteLater(); return; }
  136. if(!socket->waitForReadyRead(5000)) { socket->deleteLater(); return; }
  137. psckageSize = socket->readAll().toInt();
  138. socket->write("OK");
  139. socket->waitForBytesWritten(5000);
  140. while(socket->waitForReadyRead(5000))
  141. {
  142. buf.append(socket->readAll());
  143. }
  144. if(buf.size() != psckageSize) { socket->deleteLater(); return; }
  145. socket->write("OK");
  146. socket->waitForBytesWritten(5000);
  147. emit accepted(socket->peerAddress(), buf);
  148. QTimer::singleShot(1000, socket, SLOT(deleteLater()));
  149. }, socketDescriptor);
  150. }

我也写了一个示例工程,可以到下方链接中下载

http://download.csdn.net/detail/wsj18808050/8369201

http://blog.csdn.net/wsj18808050/article/details/42778751