You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt C++ TCP通信:客户端未完整读取服务端数据问题求助

Qt C++ TCP服务端数据同步问题排查与解决

开发Qt C++ TCP服务端时遇到以下问题:服务端通过QTimer向socket写入数据块,客户端读取速度较慢,点击停止按钮停止QTimer后,socket中未被读取的数据无法被客户端接收,导致服务端窗口数据量多于客户端,无法实现两端数值完全一致的预期。

预期流程

  • 点击“启动服务端”按钮启动服务端,客户端点击“连接”按钮建立连接;
  • 在服务端窗口输入初始值,客户端点击“启动”按钮触发数值(Frequency、Attenuation、Snr、Temperature)自增;
  • 数值自增后,服务端通过QTimer将数据写入socket发送给客户端;
  • 使用QByteArray和QDataStream进行分块通信;
  • 通过QTimer更新QWidgetTable;
  • 客户端点击“停止”按钮终止自增,点击“断开连接”按钮断开连接;
  • 服务端与客户端的输出数值需完全一致。

目前除第7点外其余流程均已实现,但客户端读取速度过慢,停止后服务端已写入socket的数据未被客户端读取,导致两端数据量不一致。


服务端部分代码(Server.cpp)

void Server::readyRead()
{
nextBlockSize = 0;
QTcpSocket *client = (QTcpSocket*)sender();
qDebug() << client;
QDataStream in(client);
in.setVersion(QDataStream::Qt_6_5);
if (nextBlockSize == 0) {
    if (client->bytesAvailable() < sizeof(quint16))
        return;
    in >> nextBlockSize;
}
if (client->bytesAvailable() < nextBlockSize)
    return;

QString header;
QString cid;
QString footer;
in >> header;
in >> cid;
in >> footer;
if (cid == "0x1B10") {
    m_pWindow->addMessage("Client Connected");
}
if(cid =="0x1A10")
{
    m_pWindow->addMessage("Process Started");
    process();
}
 if(cid =="0x1A20")
{
    m_pWindow->addMessage("Process Stopped!");
    m_timer->stop();
}
 QDataStream out(client);
 out << quint16(0xFFFF);
 close();
 if(cid=="0x1B20")
 {
    m_pWindow->addMessage("Client Disconnected");
    disconnected();
 }

}

void Server::process()
{
m_timer = new QTimer(this);
connect (m_timer, SIGNAL(timeout()), this, SLOT(passvalue())) ;
m_timer->start(1000);
}

void Server::passvalue()
{
m_pWindow->getinput();
}

void Server::increment(int freq, int att, int snr, int temp)
{
if(freq>=3000)
    freq=20;
if(att>=100)
    att=0;
if(snr>=10)
    snr=0;
if(temp>=30)
    temp=0;
freq++;
att++;
snr++;
temp++;
bool success=m_pWindow->updateTable(freq,att,snr,temp);

if(success)
{
    QByteArray block;
    QDataStream out(&block, QIODevice::ReadWrite);
    out.setVersion(QDataStream::Qt_6_5);
    quint16 header=rand();
    quint16 payload1=freq;
    quint16 payload2=att;
    quint16 payload3=snr;
    quint16 payload4=temp;
    quint16 size=sizeof(payload1)+sizeof(payload2)+sizeof(payload3)+sizeof(payload4);
    quint16 footer=rand();
    out << quint16(0) << header << size << payload1 << payload2 << payload3 << payload4  << footer  ;
    uint16_t cs=checksum(block,size);
    out << cs;
    out.device()->seek(0);
    out << quint32(block.size() - sizeof(quint16));
    foreach (QTcpSocket *socket, clients)
    {
        qDebug() << socket;
        socket->write(block);
        socket->waitForBytesWritten();
    }
}

}

客户端部分代码(dialog.cpp)

void Dialog::readyRead()
{
nextBlockSize = 0;
QDataStream in(m_pClientscket);
m_pClientscket->waitForBytesWritten();
m_pClientscket->waitForReadyRead();
in.setVersion(QDataStream::Qt_6_5);
if (nextBlockSize == 0) {
    if (m_pClientscket->bytesAvailable() < sizeof(quint16))
        return;
    in >> nextBlockSize;
}
if (m_pClientscket->bytesAvailable() < nextBlockSize)
    return;
quint16 header;
quint16 payload1;
quint16 payload2;
quint16 payload3;
quint16 payload4;
quint16 size;
quint16 footer;
uint16_t cs;
updateTable(payload1,payload2,payload3,payload4);

}

void Dialog::updateTable(int freq,int att,int snr,int temp)
{
QTableWidgetItem *item;
ui->tableWidget->insertRow( ui->tableWidget->rowCount() );
for(int j=0;j<4;j++)
{
    item= new QTableWidgetItem;
    if(j==0)
        item->setText(QString::number(freq));
    if(j==1)
        item->setText(QString::number(att));
    if(j==2)
        item->setText(QString::number(snr));
    if(j==3)
        item->setText(QString::number(temp));
    ui->tableWidget->setItem(i,j,item);

}
ui->textEditFrequency->setText(QString::number(freq));
ui->textEditAttenuation->setText(QString::number(att));
ui->textEditSnr->setText(QString::number(snr));
ui->textEditTemp->setText(QString::number(temp));
i++;
}


void Dialog::on_pushButtonStop_clicked()
{
QByteArray block;
QDataStream out(&block, QIODevice::ReadWrite);
out.setVersion(QDataStream::Qt_6_5);
QString header="0x228";
QString cid="0x1A20";
QString footer="0x497";
out << quint16(0) << header << cid << footer  ;
out.device()->seek(0);
out << quint16(block.size() - sizeof(quint16));
m_pClientscket->write(block);
}

问题分析与解决建议

  1. 修复客户端数据读取逻辑

    • 客户端readyRead()中未按服务端的分块格式读取所有字段,payload1-payload4为未初始化的垃圾值,导致显示错误且残留数据无法解析。需补全读取代码:
      // 在readyRead()的bytesAvailable判断后添加:
      in >> header >> size >> payload1 >> payload2 >> payload3 >> payload4 >> footer >> cs;
      
    • 移除readyRead()中的waitForBytesWritten()和waitForReadyRead(),Qt的readyRead信号已触发说明有数据可读,阻塞等待会破坏事件循环,降低读取效率。
  2. 服务端停止后确保数据发送完成

    • 服务端收到停止指令(0x1A20)时,仅停止定时器无法保证已发送数据被接收,需添加缓冲区排空逻辑:
      if(cid =="0x1A20")
      {
          m_pWindow->addMessage("Process Stopped!");
          m_timer->stop();
          // 等待所有数据写入底层缓冲区并发送
          foreach (QTcpSocket *socket, clients)
          {
              socket->flush();
              socket->waitForBytesWritten(3000); // 设置3秒超时
          }
      }
      
  3. 客户端停止后读取残留数据

    • 客户端发送停止指令后,需继续监听readyRead信号,直到读取完服务端所有残留数据。可设置一个标记,允许读取完剩余数据后再停止表格更新逻辑。
  4. 严格保证分块格式一致性

    • 服务端和客户端的分块字段顺序、数据类型必须完全匹配,否则会导致解析错误,残留数据无法被正确读取。

内容的提问来源于stack exchange,提问作者Belle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 07:15:53