QT 套接字不读取所有数据
Posted
技术标签:
【中文标题】QT 套接字不读取所有数据【英文标题】:QT socket does no read all data 【发布时间】:2015-03-10 04:04:04 【问题描述】:我想通过 Qt 中的套接字读取数据。我正在使用 QBytearray 来存储数据。实际上,服务器一次发送 4095 个字节,但在 QT 客户端,由于我的应用程序设计,我收到了不同的块。
void Dialog::on_pushButton_clicked()
socket=new QTcpSocket(this);
socket->connectToHost("172.17.0.1",5000);
if(socket->waitForConnected(-1))
qDebug()<<"Connected";
Read_data();
void Dialog::Read_data()
QString filename(QString("%1/%2.bin").arg(path,device));
qDebug()<<"filename"<<filename;
QFile fileobj(filename);
int cmd,file_size,percentage_completed;
if(!fileobj.open(QFile::WriteOnly | QFile::Text))
qDebug()<<"Cannot open file for writting";
return;
QTextStream out(&fileobj);
while(1)
socket->waitForReadyRead(-1);
byteArray=socket->read(4);
qDebug()<<"size of bytearray"<<byteArray.size();
length=0xffff & ((byteArray[3]<<8)|(0x00ff & byteArray[2]));
int rem;
byteArray=socket->read(length);
while(byteArray.size()!=length)
rem=length-byteArray.size();
byteArray.append( socket->read(rem));
fileobj.write(byteArray);
fileobj.flush();
byteArray.clear();
服务器代码:
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<sys/ioctl.h>
#include<mtd/mtd-user.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include<math.h>
#include <netinet/tcp.h>
static int msb,lsb,size,listenfd = 0, connfd = 0,len;
main()
struct sockaddr_in serv_addr;
serverlen=sizeof(serv_addr);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(5000);
if(bind(listenfd,(struct sockaddr*)&serv_addr, sizeof(serv_addr))<0)
perror("\n Error in binding");
exit(1);
size=100000;
listen(listenfd, 1);
fd=fopen(new.bin,"r");
len=4089;
while(1)
buff[0]=25;
buff[1]=2;
buff[2]=60;
buff[3]=47;
n=fread(buff+4,1,length, fd);
buff[len+4]=5;
buff[len+5]='\n';
if(n>0)
sent_bytes=send(connfd,buff,n+6,0);
size =size-len;
if(size==0)
break;
如果我在 localhost(127.0.0.1) 中执行代码,我可以完全接收数据。只有当我连接到不同的主机 IP 时才会出现问题。请在这方面帮助我
编辑 1: 问题是当 bytesAvailable() 返回我等待的最大字节时 waitForReadyRead() 超时。如果 bytesAvailable() 小于预期,它工作正常。 bytesAvailable() 是否分配任何受此行为困扰的缓冲区。
while(1)
while(socket->bytesAvailable()<4)
if (!socket->waitForReadyRead())
qDebug() << "waitForReadyRead() timed out";
return;
byteArray=socket->read(4);
length=0xffff & ((byteArray[3]<<8)|(0x00ff & byteArray[2]));
int rem_bytes=length+2;
qDebug()<<"bytes available"<<socket->bytesAvailable();
while(socket->bytesAvailable()<=rem_bytes)
qDebug()<<"reading";
if (!socket->waitForReadyRead(10000))//times out here if bytesAvailable() == rem_bytes but executes well in other cases
qDebug() << "waitForReadyRead() timed out";
return;
qDebug()<<"ready";
byteArray.append(socket->read(rem_bytes));
qDebug()<<"size of bytearray"<<byteArray.size();
if(byteArray.size()==length+2)
for(int j=0;j<length;j++)
newarray.append(byteArray[j]);
fileobj.write(newarray);
fileobj.flush();
newarray.clear();
byteArray.clear();
break;
else
rem_bytes -=byteArray.size();
Send();
我尝试通过发送不同大小的数据无法弄清楚为什么?请提供一个解决方案,指出我哪里出错了
【问题讨论】:
你能提供更多关于你的代码的上下文吗?即,您的变量是什么显式类型(QTcpSocket,QByteArray) 很难从代码中的稀疏信息中说出来......但你知道吗,使用 tcp 没有办法告诉你的数据到达了多少块大小的块?您必须确保自己等到所有数据都到达后再开始处理数据 【参考方案1】:您的问题源于您对 TCP 工作原理的误解。
当数据从发送方发送时,它被分成数据包,然后每个数据包一个接一个地发送,直到所有数据发送完毕。如果数据包丢失,它们会被重新传输,直到它们到达目的地或超时。
作为一个额外的复杂因素,每个数据包在到达目的地之前可能会遵循不同的路线。接收方的任务是向发送方确认已收到数据包,然后确保数据包以正确的顺序重新连接在一起。
因此,网络路由越长,在重新组装数据时出现延迟的可能性就越大。这就是您在 localhostvsus 联网计算机测试中遇到的情况。
您计算机上的 IP 堆栈不会等待完整数据到达后再将其传递给您的应用程序,但如果它按顺序丢失了一个数据包,它将暂停。
例如如果您有 10 个数据包并且数据包 4 最后到达,IP 堆栈会将数据分成两组传递给您的应用程序:1-2-3、[[等待 4 到达]]、4-5-6-7-8 -9-10。
因此,当waitForReadyRead()
返回true
时,您不能期望所有数据都已到达,您必须始终检查实际接收了多少字节。
您的代码中有两个地方可以等待数据。您等待的第一件事是一个四字节的数字,告诉您已经发送了多少数据。即使您很可能已收到所有四个字节,但最好还是检查一下。
while(socket.bytesAvailable() < 4)
if (!socket.waitForReadyRead()) // timeout after 30 second, by default
qDebug() << "waitForReadyRead() timed out";
return;
byteArray=socket->read(4);
qDebug()<<"size of bytearray"<<byteArray.size();
length=0xffff & ((byteArray[3]<<8)|(0x00ff & byteArray[2]));
接下来您需要做的是不断循环等待-读取-等待-读取循环,直到所有数据都到达,每次都记录您仍希望接收多少字节。
int bytesRemaining = length;
while(socket->bytesAvailable() < bytesRemaining)
if (!socket->waitForReadyRead())
qDebug() "waitForReadyRead() timed out";
return;
// calling read() with the bytesRemaining argument will not guarantee
// that you will receive all the data. It only means that you will
// receive AT MOST bytesRemaining bytes.
byteArray = socket->read(bytesRemaining);
bytesRemaining -= byteArray.size();
fileobj.write(byteArray);
fileobj.flush();
这就是说,您不应该在主线程中使用阻塞 API,否则您的 GUI 可能会死机。我建议要么使用异步 API,要么创建一个工作线程来处理下载(并在工作线程中使用阻塞 API)。
要查看如何使用这两种不同 API 的示例,请查看 Fortune Client Example
和 Blocking Fortune Client Example
的文档。
编辑: 很抱歉,上面的代码中存在一个错误,它没有考虑多种可能性,最重要的是,如果所有数据都已经收到,并且一旦所有数据最终到达就结束游戏。
以下一行更改应该可以解决这个问题:
改变
while(socket->bytesAvailable() < bytesRemaining)
到
while (bytesRemaining > 0)
【讨论】:
感谢 Robbie E,这真的很有用。但现在我面临一个奇怪的 waitForReadyRead() 行为。在进行上述更改后,我已经编辑了我的代码。 我已经尝试过了,但我的问题仍然存在。有时它发生在开始,有时发生在中间。如果正在等待 3000 字节并且 bytesavailable() 返回 1000,则 waitForReadyRead() 返回 true,但只要 bytesAvailabe() 有 3000 字节,waitfForReadyRead() 就会返回 false。我尝试了不同的数据大小,结果保持不变。【参考方案2】:所以你说 waitForReadyRead() 一旦你的缓冲区有所有 3000 个预期字节,就会返回 false,不管给定的时间。你还想要什么其他行为?也许您需要重新考虑这里的触发逻辑。许多 TCP/IP 应用程序协议具有某种帧开始检测逻辑,它们与所需的消息大小相结合,然后触发处理。这使他们能够应对中间网络将施加的广泛不同的包大小,以及截断/部分消息。一旦你让它工作,通过你的手机连接到它,你会得到不同的数据包碎片示例集进行测试。
【讨论】:
以上是关于QT 套接字不读取所有数据的主要内容,如果未能解决你的问题,请参考以下文章