我知道有些类似的问题可能已经问过了,但我发现的答案涵盖了非常具体的问题,但我仍然没有弄清楚。
在我的程序中,我创建了一个QObject (称为QPeer),它使用QTcpSocket通过网络与另一个这样的对象进行通信。QPeer有一个接受带有数据的QByteArray (sendData(QByteArray))的插槽。该数组的整个内容被认为是一个“消息”,并被写入套接字。我想做以下工作:每次写入消息时,我都希望接收QPeer只发出一次信号dataReceived(QByteArray),即包含整个消息的QByteArray。(注意:所有信号/插槽,包括连接QPeer与其套接字的私有信号/时隙和公共信号/插槽(如sendData(QByteArray) ),都在必要时使用Qt::QueuedConnection进行序列化。)
我使用信号QTcpSocket::readyRead()从套接字进行异步读取。现在我知道我不能只在QTcpSocket::write()中调用sendData一次,然后假设每写一次,另一边的QTcpSocket就会产生一个readyRead信号。那我该怎么办?
这是我的主意,请告诉我这是否可行:
写作:
void QPeer::sendData(QByteArray data)
{
// TODO: write data.size() as raw int of exactly 4 bytes to socket
const char *bytes = data.constData();
int bytesWritten = 0;
while (bytesWritten < data.size())
bytesWritten += _socket->write(bytes + bytesWritten);
}阅读:
现在,我希望read函数(连接到QTcpSocket::readyRead())使用标头(指定消息长度的4字节int ),然后读取该字节数;接下来,使用这些字节发出dataReceived。我很难做到这一点。例如:如果发出readyRead并且我可以读取消息的头,但不能读取指定的字节数,该怎么办?或者,如果只有部分接收到标头怎么办?
1.如何正确地将头(4字节int)写入套接字?
2.如何正确地实现read函数,以便它能实现我想要的?
任何小费都欢迎。谢谢!
发布于 2013-12-13 04:10:35
我做了一个项目,做的和你期望的一样,在这里看到我为我们的问题开发的解决方案,简化为更容易理解:
编辑,增加了对服务器的支持,可以处理多个客户端.
客户:
#include <QtCore>
#include <QtNetwork>
class Client : public QObject
{
Q_OBJECT
public:
explicit Client(QObject *parent = 0);
public slots:
bool connectToHost(QString host);
bool writeData(QByteArray data);
private:
QTcpSocket *socket;
};Client.cpp:
#include "client.h"
static inline QByteArray IntToArray(qint32 source);
Client::Client(QObject *parent) : QObject(parent)
{
socket = new QTcpSocket(this);
}
bool Client::connectToHost(QString host)
{
socket->connectToHost(host, 1024);
return socket->waitForConnected();
}
bool Client::writeData(QByteArray data)
{
if(socket->state() == QAbstractSocket::ConnectedState)
{
socket->write(IntToArray(data.size())); //write size of data
socket->write(data); //write the data itself
return socket->waitForBytesWritten();
}
else
return false;
}
QByteArray IntToArray(qint32 source) //Use qint32 to ensure that the number have 4 bytes
{
//Avoid use of cast, this is the Qt way to serialize objects
QByteArray temp;
QDataStream data(&temp, QIODevice::ReadWrite);
data << source;
return temp;
}Server.h:
#include <QtCore>
#include <QtNetwork>
class Server : public QObject
{
Q_OBJECT
public:
explicit Server(QObject *parent = 0);
signals:
void dataReceived(QByteArray);
private slots:
void newConnection();
void disconnected();
void readyRead();
private:
QTcpServer *server;
QHash<QTcpSocket*, QByteArray*> buffers; //We need a buffer to store data until block has completely received
QHash<QTcpSocket*, qint32*> sizes; //We need to store the size to verify if a block has received completely
};Server.cpp:
#include "server.h"
static inline qint32 ArrayToInt(QByteArray source);
Server::Server(QObject *parent) : QObject(parent)
{
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()), SLOT(newConnection()));
qDebug() << "Listening:" << server->listen(QHostAddress::Any, 1024);
}
void Server::newConnection()
{
while (server->hasPendingConnections())
{
QTcpSocket *socket = server->nextPendingConnection();
connect(socket, SIGNAL(readyRead()), SLOT(readyRead()));
connect(socket, SIGNAL(disconnected()), SLOT(disconnected()));
QByteArray *buffer = new QByteArray();
qint32 *s = new qint32(0);
buffers.insert(socket, buffer);
sizes.insert(socket, s);
}
}
void Server::disconnected()
{
QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
QByteArray *buffer = buffers.value(socket);
qint32 *s = sizes.value(socket);
socket->deleteLater();
delete buffer;
delete s;
}
void Server::readyRead()
{
QTcpSocket *socket = static_cast<QTcpSocket*>(sender());
QByteArray *buffer = buffers.value(socket);
qint32 *s = sizes.value(socket);
qint32 size = *s;
while (socket->bytesAvailable() > 0)
{
buffer->append(socket->readAll());
while ((size == 0 && buffer->size() >= 4) || (size > 0 && buffer->size() >= size)) //While can process data, process it
{
if (size == 0 && buffer->size() >= 4) //if size of data has received completely, then store it on our global variable
{
size = ArrayToInt(buffer->mid(0, 4));
*s = size;
buffer->remove(0, 4);
}
if (size > 0 && buffer->size() >= size) // If data has received completely, then emit our SIGNAL with the data
{
QByteArray data = buffer->mid(0, size);
buffer->remove(0, size);
size = 0;
*s = size;
emit dataReceived(data);
}
}
}
}
qint32 ArrayToInt(QByteArray source)
{
qint32 temp;
QDataStream data(&source, QIODevice::ReadWrite);
data >> temp;
return temp;
}注意:不使用此方法来传输大文件,因为使用此方法,消息的全部内容在发送之前都放在内存中,这会导致内存的高使用率。因为32位有符号的INT有最大值给2,147,483,647,如果输入数据的值大于字节,它就不能工作。保重。
发布于 2013-12-12 15:33:18
如前所述,在读取头之前,需要等待头完全发送,然后读取大量字节,并发出数据可用性信号。
这里有一个例子(未经测试):
//header file
class Peer {
//[...]
protected:
bool m_headerRead; //initialize to false
unsigned int m_size_of_data_to_read;
//[...]
};
//source file
void QPeer::sendData(QByteArray data)
{
int size = data.size();
_socket->write((const char*) &size, sizeof(int);
//use directly QIODevice::write(QByteArray)
_socket->write(data);
}
void QPeer::readData()
{
int bytes = _socket->bytesAvailable();
bool contains_enough_data = true;
while (contains_enough_data) {
if (! m_headerRead && _socket->bytesAvailable() >= sizeof(int)) {
//read header only and update m_size_of_data_to_read
m_headerRead = true;
} else if (m_headerRead && _socket->bytesAvailable >= m_size_of_data_to_read) {
//read data here
m_headerRead = false;
emit dataAvailable();
} else {
contains_enough_data = false; //wait that further data arrived
}
}
}https://stackoverflow.com/questions/20546750
复制相似问题