QTを使用してTCPソケットを介して部品でXMLメッセージを送信する
-
28-09-2019 - |
質問
XMLリクエストを生成し、それらをサーバーに送信するクライアントがいるプロジェクトを作成しています。
XMLの応答が小さいときにアプリケーションは正常に機能しますが、約2500文字を超えると、クライアントの端でカットされることがあります。クライアントとサーバーが同じマシンで実行され、自宅の住所127.0.0.1を通信すると、返信が正常に解析されると時々言います。ただし、クライアントとサーバーが異なるマシン上にあり、LANを介して通信している場合、これはクライアントが約2500文字にメッセージをカットするときです。
通信はTCPソケットによって行われます。 QTを使用しています。クライアントにはQTCPSocket、サーバーはQTCPSERVERとQTCPSocketへのポインターを使用しています。
私たちの問題の可能な解決策は、文字カウントまたはタグで区切られたXMLを断片的に送信することだと思います。メッセージを部品に分割するのは簡単ですが、部品を送信し、クライアントまたはサーバーにパーツを1つのXMLリクエストにコンパイルしてコンパイルすることで問題が発生します。
たとえば、クライアントに複数の部品でリクエストを送信するようにテストしたいと考えました。
クライアント関数呼び出しは、リクエストを送信します。 XMLREQは他の場所に生成され、渡されます。メッセージを部分に分割する例として、XML要求からクロージングタグを取り除き、後で別のピースとして送信します。
QString ClientConnection::sendRequest(QString xmlReq)
{
this->xmlRequest = xmlReq;
QHostAddress addr(address);
QList<QString> messagePieces;
xmlRequest.remove("</message>");
messagePieces.append(xmlRequest);
messagePieces.append("</message>");
client.connectToHost(addr,6789);
if(client.waitForConnected(30000))
{
for(int i = 0; i < messagePieces.length();i++)
{
client.write(messagePieces[i].toAscii(),messagePieces[i].length()+1);
qDebug() << "Wrote: " << messagePieces[i];
}
}
char message[30000] = {0};
xmlReply = "";
if(client.waitForReadyRead(30000)){
client.read(message,client.bytesAvailable());
}else{
xmlReply = "Server Timeout";
}
client.close();
xmlReply = (QString) message;
return xmlReply;
}
次はサーバーコードです。 XMLクロージングメッセージタグが表示されるまでクライアントからメッセージを読み取り、データを処理してクライアントに返信するまで、クライアントからメッセージを読み取ることになっているように書かれています。
これは、サーバーを起動するコードです。
//Start the server, pass it the handler so it can perform queries
connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
server.listen(QHostAddress::Any, 6789);
新しい接続を取得すると、このように見えるAcceptConnectionスロットを呼び出します
void CETServer::acceptConnection()
{
client = server.nextPendingConnection();
connect(client, SIGNAL(readyRead()), this, SLOT(startRead()));
}
スターリードは次のように見えます:
void CETServer::startRead()
{
char buffer[1024*30] = {0};
client->read(buffer, client->bytesAvailable());
QString readIn;
readIn = (QString) buffer;
ui->statusText->appendPlainText("Received: " + readIn);
//New messages in will be opened with the xml version tag
//if we receive said tag we need to clear our query
if (readIn.contains("<?xml version =\"1.0\"?>",Qt::CaseSensitive))
{
xmlQuery = "";
}
//add the line received to the query string
xmlQuery += readIn;
//if we have the clsoe message tag in our query it is tiem to do stuf with the query
if(xmlQuery.contains("</message>"))
{
//do stuff with query
ui->statusText->appendPlainText("Query received:" + xmlQuery);
QString reply = this->sqLite->queryDatabase(xmlQuery);
xmlQuery = "";
this->commandStatus(reply);
if(client->isWritable()){
//write to client
client->write(reply.toAscii(),reply.length()+1);
ui->statusText->appendPlainText("Sent to client: " + reply);
client->close();
}
}}
私の考えでは、スタートの読み取りは、クライアントがメッセージを書き込むときはいつでも、サーバーがそれを読み取り、サーバーのストアのXMLRequestに添付するような方法でコーディングされています。メッセージが含まれている場合、XMLクロージングタグが含まれている場合、リクエストを処理します。
しかし、何が起こるかは、クライアントが連続した書き込みを行う場合、サーバーはそれらすべてを読み取らず、最初のものだけでなく、XMLクロージングタグを受信することはないため、リクエストを処理しません。
私が答える必要がある質問は、なぜサーバーが複数の書面でクライアントに応答しないのですか? XML文字列を送信して破片にし、サーバーにすべてのピースを読み取り、再び1つの文字列に変えることができるように、どのように作成する必要がありますか?
解決
それは、TCPプロトコルの「ストリーム」の性質のために起こっています。データは多くのパケットに分割されており、アプリでは、それらの一部だけを読んでいます(bytesavailable()は、他のホストが送信したバイトの量と等しく必要ありません。ソケットバッファー)。あなたがしなければならないことは、クライアントとサーバーの間に簡単なプロトコルを確立することです。たとえば、クライアントは最初にSTX文字、次にXML、次にETX文字を送信します。サーバーがSTX文字を見ると、すべてをバッファーに読み取り、ext文字まで読み取ります。その他のアプローチ - バイト単位のXMLデータのサイズを示す4バイト整数をネットワークバイト順序で送信し、XMLを送信します。他のホストは、整数を受け取り、ネイティブバイトオーダーに変換し、指定された量のデータをソケットからバッファーまで読み取る必要があります。
他のヒント
TCPでは、最大セグメントサイズと呼ばれるものがあります。データ転送の初期化の前に、両当事者はSynハンドシェイクフェーズでMSSを決定します。それがあなたのデータが分割されている理由です。
client.read()は1つだけです。サーバーは、処理された読み取りごとに返信を送信します。読み取りを処理するには、クライアント側に同様のメカニズムが必要です。 nのバイト数を読み取るまで読み取る関数。データ転送の開始時に値nを送信できます。
comp 3004わかります。このような悪夢は、QXMLStreamReaderとQXMLStreamWriterで試しています。作家は素晴らしくてシンプルですが、読者は悪夢です。私たちは、より多くのデータがあることを知るためのブレークポイントとして、早期dowdocumentエラーを使用しようとしています。