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

封装了一个类,可以进行在局域网进行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

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

Jason_LanSocket.cpp

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

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

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

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

原文地址:https://www.cnblogs.com/findumars/p/5034560.html