Qt C++ TCP 客户端与 Python Twisted 服务器
我正在尝试把一个非常简单的 twisted "你好,世界" 服务器和一个基本的 Qt TCP 客户端连接起来。
这个客户端使用了这些信号:
connect(&socket, SIGNAL(connected()), this, SLOT(startTransfer()));
connect(&socket, SIGNAL(readyRead()), this, SLOT(readServer()));
然后 readServer() 看起来是这样的:
ui->resultLabel->setText("Reading..");
QDataStream in(&socket);
//in.setVersion(QT_4_0);
if (blockSize == 0) {
if (socket.bytesAvailable() < (int)sizeof(quint16))
return;
in >> blockSize;
}
if (socket.bytesAvailable() < blockSize)
return;
QString theResult;
in >> theResult;
qDebug() << in;
qDebug() << theResult;
ui->resultLabel->setText(theResult);
我用来测试的服务器只是从 twisted 的文档中拿来的一个示例。
from twisted.internet.protocol import Protocol, Factory
from twisted.internet import reactor
### Protocol Implementation
# This is just about the simplest possible protocol
class Echo(Protocol):
def dataReceived(self, data):
"""
As soon as any data is received, write it back.
"""
self.transport.write(data)
def main():
f = Factory()
f.protocol = Echo
reactor.listenTCP(8000, f)
reactor.run()
if __name__ == '__main__':
main()
readServer() 被调用得很好,但似乎从来没有收集到任何数据。我看到有人提到这可能和 QDataStream 的 << 操作符有关,因为 Python 发送数据的方式和 Qt 期待的有些不同。
我承认我对 C++ 或 Qt 不是很在行,但这个项目的想法是写一个客户端来和现有的 twisted 服务器一起工作,所以虽然客户端可以修改,但我别无选择,只能让它和这个服务器一起工作。
提前感谢任何帮助。
2 个回答
理解TCP的重要一点是,它并不是用来传输特定大小的消息(或者称为“块”)。它实际上是用来传输一串字节的。当你写下这样的代码:
if (socket.bytesAvailable() < (int)sizeof(quint16))
return;
那么你最好在某个地方有个循环来再次调用这段代码,因为第一次调用的时候,你不能保证能收到所有需要的字节来通过这个检查。
你没有分享任何负责向回声服务器发送数据的代码,所以我们无法确切知道为什么你的Qt客户端没有收到你期待的数据。但根据上面的内容,如果出现了不完整数据的缓冲问题,我也不会感到惊讶。
确保如果你决定不从套接字读取数据,因为数据不够,你可以在之后再尝试读取,看看是否有更多数据到达。一个好的方法是始终将数据读取到一个应用程序的缓冲区,这样你就可以使用select()或epoll()等方法来判断套接字上是否有更多数据可用。然后只需在应用程序的缓冲区上进行操作即可。
问题的根源在于QDataStream,它对读取的数据要求比较严格。
幸运的是,我发现了QDataStream::readRawData,这个方法对Python发送的数据更友好(后来我发现这和twisted没有关系,而是Python的socket实现本身的问题。)最后的代码看起来是这样的:
//use socket to construct a QDataStream object, like before
QDataStream in(&socket);
//in.setVersion(QDataStream::Qt_4_0);
char buffer[1024] = {0};
//readRawData takes a char to dump to and the length,
//so I'm sure there is a better way to do this. It worked for my example.
in.readRawData(buffer, socket.bytesAvailable());
QString result;
result = buffer;
ui->resultLabel->setText(result);