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

【写在前面】

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

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

        不过今天我决定还是赶紧把它写完( 代码已经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,这将在下一篇博客进行讲解,当然也远比本篇复杂。

        最后,资源地址:QtQuick制作的文件传输器-C++文档类资源-CSDN下载

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

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
Qt代码实现文件传输一般可以通过使用Qt提供的网络模块来实现。下面是一个简单的Qt代码示例,用于将文件从客户端传输到服务端: 服务端代码: ```cpp // 引入相关头文件 #include <QTcpServer> #include <QTcpSocket> #include <QFile> // 创建服务器类 class Server : public QTcpServer { Q_OBJECT public: explicit Server(QObject *parent = nullptr) : QTcpServer(parent) {} // 重写incomingConnection函数,处理客户端连接 void incomingConnection(qintptr socketDescriptor) override { // 创建新的socket对象 QTcpSocket *socket = new QTcpSocket(this); connect(socket, &QTcpSocket::readyRead, this, &Server::readData); connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater); // 设置socket的描述符 if (!socket->setSocketDescriptor(socketDescriptor)) { socket->deleteLater(); return; } } public slots: // 接收客户端发送的数据并保存为文件 void readData() { QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender()); if (!socket) return; // 读取客户端发送文件数据 QByteArray data = socket->readAll(); // 保存为文件 QFile file("received_file.txt"); if (file.open(QFile::WriteOnly)) { file.write(data); file.close(); } // 发送响应给客户端 socket->write("File received successfully"); socket->flush(); } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); Server server; if (server.listen(QHostAddress::Any, 1234)) { qDebug() << "Server started"; return a.exec(); } else { qDebug() << "Failed to start server"; return 1; } } ``` 客户端代码: ```cpp // 引入相关头文件 #include <QTcpSocket> #include <QFile> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 创建socket对象并连接到服务器 QTcpSocket socket; socket.connectToHost("127.0.0.1", 1234); // 打开要发送文件 QFile file("file_to_send.txt"); if (!file.open(QFile::ReadOnly)) return 1; // 读取文件数据 QByteArray data = file.readAll(); file.close(); // 发送文件数据给服务器 socket.write(data); socket.waitForBytesWritten(); // 等待服务器的响应 if (socket.waitForReadyRead()) { QByteArray response = socket.readAll(); qDebug() << "Server response:" << response; } // 关闭socket连接 socket.close(); return a.exec(); } ``` 以上示例代码演示了如何使用Qt实现简单的文件传输功能。注意,在实际生产环境中,还需要添加错误处理和异常处理的代码,以确保程序在异常情况下能够正常运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦起丶

您的鼓励和支持是我创作最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值