我想通过 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/