我正在尝试在QUdpSocket上实现我自己的包装器,因为它是uncomfortable to use。我可以使用它,但无论如何我需要实现一些中间缓冲区来访问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);
}
}
上的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
派生类。
答案 0 :(得分: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();
}
}