QT 套接字不读取所有数据

标签 qt sockets

我想通过 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();
}

我尝试通过发送不同的数据大小无法弄清楚为什么?请为我提供一个解决方案,指出我出错的地方

最佳答案

您的问题源于您对 TCP 工作原理的误解。

当数据从发送方传输时,它被分解成数据包,然后每个数据包一个一个地传输,直到所有数据都发送完毕。如果数据包丢失,它们将被重新传输,直到它们到达目的地或超时。

更复杂的是,每个数据包在到达目的地之前可能会遵循不同的路线。接收方的任务是向发送方确认已收到数据包,然后确保数据包以正确的顺序重新连接在一起。

因此,网络路由越长,重新组装数据时出现延迟的可能性就越大。这就是您在本地主机与联网计算机测试中遇到的情况。

您计算机上的 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) {

关于QT 套接字不读取所有数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28955961/

相关文章:

c++ - 无法使用 QML/C++ 中的绑定(bind)对象初始化 UI

c++ - QDateTimeAxis (QtCharts) 中的错误值

c++ - 如何在 Windows 7 任务栏中显示进度(使用 Qt)?

c - 为什么recvfrom关心数据来自谁

python - 使用 select 处理多个请求

c++ - 如果底层 Windows 是 32 位或 64 位版本,如何在正在运行的 C++/Qt 应用程序中进行检测?

qt - QML 中是否有类似于 QT 的 setMask() API 的 API?

php - 多人PHP游戏(套接字编程)

android - 两个 Android 设备之间的 RFCOMM 连接?

c++ - 是否可以配置 UDP 多播套接字以便可以调用 write() 而不是 sendto()?