Qtのソケットからデータを読み取りたい。QBytearrayを使用してデータを保存しています。実際、サーバーは1回のストレッチで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がどのように機能するかについてのあなたの誤解から生じています。
データが送信者から送信されると、データはパケットに分割され、すべてのデータの送信が完了するまで、各パケットが1つずつ送信されます。パケットが欠落した場合、宛先に到達するか、タイムアウトに達するまで、パケットは再送信されます。
さらに複雑なことに、各パケットは宛先に到着する前にさまざまなルートをたどる場合があります。受信者には、パケットが受信されたことを送信者に確認し、パケットが正しい順序で結合されていることを確認するタスクがあります。
このため、ネットワークルートが長いほど、データの再構築が遅れる可能性が高くなります。これは、ローカルホストとネットワークコンピューターのテストで経験したことです。
コンピューターのIPスタックは、完全なデータが到着するのを待たずにアプリケーションに渡しますが、パケットが順番に欠落している場合は一時停止します。
たとえば、10個のパケットがあり、パケット4が最後に到着した場合、IPスタックは2つのセットでデータをアプリケーションに渡します:1-2-3、[[4が到着するのを待つ]]、4-5-6-7- 8-9-10。
このため、がwaitForReadyRead()
返されるときにtrue
、すべてのデータが到着したとは期待できません。実際に受信されたバイト数を常に確認する必要があります。
コードには、データを待つ場所が2つあります。最初に待つのは、送信されたデータの量を示す4バイトの数値です。4バイトすべてを受信する可能性が高い場合でも、確認することをお勧めします。
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]));
次に行う必要があるのは、すべてのデータが到着するまで、wait-read-wait-readループを循環し続けることです。そのたびに、まだ受信する予定のバイト数を追跡します。
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を使用します)。
2つの異なるAPIの使用方法の例を確認するには、Fortune Client Example
とのドキュメントを参照してくださいBlocking Fortune Client Example
。
編集:申し訳ありませんが、上記のコードには多くの可能性を考慮していないバグがあります。最も重要なのは、すべてのデータがすでに受信されている場合、そしてすべてのデータが最終的に到着したらゲームを終了することです。
次の1行の変更で、それが明確になります。
変化する
while(socket->bytesAvailable() < bytesRemaining){
に
while (bytesRemaining > 0) {
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加