为啥 QTcpSocket 接收到错误的数据?

Posted

技术标签:

【中文标题】为啥 QTcpSocket 接收到错误的数据?【英文标题】:Why QTcpSocket received wrong data?为什么 QTcpSocket 接收到错误的数据? 【发布时间】:2013-06-29 15:39:53 【问题描述】:

我正在编写一个简单的网络应用程序。客户端发送到服务器消息服务器在 QTextEdit 中打印并响应客户端。 我正在使用 QTcpServer 和 QTcpSocket。 有一个我无法解决的问题。接收数据是 quint16 + QTime + QString,它作为 QByteArrey 发送。 我使用 quint16 接收数据块的大小。并且由于某种原因,当客户端发送到服务器时

next block size: 16 (quint16 value)
block size: 18 

服务器获取:

next block size: 30073  (quint16 value)
block size: 18 

如您所见,由于某种原因,服务器从 QDataStrem 获取了错误的变量值,并且始终为 30073。我不明白为什么?

void Widget::slotSendToServer()

    logTextEdit->append("slotSendToServer()");
    QByteArray arrBlock;
    QDataStream serverSendStream(&arrBlock, QIODevice::ReadWrite);
    QString messageStr = messageLineEdit->text();

    serverSendStream << quint16(0) << QTime::currentTime()
                     << messageStr;

    serverSendStream.device()->seek(0);
    serverSendStream << (quint16)(arrBlock.size() - sizeof(quint16));

    qDebug() << "next_block_size:"
             <<(quint16)(arrBlock.size() - sizeof(quint16)) 
             << endl
             << "full size of Byte arrey:" << arrBlock.size();

    tcpSocket->write(arrBlock);
    messageLineEdit->clear();




void Widget::slotReadClient()

    logTextEdit->append("slotReadClient()");
    QTcpSocket *tcpSocket = (QTcpSocket*)sender();
    QDataStream clientReadStream(tcpSocket);

    while(true)
    
        if (!next_block_size)
        
            if (tcpSocket->bytesAvailable() < sizeof(quint16))
            
                break;
            
            clientReadStream >> next_block_size;
        

        if (tcpSocket->bytesAvailable() < next_block_size)
        
            break;
        
        QTime   time;
        QString messageTextStr;
        clientReadStream >> time >> messageTextStr;

        QString messageCompleteStr =
                time.toString() + " " + "Client has sent - " 
                                + messageTextStr;
        logTextEdit->append("Message received: ");
        logTextEdit->append(messageCompleteStr);

        next_block_size = 0;

        sendToClient(tcpSocket,
                     "Server Response: Received \"" 
                      + messageTextStr + "\"");
    

【问题讨论】:

TL;博士!请缩小范围并仅发布相关代码。 @Joachim Pileborg 完成。 您可以尝试两件事:不要使用流和流运算符,而是尝试使用套接字函数进行写入/读取(如writeread)。第二件事是检查发送方和接收方的字节顺序,它们是否相同? 这似乎不是字节顺序问题。什么字节顺序会给你 30073 而不是 16? 在阅读任何内容之前您是否将next_block_size 初始化为0? 【参考方案1】:

您应该确保每次连接套接字时变量next_block_size初始化为0。

如果您不重用相同的 QTcpSocket 对象,这可以在您的 Widget 类构造函数中完成,或者如果您这样做,则在连接到信号 connected() 的插槽中完成。

【讨论】:

【参考方案2】:

我不知道你为什么做的这么复杂。 这应该有效:

void Widget::slotSendToServer()

    logTextEdit->append("slotSendToServer()");
    QByteArray arrBlock;
    QDataStream serverSendStream(tcpSocket);

    serverSendStream << QTime::currentTime()
                     << messageLineEdit->text();
    messageLineEdit->clear();


void Widget::slotReadClient()

    logTextEdit->append("slotReadClient()");
    QTcpSocket *tcpSocket = (QTcpSocket*)sender();
    QDataStream clientReadStream(tcpSocket);

    QTime time;
    QString message;

    clientReadStream >> time >> message;

    emit YourSignal(time, message);

您不必担心大小,QDataStream 会跟踪它,您只需保持与写入时相同的读取顺序,因此您的缓冲区arrBlock 是对代码的浪费。


根据documentation,当读取时并非所有数据都可用时,我的代码应该阻塞,这只是缺点。对于像日期和一些字符串这样的小数据,它永远不会发生。

关于你的代码,你肯定用 while 循环弄乱了你的阅读器,所以这里是没有这个循环的更正。

void Widget::slotReadClient()

    logTextEdit->append("slotReadClient()");
    QTcpSocket *tcpSocket = (QTcpSocket*)sender();
    QDataStream clientReadStream(tcpSocket);

    if (next_block_size==0) 
        // need to read data size
        if (tcpSocket->bytesAvailable() < sizeof(quint16))
            return; // wait for next signal
        // next_block_size must be type of qint16
        clientReadStream >> next_block_size;
    
    if (tcpSocket->bytesAvailable() < next_block_size) 
        // not enought data to complete read immediately
        return;  // wait for next signal
    
    QTime time;
    QString messageTextStr;
    clientReadStream >> time >> messageTextStr;
    QString messageCompleteStr =
                time.toString() + " " + "Client has sent - " 
                                + messageTextStr;
    logTextEdit->append("Message received: ");
    logTextEdit->append(messageCompleteStr);

    // mark that data read has been finished
    next_block_size = 0;

【讨论】:

所以,即使我有一个大文件,我也不在乎尺寸? 如果您使用 QDataStream 来写入/读取数据,则不会。它嵌入了所有必需的信息。对于大块数据,阻塞线程可能存在问题,但您的示例没有这个问题。 这行不通。当接收到一些数据时调用该槽,而不是在读取消息所需的所有数据时调用,因此,如果没有足够的可用字节,QDataStream::status() 将返回QDataStream::ReadPastEnd。这就是为什么您必须使用一种方法来确定整个消息是否已到达,方法是在消息前面加上大小或消息结束标志。 文档并没有说readData会阻塞,只是它会返回已经可用的字节(可以是0),或者返回-1表示错误或关闭插座。并且函数源代码显示,如果接收到任何数量的数据,它将立即返回(恕我直言,根据 Qt API 的设计,阻塞意味着用户应该能够指定超时,这只能通过waitForReadyRead 函数)。 while 循环也是必要的,因为如果同时收到几条完整的消息,只会发出一个readyRead() 信号,所以你不会有任何机会重读这些消息,直到一些新数据到达并发出新的readyRead() 信号。

以上是关于为啥 QTcpSocket 接收到错误的数据?的主要内容,如果未能解决你的问题,请参考以下文章

Qt笔记-解决QTcpSocket发送数据成功,但接收端接收不全的问

Qt笔记-解决QTcpSocket发送数据成功,但接收端接收不全的问题

UART 到 Qt 软件错误 - 为啥数据总是分裂?

QTcpSocket 读取错误

无法写入 QRunnable 内的 QTcpSocket

C++ Qt - QTcpSocket - 找不到文件