如何在QTcpSocket中读取完整的数据?

7
现在服务器(使用Java实现)将向我发送一些流数据,我的代码如下:
connect(socket, SIGNAL(readyRead()), this, SLOT(read_from_server()));

read_from_server()函数中:

{
    while (socket->bytesAvailable())
    {
        QString temp = socket->readAll();
    }
}

我发现即使服务器只发送了几个字符的字符串,数据也会被截断,并且我的函数被调用两次,因此temp是我想要的不完整的数据。
如果服务器发送了更长的字符串,我的函数可能会被调用三次或更多次,这使得我难以知道何时数据被完全传输。
有没有人告诉我如何轻松地完全接收数据,而不需要那么多麻烦的步骤?如果这与其他问题重复了,我很抱歉,我无法让它们的答案对我起作用。非常感谢!

4个回答

15
您所看到的是客户端和服务器之间通信的正常情况。数据以数据包的形式发送,并且readyRead信号会通知您的程序有可用数据,但它没有关于数据内容或数量的概念,因此您需要处理这个问题。
为了正确读取数据,您需要一个缓冲区,如@ratchetfreak所提到的,将字节附加到从流中读取的字节后面。重要的是,您要知道正在发送的数据格式,以便知道何时有完整的消息。我以前使用过至少两种方法来实现这一点:-
1)确保发送的消息以消息大小(以字节为单位)开头。在接收数据时,您首先读取大小并将其附加到缓冲区,直到总大小达到预期大小。
2)以已知格式发送所有数据,例如JSONXML,可以检查消息的结尾。例如,在JSON的情况下,所有数据包都将以打开大括号'{'开始,并以关闭大括号'}'结束,因此您可以计算大括号并匹配数据,或者使用QJsonDocument :: fromRawData验证数据是否完整。

我曾使用过这两种方法,我建议使用第一种方法;包括正在发送的消息的大小。


关于“2”,如果对象包含带括号的字符串,则计算括号将是一个问题。最好尝试正确解析它,并在失败时假定它不完整。 - Guilherme Bernal
我完全同意,你需要考虑到这一点,并且也要检查字符串,尽管我提到了使用QJsonDocument来验证数据。感谢你的指出;O) - TheDarkKnight
@Merlin069 谢谢你的经验! - yakiang
@TheDarkKnight 如果我要从PHP发送一个JSON数据包到与服务器应用程序连接的套接字,我需要不断向QByteArray添加字节,直到检查成功,检查是否所有字节加在一起等于一个QJsonDocument吗?我已经花了几天时间来处理这个套接字流,直到你掌握它,它是相当复杂的。 - CantThinkOfAnything
1
@CantThinkOfAnything 简单的答案是:是的。但是,你可以发送单个Json对象并检查它们是否完整,每个对象都建立QJsonDocument,但最好先在字节数组中构建整个文档。 - TheDarkKnight

3
你可以使用缓冲区字段暂时保存未完成的数据,并在数据包完成后处理它们:
{
    while (socket->bytesAvailable())
    {
        buffer.append(socket->readAll());
        int packetSize = getPacketSize(buffer);
        while(packetSize>0)
        {
            handlePacket(buffer.left(packetSize);
            buffer.remove(0,packetSize);
            packetSize = getPacketSize(buffer);
        }
    }
}

2
如果所有数据尚未到达,则您的while循环将过早退出。您需要使用一种消息格式,让接收代码确定何时已完全接收消息。例如,消息可以以长度元素开头,或者如果您正在处理文本,则消息可以以某个字符作为终止符号结尾。

谢谢,我会请求服务器添加一个结束字符 :) - yakiang

2
问题是在tcp数据传输期间,数据以未定义的块发送。如果您要尝试读取定义的块大小,则必须事先知道预期的块大小或者有一种方法确定块何时结束(类似于零终止的C字符串)。
请检查此答案是否对您有帮助(有一种技巧可以等待预期的数据块)。

谢谢分享 :) 像你说的那样,我会找到一些方法来确定块何时结束。 - yakiang

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