1

私たちは、xml 要求を生成してサーバーに送信し、要求を解析して要求された情報を 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()));
}

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 つの文字列に再度変換できるようにするにはどうすればよいですか?

4

3 に答える 3

1

TCP には、最大セグメント サイズと呼ばれるものがあります。データ転送の初期化の前に、両当事者は SYN ハンドシェイク フェーズで MSS を決定します。これが、データが分割されている理由です。

client.read() は 1 つだけです。サーバーは、処理された読み取りごとに応答を送信します。読み取りを処理するには、クライアント側でも同様のメカニズムが必要です。N バイトを読み取るまで読み取る関数。データ転送の開始時に値 N を送信できます。

于 2010-11-28T00:29:21.963 に答える
0

これは、TCPプロトコルの「ストリーム」の性質が原因で発生しています。データは多くのパケットに分割され、アプリでは実際にはそれらの一部のみを読み取っています(bytesAvailable()は、他のホストが送信したバイト数と同じである必要はありません。これは、ソケットバッファ)。あなたがしなければならないことは、クライアントとサーバーの間に単純なプロトコルを確立することです。たとえば、クライアントは最初にSTX文字を送信し、次にXMLを送信し、次にETX文字を送信します。サーバーはSTX文字を検出すると、EXT文字まですべてをバッファーに読み込みます。他のアプローチ-XMLデータのサイズをバイト単位で示すネットワークバイトオーダーで4バイト整数を送信してから、XMLを送信します。他のホストは整数を受け取り、それをネイティブのバイトオーダーに変換する必要があります。

于 2010-11-28T07:19:48.107 に答える
0

COMP 3004 なるほど。そのような悪夢、私たちは QXmlStreamReader と QXmlStreamWriter を試してきました。ライターは素晴らしくシンプルですが、リーダーは悪夢です。私たちは PrematureEndOfDocument エラーをブレークポイントとして使用して、さらにデータがあることを認識しようとしました。

于 2010-12-01T02:16:01.823 に答える