我想通过Qt中的socket读取数据。我正在使用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();
}
我试过通过发送不同的数据大小无法搞清楚为什么?请给我一个指出我出错的地方的解决方案
答案 0 :(得分:5)
您的问题源于您对TCP工作方式的误解。
当数据从发送方传输时,它被分成数据包,然后逐个传输每个数据包,直到所有数据都完成发送。如果数据包丢失,它们将被重新传输,直到它们到达目的地或达到超时。
作为一个额外的复杂因素,每个数据包在到达目的地之前可能会遵循不同的路由。接收方的任务是向发送方确认已收到数据包,然后确保数据包以正确的顺序重新连接在一起。
因此,网络路由越长,获得数据重组的延迟就越大。这是您在localhost 与联网计算机测试中遇到的问题。
计算机上的IP堆栈在将数据传递到您的应用程序之前不会等待完整数据到达,但如果它按顺序丢失数据包,它将暂停。
e.g。如果您有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
的文档。
编辑: 我很抱歉,上面的代码中存在一个错误,它没有考虑到许多可能性,最重要的是,如果已经收到所有数据,并且一旦所有数据最终到达,则结束游戏。< / p>
以下单行更改应该清除:
更改
while(socket->bytesAvailable() < bytesRemaining){
要
while (bytesRemaining > 0) {
答案 1 :(得分:0)
所以你说waitForReadyRead()返回false,无论你的缓冲区有多少3000个预期字节,都会给出时间。您还想要其他什么行为?也许你需要在这里重新考虑触发器逻辑。许多TCP / IP应用程序协议具有某种帧起始检测逻辑,它们与所需的消息大小相结合,然后触发处理。这使他们能够应对中间网络将要施加的广泛不同的包大小,以及截断/部分消息。一旦你工作了,通过你的手机连接到它,你将得到不同的数据包碎片示例集来测试。