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 ExampleBlocking 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 套接字不读取所有数据的主要内容,如果未能解决你的问题,请参考以下文章

客户端不从套接字读取数据?

如何在 Qt 中读取下一个数据包

OpenCV imshow() 防止 Qt / python 崩溃

QT学习笔记(13) QT下的UDP通信

QT下UDP套接字通信——QUdpSocket 简单使用

在 C++/Qt 中用于高效套接字通信的数据格式 [关闭]