2017-09-18 8 views
0

私はの上に独自のラッパーを実装しようとしています。その理由はuncomfortable to useです。私はそれを使用することができますが、とにかく私は、QDataStream操作へのアクセスのためにいくつかの中間バッファを実装する必要があります。追加的に:サブクラスQIODevice:QUdpSocketのラッパー

Iはsublass 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ファイル:のQt documuntationから

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()オン

..この関数を再実装するときは、この関数 が返す前にすべての必要なデータを読み込むことが重要です。 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; 
} 

問題は、最初の読み取りの後、私は空のバッファを持っているということですので、私:問題が...あった場合QDataStream は、すべての要求された情報が読み取られたと仮定し、したがって、 リトライが読んでいません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由来のクラスに移すことにするまで、うまくいきました。

答えて

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(); 
    } 
}