封裝了一個類,可以進行在區域網路進行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_OBJECT
public:
void incomingConnection(qintptr socketDescriptor);
signals:
void newConnect(const qintptr socketDescriptor);
};
class JasonQt_LanSocket: public QObject
{
Q_OBJECT
private:
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_TcpListen
void JasonQt_LanSocket_TcpListen::incomingConnection(qintptr socketDescriptor)
{
emit newConnect(socketDescriptor);
}
// JasonQt_LanSocket
JasonQt_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 en0
for(const auto &interface: allInterfaces)
{
if(interface.name().indexOf("en0") != -1)
{
for(const auto &entry: interface.addressEntries())
{
if(entry.ip().toIPv4Address())
{
return entry;
}
}
}
}
// Scan other
for(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);
}
我也寫了一個示例工程,可以到下方連結中下載下傳
http://download.csdn.net/detail/wsj18808050/8369201