Qt QIODevice::write / QTcpSocket::write和写入的字节数

4
我们对于QIODevice::write的行为感到相当困惑,尤其是QTcpSocket的实现。虽然已经有一个类似的问题,但答案并不令人满意。主要的困惑来自于上述提到的bytesWritten信号以及waitForBytesWritten方法。这两个似乎指示了由QIODevice使用的缓冲区中写入到实际底层设备的字节数(如果没有这样的缓冲区,该方法就不会有太多意义)。问题在于,QIODevice::write返回的数字是否对应于这个数字,或者在这种情况下,它是否表示存储在内部缓冲区中的字节数,而不是写入到底层设备的字节数。如果返回的数字表示写入到内部缓冲区中的字节数,我们需要使用以下模式来确保我们的所有数据都被写入:
void writeAll(QIODevice& device, const QByteArray& data) {
   int written = 0;
   do {
     written = device.write(data.constData() + written, data.size() - written);
   } while(written < data.size());
}

然而,如果QIODevice::write的返回值与bytesWritten信号的含义相对应,则会插入重复数据。文档对此非常混乱,因为在两种方法中都使用了“device”一词,尽管似乎逻辑和一般理解是,一个实际上表示写入缓冲区,而不是设备。
因此,总结一下,问题是:QIODevice::write返回的数字是否是写入底层设备的字节数,因此可以安全地调用QIODevice::write而不检查返回的字节数,因为所有内容都存储在内部缓冲区中?还是它指示它可以在内部存储多少字节,并且必须采用类似上面的writeAll模式才能安全地将所有数据写入设备?
(更新:查看源代码,QTcpSocket :: write实现实际上永远不会返回比要写入的字节数更少的字节,因此不需要上面的writeAll。但是,这是特定于套接字和此Qt版本的,文档仍然令人困惑...)
2个回答

5

QTcpSocket是一个有缓冲的QAbstractSocket。在QAbstractSocket内部分配了一个缓冲区,并将数据复制到该缓冲区中。write的返回值是传递给write()的数据大小。

waitForBytesWritten会等待QAbstractSocket内部缓冲区中的数据被写入本机套接字。


1

那个之前的问题回答了你的问题,QIODevice::write(const char * data, qint64 maxSize) 的文档也是如此:

从数据中写入最多 maxSize 字节到设备中。返回实际写入的字节数,如果发生错误则返回 -1。

这可能会(在实际情况下)返回少于您请求的数据量,您需要再次调用 write 来写入剩余的数据。

至于 waitForBytesWritten

对于缓冲设备,此函数等待缓冲写入数据的有效负载已写入设备...

它仅适用于缓冲设备。并非所有设备都是缓冲的。如果是缓冲设备,并且您写入的数据量小于缓冲区容量,则 write 可以在设备完成发送所有数据之前成功返回。

设备不一定是缓冲的。


那么waitForBytesWritten是如何适应其中的呢?文档中似乎对设备这个词有点过载,因为它既指写入的内部缓冲区,但也必须指实际的底层设备用于waitForBytesWritten。如果不是这样,那么该方法根本就没有任何意义,因为我已经从write中获取了信息,所以Qt必须使用一个内部缓冲区。而且,write的返回值要么返回到该缓冲区(正如你所说),要么也返回到底层设备(这是文档中使用的术语)。因此,这对我来说非常不清楚。 - Janick Bernet
编辑过的。向设备写入是向设备写入,无论它是否被缓冲。 - Mat
谢谢,我并不完全了解缓冲和非缓冲设备的概念。然而,仍然很困惑的是,在 QIODevice::write 中使用 'device' 意味着内部缓冲区还是实际设备,而在 waitForBytesWritten 方法中则明确指的是实际底层设备。此外,根据 crysm 的答案,QTcpSocket 显然是有缓冲区的,并且在当前实现中没有写文件的情况,实际上可能仍然与 waitForBytesWritten 中的写入概念相同。 - Janick Bernet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接