Отправка XML-сообщения в деталях через сокет TCP с помощью Qt

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

  •  28-09-2019
  •  | 
  •  

Вопрос

Мы пишем проект, в котором есть клиент, который генерирует запросы XML, отправляет их на сервер, который анализирует запрос и возвращает запрошенную информацию в строке XML.

Приложение работает нормально, когда XML отвечает невелики, но когда они превышают около 2500 символов, которые они иногда отключаются на конце клиента. Я иногда говорю, потому что когда клиент и сервер запускаются на одной и той же машине и обмениваются домой через домашний адрес 127.0.0.1, ответы проанализированы просто нормально. Однако, когда клиент и сервер находятся на разных машинах и общаются через локальную сеть, это когда клиент сокращает сообщение примерно на 2500 символов.

Общение осуществляется сокетами TCP. Мы используем QT, у клиента есть QtcposeSocket, а сервер a qtcpserver и указатель на qtcpotte.

Мы думаем, что возможное решение для нашей проблемы отправляет 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);

Когда он получает новое соединение, это называет игровой слот 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-строку, сломанную на кусочки, а сервер прочитал все фигуры и снова включить его в одну строку?

Это было полезно?

Решение

Что происходит из-за «поток» природы протокола TCP. Данные разделяются во многие пакеты, а в вашем приложении вы действительно читаете только часть из них (Bytesavailabilabilable () не требуется равным количеству байтов, которые отправлен другой хост, это просто сколько байтов доступно в Разъем буфера). Что вам нужно сделать, это установить простой протокол между клиентом и сервером. Например, клиент сначала отправляет символ STX, затем XML, затем символ ETX. Когда сервер видит символ STX, он все считывается в буфер до уровня EXT. Другой подход - отправьте 4-байтовое целое число в порядке сетевого байта, указывающего размер данных XML в байтах, затем отправьте XML. Другой хост должен получить целое число, преобразуйте его в свой собственный байтформенный заказ, затем прочитайте указанное количество данных из разъема в буфер.

Другие советы

В TCP есть что-то известное как максимальный размер сегмента. Перед инициализацией передачи данных обе стороны решают MSS в фазе рукопожатия SYN. Это причина, почему ваши данные расщепляются.

У вас только один клиент. Сервер отправит ответ на каждый обрабатываемый чтение. Вам нужен аналогичный механизм на стороне клиента, чтобы обработать чтения. Функция, которая читает, пока она не прочитала число байтов. Вы можете отправить значение N в начале передачи данных.

Comp 3004 я вижу. Такой кошмар, мы пытались с QXMLstreamreader и QXMLstreamwriter. Писатель приятный и простой, но читатель - это кошмар, мы пытаемся использовать ошибку преможения DrematureendOFDocument как точку перерыва, чтобы узнать, что есть больше данных.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top