Question

Nous écrivons un projet où il y a un client qui génère des requêtes XML, les envoie à un serveur, qui analyse la demande et renvoie les informations demandées dans une chaîne XML.

L'application fonctionne très bien lorsque les réponses XML sont petites, mais quand ils dépassent ils sont coupés parfois environ 2500 caractères hors du côté client. Je dis parfois parce que lorsque le client et le serveur fonctionnent sur la même machine et communiquer à l'adresse de la maison 127.0.0.1 les réponses sont analysées très bien. Cependant, lorsque le client et le serveur sont sur des machines différentes et communiquent lan, c'est lorsque le client coupe le message à environ 2500 caractères.

La communication se fait par les prises de tcp. Nous utilisons Qt, le client dispose d'un QTcpSocket, et le serveur a qTCPserver et un pointeur vers un QTcpSocket.

Nous pensons qu'une solution possible à notre problème envoie le xml en morceaux, soit séparés par le nombre de caractères ou par tag. Alors qu'il est facile pour nous de rompre le message en plusieurs parties, l'envoi des pièces et ayant le client ou le serveur lu et compilons les pièces en une seule requête XML nous causer des ennuis.

Par souci d'exemple, nous avons voulu tester avec le client envoie une requête en plusieurs parties.

Voici notre appel de fonction client pour envoyer une demande. xmlReq est produit ailleurs où et transmise. A titre d'exemple de briser le message en plusieurs parties, nous supprimons la balise de fermeture de la requête XML, puis de l'envoyer comme un autre morceau plus tard.

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;

}

Suivant est notre code serveur. Il est écrit de manière telle qu'il est censé lire les messages du client jusqu'à ce qu'il voit la balise de message de fermeture de XML et de traiter les données et envoyer le dos de réponse au client.

Ceci est le code qui démarre le serveur.

//Start the server, pass it the handler so it can perform queries
    connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
    server.listen(QHostAddress::Any, 6789);

Quand il obtient une nouvelle connexion, il appelle la fente de acceptConnection qui ressemble à ceci

    void CETServer::acceptConnection()
{
    client = server.nextPendingConnection();
    connect(client, SIGNAL(readyRead()), this, SLOT(startRead()));
}

Le startRead ressemble à ceci:

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();

        }
    }}

Dans mon esprit, la lecture de départ est codé de manière à ce que chaque fois que le client a écrit un message, le serveur lit et se fixe au XMLRequest le serveur stocke. Si le message contient, la balise de fermeture xml, il traite la demande.

Qu'est-ce qui se passe bien, est si le client ne écrit successives, le serveur ne les lit pas tous, seul le premier, et ne reçoit jamais la balise de fermeture xml, et traite donc aucune demande.

La question que je besoin répondre est pourquoi ne pas répondre du serveur aux clients écritures multiples? Comment dois-je m'y rendre telle que je peux envoyer une chaîne xml, décomposé en morceaux, et que le serveur de lire tous les morceaux et la transformer en une chaîne à nouveau?

Était-ce utile?

La solution

Ce qui se passe à cause de la nature « flux » du protocole TCP. Les données est divisé en plusieurs paquets, et dans votre application, vous lisez vraiment seulement une partie d'entre eux (bytesAvailable () n'est pas nécessaire égale à la quantité d'octets l'autre ENVOYÉ hôte, il est juste combien d'octets sont disponibles dans le tampon de socket). Qu'est-ce que vous avez à faire est d'établir un protocole simple entre le client et le serveur. Par exemple, le client envoie d'abord le caractère STX, le XML, puis caractère ETX. Lorsque le serveur voit un caractère STX, il lit tout dans la mémoire tampon jusqu'à ce que le caractère EXT. Autre approche - envoyer un nombre entier de 4 octets dans l'ordre des octets du réseau indiquant la taille des données XML en octets, puis envoyer XML. L'autre hôte doit recevoir le nombre entier, le convertir en sa byteorder native, puis lire la quantité spécifiée de données de la prise de la mémoire tampon.

Autres conseils

Dans TCP, il y a quelque chose connu comme la taille du segment maximum. Avant l'initialisation du transfert de données, les deux parties décident du MSS dans la phase de prise de contact SYN. Telle est la raison pour laquelle vos données sont séparés.

Vous avez un seul client.read (). Le serveur envoie une réponse pour chaque lecture traitée. Vous avez besoin d'un mécanisme similaire du côté client à poignée lit. Une fonction qui lit jusqu'à ce qu'il a lu nombre N d'octets. Vous pouvez envoyer la valeur N au début de votre transfert de données.

COMP 3004 que je vois. Un tel cauchemar, nous avons essayé avec QXmlStreamReader et QXmlStreamWriter. L'écrivain est agréable et simple, mais le lecteur est un cauchemar, nous avons essayé d'utiliser l'erreur PrematureEndOfDocument comme point de rupture pour savoir il y a plus de données.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top