Qt Quick实现的文件传输工具(UDP广播扫描篇)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011283226/article/details/89451701

【写在前面】

在前面两篇博客中,都是实现一个扫描的小动画,Ծ‸ Ծ 然鹅却浪费不少时间。

再者,因为最近一直在打游戏,就一直没有怎么做(毕竟只是突然做着玩玩)。

不过今天我决定还是赶紧把它写完(代码已经OK了)。

本次分为两篇来些,上篇为扫描篇。


【正文开始】

效果图太大。。上传不了,不过Github可以,所以想看效果的直接去项目地址看就可以。

首先,想象一下,有两个人在局域网(热点也算)环境下,他们之间有一些文件要发,但是他们互不知道对方地址(IP),因此,第一步我们应该让他们互相知道对方地址。

我将这一步称为扫描,想法是其中一方点击扫描后,发送一个UDP广播,然后另一方进行回复,通过IP头中地址即可知双方地址,下一步通过TCP连接对方,然后就可以在此连接上收发文件。

discoverconnection.h:

#ifndef DISCOVERCONNECTION_H
#define DISCOVERCONNECTION_H

#include <QUdpSocket>

class DiscoverConnection : public QUdpSocket
{
    Q_OBJECT

    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)

public:   
    static DiscoverConnection* instance();
    ~DiscoverConnection();

    QString getName(const QHostAddress &address) const;
    QHostAddress getAddress(const QString &name) const;

    QString name() const;
    void setName(const QString &name);

    Q_INVOKABLE void discover();
    Q_INVOKABLE void connectToName(const QString &name);

signals:
    void nameChanged();
    void newConnection(const QString &name);
    void newAccessPoint(const QString &name);

private:
    DiscoverConnection(QObject *parent = nullptr);
    void processDatagram();

    QString m_name = "未命名";
    QMap<QString, QHostAddress> m_accessPoints;
};

#endif // DISCOVERCONNECTION_H

这里,有一个name属性,主要是用于区分不同用户的名称。

然后是m_accessPoint,扫描到的访问点都会以<name, ip>的形式进行存储。

接下来看discoverconnection.cpp:

#include "discoverconnection.h"

#include <QNetworkDatagram>

DiscoverConnection::DiscoverConnection(QObject *parent)
    : QUdpSocket (parent)
{
    bind(QHostAddress::Any, 43801);
    connect(this, &QUdpSocket::readyRead, this, &DiscoverConnection::processDatagram);
}

DiscoverConnection *DiscoverConnection::instance()
{
    static DiscoverConnection d;
    return &d;
}

DiscoverConnection::~DiscoverConnection()
{

}

QString DiscoverConnection::getName(const QHostAddress &address) const
{
    return m_accessPoints.key(address);
}

QHostAddress DiscoverConnection::getAddress(const QString &name) const
{
    return m_accessPoints[name];
}

QString DiscoverConnection::name() const
{
    return m_name;
}

void DiscoverConnection::setName(const QString &name)
{
    if (name != m_name)
    {
        m_name = name;
        emit nameChanged();
    }
}

void DiscoverConnection::discover()
{
    m_accessPoints.clear();
    writeDatagram("[DISCOVER]", QHostAddress::Broadcast, 43801);
}

void DiscoverConnection::connectToName(const QString &name)
{
    writeDatagram("[CONNECT]##" + m_name.toLocal8Bit(), getAddress(name), 43801);
}

void DiscoverConnection::processDatagram()
{
    while (hasPendingDatagrams())
    {
        QNetworkDatagram datagram = receiveDatagram();
        if (!datagram.senderAddress().isNull() && datagram.senderPort() != -1)
        {
            if (datagram.data() == "[DISCOVER]")
            {
                writeDatagram("[NAME]##" + m_name.toLocal8Bit(), datagram.senderAddress(),
                              quint16(datagram.senderPort()));
            }
            else if (datagram.data().left(8) == "[NAME]##")
            {
                QString name = QString::fromLocal8Bit(datagram.data().mid(8));
                m_accessPoints[name] = datagram.senderAddress();
                qDebug() << name << datagram.senderAddress();
                emit newAccessPoint(name);
            }
            else if (datagram.data().left(11) == "[CONNECT]##")
            {
                QString name = QString::fromLocal8Bit(datagram.data().mid(11));
                emit newConnection(name);
            }
        }
    }
}

对于UDP套接字,使用bind()绑定后,只要UDP数据报到达指定的地址和端口,就会发出信号QUdpSocket :: readyRead()信号,这里bind到QHostAddress::Any,并且我们约定UDP数据的端口为43801,TCP数据的端口为43800。

1、discover()函数为开始扫描的函数,使用Q_INVOKABLE饰,即可在QML中调用,这里就是发送一个UDP广播,内容为[DISCOVER],其实可以弄一个自定义协议,但因为软件比较简单,就直接在代码里体现了。

2、processDatagram()在有UDP数据到达时被调用:

     如果为[DISCOVER],我们就将自己的name以[NAME]## + name的形式发送回去。

     如果为[NAME]##即另一端的回复,就加入访问点,并发送newAccessPoint()信号,这将会在qml更新访问点。

3、connectToName()如果用户选择了一个name进行连接,那么将会发送[CONNECT]## + name数据报。

     如果收到[CONNECT]##即另一端进行连接,就发送newConnection()信号,这将会在qml更新连接名。

至此,UDP扫描结束。


【结语】

其实UDP用起来方便好用还很简单,不过文件传输的话还是选择TCP,这将在下一篇博客进行讲解,当然也远比本篇复杂。

最后,资源地址:https://download.csdn.net/download/u011283226/11135788

也可以访问项目地址:https://github.com/mengps/FileTransfer

猜你喜欢

转载自blog.csdn.net/u011283226/article/details/89451701