您的位置:首页 > 理论基础 > 计算机网络

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

2015-01-16 16:25 633 查看
封装了一个类,可以进行在局域网进行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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: