2010-11-28 5 views
1

Nous écrivons un projet où un client génère des requêtes XML, les envoie à un serveur, qui analyse la requête et renvoie les informations demandées dans une chaîne XML.Envoi d'un message xml en pièces via un socket TCP en utilisant Qt

L'application fonctionne correctement lorsque les réponses xml sont petites, mais lorsqu'elles dépassent environ 2500 caractères, elles sont parfois tronquées du côté client. Je dis parfois parce que lorsque le client et le serveur s'exécutent sur la même machine et communiquent via l'adresse de la maison 127.0.0.1, les réponses sont très bien analysées. Cependant, lorsque le client et le serveur se trouvent sur des machines différentes et communiquent sur un réseau local, le client coupe le message à environ 2500 caractères.

La communication est effectuée par des sockets tcp. Nous utilisons Qt, le client a un qTCPsocket, et le serveur un qTCPserver et un pointeur vers un qtcpsocket.

Nous pensons qu'une solution possible à notre problème consiste à envoyer le fichier XML en morceaux, soit séparés par nombre de caractères ou par étiquette. Bien qu'il soit facile pour nous de diviser le message en plusieurs parties, envoyer les parties et demander au client ou au serveur de lire et de compiler les parties dans une requête XML nous cause des problèmes. Par exemple, nous voulions tester le fait que le client envoie une requête en plusieurs parties.

Voici notre appel de fonction client pour envoyer une requête. xmlReq est généré ailleurs où et passé. Comme exemple de rupture du message en parties, nous supprimons la balise de fermeture de la requête xml, puis nous l'envoyons comme une autre pièce 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 telle façon qu'il est supposé lire les messages du client jusqu'à ce qu'il voit la balise de fermeture de xml, puis traiter les données et renvoyer la 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 à ce

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 telle sorte qu'à chaque fois que le client écrit un message, le serveur le lit et l'attache à la xmlRequest que le serveur stocke. Si le message contient la balise de fermeture xml, il traite la requête. Ce qui se passe cependant, c'est que si le client effectue des écritures successives, le serveur ne les lit pas tous, seulement le premier, et ne reçoit jamais la balise de fermeture xml, et ne traite donc aucune requête.

La question à laquelle je dois répondre est la suivante: pourquoi le serveur ne répond pas aux écritures multiples des clients? Comment dois-je faire pour que je puisse envoyer une chaîne de caractères en morceaux, et que le serveur lise toutes les pièces et les transforme en une seule chaîne?

+0

Ceci est un trou de sécurité: "char buffer [1024 * 30] = {0}; client-> read (buffer, client-> bytesAvailable());" Il peut y avoir plus d'octets disponibles que l'ajustement dans votre tampon, puis vous avez un dépassement de tampon. –

Répondre

0

Cela se produit en raison de la nature "en flux" du protocole TCP. Les données sont divisées en plusieurs paquets, et dans votre application, vous n'en lisez réellement qu'une partie (bytesAvailable() n'est pas nécessaire égal à la quantité d'octets envoyés par l'autre hôte, c'est juste combien d'octets sont disponibles dans le tampon de socket). Ce que vous devez faire est d'établir un protocole simple entre le client et le serveur. Par exemple, le client envoie d'abord le caractère STX, puis le XML, puis le caractère ETX. Lorsque le serveur voit un caractère STX, il lit tout dans la mémoire tampon jusqu'au caractère EXT. Autre approche - envoie un entier de 4 octets dans l'ordre des octets du réseau indiquant la taille des données XML en octets, puis envoie un fichier XML. L'autre hôte doit recevoir l'entier, le convertir en son byteorder natif, puis lire la quantité de données spécifiée depuis le socket vers le buffer.

1

Dans TCP, il existe quelque chose de connu sous le nom de taille de segment maximale. Avant l'initialisation du transfert de données, les deux parties décident du MSS dans la phase de prise de contact SYN. C'est la raison pour laquelle vos données sont divisées.

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

0

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

Questions connexes