إرسال رسالة XML في أجزاء من خلال مقبس TCP باستخدام QT

StackOverflow https://stackoverflow.com/questions/4294651

  •  28-09-2019
  •  | 
  •  

سؤال

نحن نكتب مشروعًا حيث يوجد عميل يقوم بإنشاء طلبات XML ، ويرسلها إلى خادم ، يقوم بتجميل الطلب ويعيد المعلومات المطلوبة في سلسلة XML.

يعمل التطبيق بشكل جيد عندما تكون ردود XML صغيرة ، ولكن عندما تتجاوز حوالي 2500 حرفًا ، يتم قطعها أحيانًا على نهاية العميل. أقول في بعض الأحيان لأنه عندما يتم تشغيل العميل والخادم على نفس الجهاز والتواصل من خلال عنوان المنزل 127.0.0.1 يتم تحليل الردود على ما يرام. ومع ذلك ، عندما يكون العميل والخادم على أجهزة مختلفة والتواصل عبر LAN ، يكون ذلك عندما يقوم العميل بقطع الرسالة إلى حوالي 2500 حرف.

يتم الاتصال بواسطة مآخذ TCP. نحن نستخدم QT ، والعميل لديه QTCPSOCKE ، والخادم QTCPSERVER ومؤشر إلى QTCPSOCKET.

نعتقد أن الحل المحتمل لمشكلتنا هو إرسال XML إلى القطع ، إما مفصولة بواسطة عدد الأحرف أو العلامة. على الرغم من أنه من السهل علينا تقسيم الرسالة إلى أجزاء ، فإن إرسال الأجزاء وقراءة العميل أو الخادم وتجميع الأجزاء في طلب 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);

عندما يحصل على اتصال جديد ، فإنه يطلق على فتحة قبول Connection والتي تبدو هكذا

    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 ، وفكها في القطع ، وجعل الخادم يقرأ جميع القطع وتحويلها إلى سلسلة واحدة مرة أخرى؟

هل كانت مفيدة؟

المحلول

هذا يحدث بسبب طبيعة "الدفق" لبروتوكول TCP. يتم تقسيم البيانات إلى العديد من الحزم ، وفي تطبيقك ، أنت تقرأ حقًا جزءًا منها (Bytesavailivers مقبس العازلة). ما عليك فعله هو إنشاء بروتوكول بسيط بين العميل والخادم. على سبيل المثال ، يرسل العميل أولاً حرف STX ، ثم XML ، ثم حرف ETX. عندما يرى الخادم حرف STX ، فإنه يقرأ كل شيء في المخزن المؤقت حتى حرف EXT. نهج آخر - إرسال عدد صحيح من 4 بايت بترتيب بايت الشبكة الذي يشير إلى حجم بيانات XML بالبايت ، ثم إرسال XML. يجب أن يتلقى المضيف الآخر عدد صحيح ، وتحويله إلى Byteorder الأصلي ، ثم اقرأ الكمية المحددة من البيانات من المقبس إلى المخزن المؤقت.

نصائح أخرى

في TCP ، هناك شيء يعرف باسم الحد الأقصى لحجم الجزء. قبل تهيئة نقل البيانات ، يقرر كلا الطرفين MSS في مرحلة مصافحة SYN. هذا هو السبب في تقسيم بياناتك.

لديك عميل واحد فقط read (). سيرسل الخادم رد لكل معالجة القراءة. تحتاج إلى آلية مماثلة على جانب العميل للتعامل مع القراءات. وظيفة تقرأ حتى تقرأ عدد بايت. يمكنك إرسال القيمة n عند بداية نقل البيانات الخاصة بك.

شركات 3004 أرى. مثل هذا الكابوس ، كنا نحاول مع QXMLStreamReader و QXMLStreamWriter. الكاتب لطيف وبسيط ، لكن القارئ هو كابوس ، لقد نحاول استخدام خطأ PremustureendofDocument كنقطة استراحة لمعرفة وجود المزيد من البيانات.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top