首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >从QLocalSocket读取超过2048个字节

从QLocalSocket读取超过2048个字节
EN

Stack Overflow用户
提问于 2013-02-12 19:46:30
回答 1查看 2.5K关注 0票数 1

从QLocalSocket中读取超过2048个字节时出现问题。这是我的服务器端代码:

代码语言:javascript
复制
clientConnection->flush();  // <-- clientConnection is a QLocalSocket

QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);

out.setVersion(QDataStream::Qt_5_0);
out << (quint16)message.size() << message;  // <--- message is a QString

qint64 c = clientConnection->write(block);
clientConnection->waitForBytesWritten();
if(c == -1)
    qDebug() << "ERROR:" << clientConnection->errorString();

clientConnection->flush();

下面是我在客户端中读取数据的方式:

代码语言:javascript
复制
QDataStream in(sock);  // <--- sock is a QLocalSocket
in.setVersion(QDataStream::Qt_5_0);

while(sock->bytesAvailable() < (int)sizeof(quint16)){
    sock->waitForReadyRead();
}
in >> bytes_to_read; // <--- quint16

while(sock->bytesAvailable() < (int)bytes_to_read){
    sock->waitForReadyRead();
}

in >> received_message;

客户端代码连接到readyRead信号,并在第一次调用插槽后断开。

为什么我只能读取2048字节?

==EDIT==

peppe回复之后,我更新了我的代码。下面是它现在的样子:

服务器端代码:

代码语言:javascript
复制
clientConnection->flush();

QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);

out.setVersion(QDataStream::Qt_5_0);

out << (quint16)0;
out << message;
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));
qDebug() << "Bytes client should read" << (quint16)(block.size() - sizeof(quint16));

qint64 c = clientConnection->write(block);
clientConnection->waitForBytesWritten();

客户端代码:

代码语言:javascript
复制
QDataStream in(sock);
in.setVersion(QDataStream::Qt_5_0);

while(sock->bytesAvailable() < sizeof(quint16)){
    sock->waitForReadyRead();
}

quint16 btr;
in >> btr;

qDebug() << "Need to read" << btr << "and we have" << sock->bytesAvailable() << "in sock";
while(sock->bytesAvailable() < btr){
    sock->waitForReadyRead();
}

in >> received_message;

qDebug() << received_message;

我仍然无法读取更多的数据。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-02-12 23:33:16

代码语言:javascript
复制
out.setVersion(QDataStream::Qt_5_0);
out << (quint16)message.size() << message;  // <--- message is a QString

这是错误的。"message“的序列化长度将是message.size() *2+4个字节,因为QString将自己的长度预置为一个quint32,而每个QString字符实际上是一个UTF16代码单元,因此它需要2个字节。期望在读取器中只读取message.size()字节将导致QDataStream短时间读取,这是未定义的行为。

请务必检查这些行后面的“块”的大小--它将是2+4+2* message.size()字节。所以你需要修正这个数学模型。您可以放心地假设它不会改变,因为Qt数据类型is known and documented的序列化格式。

不过,我确实认识到了前缀长度的“设计模式”。它可能来自Qt附带的财富网络示例。UTF 16代码单元的字符串长度(这是没有意义的,因为它不是如何序列化的) --它预先考虑了序列化后的QString的长度。看看它做了什么:

代码语言:javascript
复制
    out << (quint16)0;
    out << fortunes.at(qrand() % fortunes.size());
    out.device()->seek(0);
    out << (quint16)(block.size() - sizeof(quint16));

首先,它通过编写0在输出中保留一些空间。然后序列化一个QString。然后,它使用序列化的QString的长度回溯并覆盖0 --此时,它正好是block.size()减去表示长度的前面的整数(我们知道quint16的序列化长度是sizeof(quint16))

重复一遍,实际上有两个原因解释了为什么这个例子是这样编写的,而且它们之间有某种联系:

  1. QDataStream没有从短读中恢复的方法:当您使用operator>>反序列化对象时,它成功解码对象所需的所有数据都必须可用。因此,在确保所有数据都已收到之前,您不能使用它。这将我们带到:
  2. TCP没有内置的机制来将数据分离到“记录”中。你不能只发送一些字节,后面跟着一个“记录标记”,它告诉接收者他已经收到了与记录相关的所有数据。TCP提供的是原始字节流。最后,您可以(半)关闭连接,向另一个对等体发出传输结束的信号。

1+2意味着您必须使用某种其他机制来了解(在接收方)您是否已经拥有所需的所有数据,或者您必须等待更多数据。例如,你可以引入带内标记,比如\r\n (就像IRC或者--在某种程度上-- HTTP do)。

fortune示例中的解决方案是在“实际”数据(带有fortune消息的序列化QString )前面加上该数据的字节长度;然后发送该长度(作为16位整数),后跟数据本身。

接收器首先读取长度;然后它读取那么多字节,然后它知道它可以解码命运。如果没有足够的数据可用(无论是长度还是有效载荷本身),客户端什么都不做,等待更多数据。

请注意:

  • the设计并不是新的:它是所有大多数协议都要做的。在“标准的”TCP/IP协议栈中,TCP、IP、以太网等等都在它们的“报头”中有一个字段,该字段指定了有效载荷的长度(或者在整个"record");
  • the传输的“长度”中使用一个按特定字节顺序发送的memcpy 16位无符号整数:它不是将()d放入缓冲区,而是使用QDataStream对其进行存储和读回。虽然这看起来微不足道,但这实际上完成了您正在使用的协议的定义。
  • 如果QDataStream已经能够从短读中恢复(f.i.通过抛出异常并将数据留在设备中),您将不需要发送有效载荷的长度,因为QDataStream 已经发送字符串的长度(作为32位无符号双字符整数),后跟UTF-16字符。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14831819

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档