0

我正在尝试在QUdpSocket上实现我自己的包装器,因为它使用起来不舒服。我可以使用它,但无论如何我需要实现一些中间缓冲区来访问QDataStream操作。另外的:

我继承QIODevice
标题(有点简化):

class BerUdp : public QIODevice
{
    Q_OBJECT

    void startup();

public:
    void setHostToWrite(const QHostAddress &address, quint16 port);
    void setPortToRead(quint16 port);

    qint64 bytesAvailable() const;

protected: // Reimplement:
    qint64 readData(char *data, qint64 maxSize);
    qint64 writeData(const char *data, qint64 maxSize);

private:
    QUdpSocket* dev_write; // udp socket to write
    QUdpSocket* dev_read;  // udp socket to read

    QBuffer     m_buffer;  // buffer to store received datagrams  
};

.cpp 文件:

void BerUdp::startup()
{    
    m_buffer.open(QIODevice::ReadWrite);    
    open(QIODevice::ReadWrite);
}

void BerUdp::setHostToWrite(const QHostAddress &address, quint16 port)
{
    dev_write->connectToHost(address, port);
    dev_write->waitForConnected();
}

void BerUdp::setPortToRead(quint16 port)
{
    dev_read->bind(port);
    dev_read->open(QIODevice::ReadOnly);

    bool ok = connect(dev_read, &QIODevice::readyRead,
        this, &BerUdp::onReceive);
}       

// Read new received data to my internal buffer
void BerUdp::onReceive()
{
    bool av = dev_read->hasPendingDatagrams();
    if (av)
    {
        int dSize = dev_read->pendingDatagramSize();
        QByteArray dtg(dSize, 0);
        dev_read->readDatagram(dtg.data(), dtg.size());    

        // write new data to the end
        int buf_read_pos = m_buffer.pos();
        m_buffer.seek(m_buffer.size());
        m_buffer.write(dtg);
        m_buffer.seek(buf_read_pos);
    }
}

来自QIODevice::readData()上的 Qt 文档

..当重新实现这个函数时,这个函数在返回之前读取所有需要的数据是很重要的。为了使 QDataStream 能够对类进行操作,这是必需的。QDataStream 假定所有请求的信息都已读取,因此如果出现问题不会重试读取...:

// Main read data function. There are only 4 bytes required, but maxSize == 0x4000 here:
qint64 BerUdp::readData(char *data, qint64 maxSize)
{    
    int n = m_buffer.read(data, maxSize);

    // clear the data which has already read:
    QByteArray& ba = m_buffer.buffer();
    ba.remove(0, n); // m_buffer.size() == 0 after this
    m_buffer.seek(0);

    return n;
}

问题是在第一次读取后我有空缓冲区,因此我的bytesAvailable()方法返回 0:

qint64 BerUdp::bytesAvailable() const
{       
    qint64 my_size = m_buffer.size();
    qint64 builtin_size = QIODevice::bytesAvailable();

    return (my_size + builtin_size); // == 0 after 1st read
}

因此,在使用该类时,我无法找出可用的字节数,例如:

BerUdp udp;
QDataStream stream;
stream.setDevice(&udp);
...

QIODevice* dev = stream.device(); // 19 bytes available here
if (dev->bytesAvailable() > 0) // == true
{
    quint32 val;
    stream >> val;
}

if (dev->bytesAvailable() > 0) // == false
{
    //...
}

如何QUdpSocket正确编写自己的包装器?
使用中间缓冲区的想法很有效,直到我决定将逻辑移动到单独的QIODevice派生类。

4

1 回答 1

0

在使用 Qt 源码调试的过程中,发现我应该将isSequential()属性设置为true. 现在我的课工作正常。

bool BerUdp::isSequential() const
{
    return true;
}

全班:

BerUdp.h

#pragma once

#include <QIODevice>
#include <QBuffer>

class QUdpSocket;
class QHostAddress;

class BerUdp : public QIODevice
{
    Q_OBJECT

public:
    BerUdp(QObject *parent = 0);
    void startup();    

