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

#ifndef __JasonQt_LanSocket_h__#define __JasonQt_LanSocket_h__// Qt lib import#include <QMap>#include <QTcpSocket>#include <QTcpServer>#include <QUdpSocket>#include <QNetworkAddressEntry>#include <QtConcurrent>class JasonQt_LanSocket_TcpListen: public QTcpServer{Q_OBJECTpublic:void incomingConnection(qintptr socketDescriptor);signals:void newConnect(const qintptr socketDescriptor);};class JasonQt_LanSocket: public QObject{Q_OBJECTprivate:quint16 m_udpPort;quint16 m_tcpPort;quint16 m_pingInterval;quint16 m_pingTimeout;QTimer m_timerPing;QUdpSocket m_udpListen;JasonQt_LanSocket_TcpListen m_tcpListen;QThreadPool m_threadPool;QNetworkAddressEntry m_NetworkAddressEntry;QMap<quint32, qint64> m_availableIp;public:JasonQt_LanSocket(const quint16 &udpPort, const quint16 &tcpPort,const int &pingInterval = 1000, const int &pingTimeout = 10000,const quint8 &threadPoolCount = 20);bool begin(void);static QNetworkAddressEntry getNetworkAddressEntry(void);public slots:void send(const QByteArray &data);void ping(void);private slots:void udpNewConnect(void);void tcpNewConnect(const qintptr &socketDescriptor);signals:void accepted(const QHostAddress address, const QByteArray data);void newConnect(const QHostAddress address);void disConnect(const QHostAddress address);void sendSucceed(const QHostAddress address);};#endif//__JasonQt_LanSocket_h__

Jason_LanSocket.cpp

#include "JasonQt_LanSocket.h"// JasonQt_LanSocket_TcpListenvoid JasonQt_LanSocket_TcpListen::incomingConnection(qintptr socketDescriptor){emit newConnect(socketDescriptor);}// JasonQt_LanSocketJasonQt_LanSocket::JasonQt_LanSocket(const quint16 &udpPort, const quint16 &tcpPort,const int &pingInterval, const int &pingTimeout,const quint8 &threadPoolCount):m_udpPort(udpPort),m_tcpPort(tcpPort),m_pingInterval(pingInterval),m_pingTimeout(pingTimeout){connect(&m_timerPing, SIGNAL(timeout()), this, SLOT(ping()));connect(&m_udpListen, SIGNAL(readyRead()), this, SLOT(udpNewConnect()));connect(&m_tcpListen, SIGNAL(newConnect(qintptr)), this, SLOT(tcpNewConnect(qintptr)));m_threadPool.setMaxThreadCount(threadPoolCount);}bool JasonQt_LanSocket::begin(void){m_NetworkAddressEntry = getNetworkAddressEntry();if(!m_NetworkAddressEntry.ip().toIPv4Address() || !m_udpListen.bind(QHostAddress::Any, m_udpPort) || !m_tcpListen.listen(QHostAddress::Any, m_tcpPort)){m_timerPing.stop();return false;}m_timerPing.start(m_pingInterval);return true;}QNetworkAddressEntry JasonQt_LanSocket::getNetworkAddressEntry(void){auto allInterfaces = QNetworkInterface::allInterfaces();// Scan en0for(const auto &interface: allInterfaces){if(interface.name().indexOf("en0") != -1){for(const auto &entry: interface.addressEntries()){if(entry.ip().toIPv4Address()){return entry;}}}}// Scan otherfor(const auto &interface: allInterfaces){for(const auto &entry: interface.addressEntries()){if(entry.ip().toIPv4Address()){if(entry.ip().toString().indexOf("10.0.") == 0){return entry;}else if(entry.ip().toString().indexOf("192.168.") == 0){return entry;}}}}return QNetworkAddressEntry();}void JasonQt_LanSocket::send(const QByteArray &data){for(auto it = m_availableIp.begin(); it != m_availableIp.end(); it++){QtConcurrent::run(&m_threadPool, [=](const QHostAddress &address, const QByteArray &data){auto socket = new QTcpSocket;socket->connectToHost(address, m_tcpPort);if(!socket->waitForConnected(5000)){ socket->deleteLater(); return; }socket->write(QString::number(data.size()).toLatin1());if(!socket->waitForBytesWritten(5000)) { socket->deleteLater(); return; }if(!socket->waitForReadyRead(5000)){ socket->deleteLater(); return; }if(socket->readAll() != "OK"){ socket->deleteLater(); return; }socket->write(data);if(!socket->waitForBytesWritten(5000)) { socket->deleteLater(); return; }socket->waitForReadyRead(5000);emit sendSucceed(address);QTimer::singleShot(5000, socket, SLOT(deleteLater()));}, QHostAddress(it.key()), data);}}void JasonQt_LanSocket::ping(void){auto &¤tTime = QDateTime::currentDateTime().toMSecsSinceEpoch();for(auto it = m_availableIp.begin(); it != m_availableIp.end(); it++){if((currentTime – it.value()) > m_pingTimeout){emit disConnect(QHostAddress(it.key()));m_availableIp.erase(it);it = m_availableIp.begin();}}QJsonObject data;data.insert("Type", "Ping");data.insert("Ip", QString::number(m_NetworkAddressEntry.ip().toIPv4Address()));auto socket = new QUdpSocket;socket->writeDatagram(QJsonDocument(data).toJson(), m_NetworkAddressEntry.broadcast(), m_udpPort);QTimer::singleShot(1000, socket, SLOT(deleteLater()));}void JasonQt_LanSocket::udpNewConnect(void){while(m_udpListen.hasPendingDatagrams()){QByteArray datagram;datagram.resize(m_udpListen.pendingDatagramSize());m_udpListen.readDatagram(datagram.data(), datagram.size());QJsonObject data = QJsonDocument::fromJson(datagram).object();if(data.contains("Type") && (data.value("Type").toString() == "Ping") && data.contains("Ip")){if(m_availableIp.find(data.value("Ip").toString().toUInt()) == m_availableIp.end()){emit newConnect(QHostAddress(data.value("Ip").toString().toUInt()));}m_availableIp[data.value("Ip").toString().toUInt()] = QDateTime::currentDateTime().toMSecsSinceEpoch();}}}void JasonQt_LanSocket::tcpNewConnect(const qintptr &socketDescriptor){QtConcurrent::run(&m_threadPool, [=](const qintptr &socketDescriptor){auto socket = new QTcpSocket;int psckageSize = -1;QByteArray buf;if(!socket->setSocketDescriptor(socketDescriptor)) { socket->deleteLater(); return; }if(!socket->waitForConnected(5000)) { socket->deleteLater(); return; }if(!socket->waitForReadyRead(5000)) { socket->deleteLater(); return; }psckageSize = socket->readAll().toInt();socket->write("OK");socket->waitForBytesWritten(5000);while(socket->waitForReadyRead(5000)){buf.append(socket->readAll());}if(buf.size() != psckageSize) { socket->deleteLater(); return; }socket->write("OK");socket->waitForBytesWritten(5000);emit accepted(socket->peerAddress(), buf);QTimer::singleShot(1000, socket, SLOT(deleteLater()));}, socketDescriptor);}

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

在乎的是看风景的心情,旅行不会因为美丽的风景终止。

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

相关文章:

你感兴趣的文章:

标签云: