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); }
问题分析与解决建议
修复客户端数据读取逻辑
- 客户端
readyRead()中未按服务端的分块格式读取所有字段,payload1-payload4为未初始化的垃圾值,导致显示错误且残留数据无法解析。需补全读取代码:// 在readyRead()的bytesAvailable判断后添加: in >> header >> size >> payload1 >> payload2 >> payload3 >> payload4 >> footer >> cs; - 移除
readyRead()中的waitForBytesWritten()和waitForReadyRead(),Qt的readyRead信号已触发说明有数据可读,阻塞等待会破坏事件循环,降低读取效率。
- 客户端
服务端停止后确保数据发送完成
- 服务端收到停止指令(
0x1A20)时,仅停止定时器无法保证已发送数据被接收,需添加缓冲区排空逻辑:if(cid =="0x1A20") { m_pWindow->addMessage("Process Stopped!"); m_timer->stop(); // 等待所有数据写入底层缓冲区并发送 foreach (QTcpSocket *socket, clients) { socket->flush(); socket->waitForBytesWritten(3000); // 设置3秒超时 } }
- 服务端收到停止指令(
客户端停止后读取残留数据
- 客户端发送停止指令后,需继续监听
readyRead信号,直到读取完服务端所有残留数据。可设置一个标记,允许读取完剩余数据后再停止表格更新逻辑。
- 客户端发送停止指令后,需继续监听
严格保证分块格式一致性
- 服务端和客户端的分块字段顺序、数据类型必须完全匹配,否则会导致解析错误,残留数据无法被正确读取。
内容的提问来源于stack exchange,提问作者Belle
相关产品推荐
相关产品推荐