    void setHostToWrite(const QHostAddress &address, quint16 port);
    void setPortToRead(quint16 port);

    bool flush();
    qint64 bytesAvailable() const;
    bool waitForReadyRead(int msecs);
    bool isSequential() const;

protected: // Main necessary reimplement
    qint64 readData(char *data, qint64 maxSize);
    qint64 writeData(const char *data, qint64 maxSize);

private slots:
    void onReceive();

private:
    void read_udp_datagram();
    void write_new_data_to_buffer(QByteArray dtg);

private:
    QUdpSocket* dev_write; // One udp socket to write
    QUdpSocket* dev_read;  // Another udp socket to read

    // intermediate buffer to store received datagrams
    // and to provide access to read- and QDataStream- operations
    QBuffer     m_buffer;  
};

BerUdp.cpp

#include "BerUdp.h"
#include <QUdpSocket>

BerUdp::BerUdp(QObject *parent)
    : QIODevice(parent)
{
    startup();
}

// Initialization
void BerUdp::startup()
{
    dev_write = new QUdpSocket(this);
    dev_read = new QUdpSocket(this);

    m_buffer.open(QIODevice::ReadWrite);
    open(QIODevice::ReadWrite);
}

// Set a virtual connection to "host"
void BerUdp::setHostToWrite(const QHostAddress &address, quint16 port)
{
    dev_write->connectToHost(address, port);
    dev_write->waitForConnected();
}

// Bind a port for receive datagrams
void BerUdp::setPortToRead(quint16 port)
{
    dev_read->bind(port);
    dev_read->open(QIODevice::ReadOnly);

    connect(dev_read, &QIODevice::readyRead,
        this, &QIODevice::readyRead);
    connect(dev_read, &QIODevice::readyRead,
        this, &BerUdp::onReceive);
}

// Flush written data
bool BerUdp::flush()
{
    return dev_write->flush();
}

// Returns the number of bytes that are available for reading.
// Subclasses that reimplement this function must call 
// the base implementation in order to include the size of the buffer of QIODevice.
qint64 BerUdp::bytesAvailable() const
{
    qint64 my_size = m_buffer.size();
    qint64 builtin_size = QIODevice::bytesAvailable();

    return (my_size + builtin_size);
}

bool BerUdp::waitForReadyRead(int msecs)
{
    return dev_read->waitForReadyRead(msecs);
}

// Socket device should give sequential access
bool BerUdp::isSequential() const
{
    return true;
}

// This function is called by QIODevice. 
// It is main function for provide access to read data from QIODevice-derived class.
// (Should be reimplemented when creating a subclass of QIODevice).
qint64 BerUdp::readData(char *data, qint64 maxSize)
{
    int n = m_buffer.read(data, maxSize);

    // clear the data which has already been read
    QByteArray& ba = m_buffer.buffer();
    ba.remove(0, n);
    m_buffer.seek(0);

    return n;
}

// This function is called by QIODevice.
// It is main function for provide access to write data to QIODevice-derived class.
// (Should be reimplemented when creating a subclass of QIODevice).
qint64 BerUdp::writeData(const char *data, qint64 maxSize)
{
    return dev_write->write(data, maxSize);
}

// Read new available datagram
void BerUdp::read_udp_datagram()
{
    int dSize = dev_read->pendingDatagramSize();
    QByteArray dtg(dSize, 0);
    dev_read->readDatagram(dtg.data(), dtg.size());

    write_new_data_to_buffer(dtg);
}

// Write received data to the end of internal intermediate buffer
void BerUdp::write_new_data_to_buffer(QByteArray dtg)
{
    int buf_read_pos = m_buffer.pos();
    m_buffer.seek(m_buffer.size());
    m_buffer.write(dtg);
    m_buffer.seek(buf_read_pos);
}

// Is called on readyRead signal
void BerUdp::onReceive()
{
    bool available = dev_read->hasPendingDatagrams();
    if (available)
    {
        read_udp_datagram();
    }
}
于 2017-09-18T19:20:28.420 回答