2009-08-27 5 views
3

J'envoie des messages sur TCP/IP, j'ai besoin de préfixer la longueur du message dans un tableau char puis de l'envoyer. Comment fait-on ça?Comment préfixer la longueur du message dans TCP/IP

Pouvez-vous s'il vous plaît fournir un exemple de comment l'extraire à l'autre extrémité. Et si possible, s'il vous plaît expliquer. J'utilise C++ et Winsock.

EDIT:

string writeBuffer = "Hello"; 

unsigned __int32 length = htonl(writeBuffer.length()); 

Il ne revient pas à la bonne longueur plutôt un très grand nombre.

Pour la partie réceptrice, si j'utilise ntohl(), alors j'obtiens aussi un grand nombre au lieu de la bonne longueur? Pourquoi est-ce si? Je reçois comme ce

bool Server::Receive(unsigned int socketIndex) 
{ 
    // Read data from the socket 
    if (receivingLength) 
    { 
     bytesReceived = recv(socketArray[socketIndex - WSA_WAIT_EVENT_0], 
      ((char*)&messageLength) + bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived, 0); 

     if (bytesReceived == SOCKET_ERROR) 
     { 
      return false; 
     } 

     if (bytesReceived == MESSAGE_LENGTH_SIZE) 
     { 
      // If uncomment the following line, 
      // I won't get the correct length, but a large number 
      //messageLength = ntohl(messageLength); 
      receivingLength = false; 
      bytesReceived = 0; 
      bytesLeft = messageLength; 
     } 
    } 
    else 
    { 
     if (bytesLeft > BUFFER_SIZE) 
     { 
      return false; 
     } 

     bytesReceived = recv(socketArray[socketIndex - WSA_WAIT_EVENT_0], 
      &receiveBuffer[bytesReceived], bytesLeft, 0); 

     if (bytesReceived == SOCKET_ERROR) 
     { 
      return false; 
     } 

     if (bytesReceived == messageLength) 
     { 
      // we have received full message 
      messageReceived = true; 

      receiveBuffer[bytesReceived] = '\0'; 

      // wait for next message 
      receivingLength = true; 
     } 

     bytesLeft -= bytesReceived; 
    } 

    return true; 
} 
+0

Votre machine utilise l'ordre des octets opposé à celui du réseau, de sorte que le nombre semble insuffisant après la conversion. N'essayez pas d'interpréter le résultat de htonl(), envoyez-le simplement. – sharptooth

+0

La même chose s'applique à ntohl - vous ne l'appliquez qu'à un nombre dans l'ordre des octets du réseau et n'interprétez que la valeur de retour. – sharptooth

+0

Pouvez-vous me dire comment le convertir à l'ordre des octets de ma machine dans .NET? (qui est l'application client) – akif

Répondre

6

Lors de l'envoi d'un champ de longueur sur un flux TCP, vous devez décider deux choses:

  1. quelle longueur doit la longueur avoir (1 octet, 2 octets, 4 octets, longueur variable)
  2. quelle finesse dois-je utiliser

Je recommande d'utiliser une longueur de 4 octets et un ordre d'octets de réseau (c.-à-d. big-endian). Pour l'ordre des octets réseau, les macros htonl et ntohl vont convertir entre l'ordre des octets de l'hôte (natif) (little-endian, dans votre cas) et l'ordre des octets du réseau.

Pour envoyer des données, le fragment devrait ressembler à ceci:

size_t length = strlen(data); 
uint32_t nlength = htonl(length); 
send(sock, &nlength, 4, 0); 
send(sock, data, length, 0); 

Du côté de la réception, vous extrayez d'abord la longueur, les données:

uint32_t length, nlength; 
recv(sock, &nlength, 4, 0); 
length = ntohl(nlength); 
data = malloc(length+1); 
recv(sock, data, length, 0); 
data[length] = 0; 

Qu'est-ce que ce code manque est gestion des erreurs: chacun des appels d'émission et de réception peut échouer; les recvs peuvent recevoir moins de données que prévu. Mais cela devrait vous donner une idée.

Modifier: Pour traiter le cas où le recv renvoie trop peu de données, exécutez-le en boucle, en gardant un compte de ce que vous avez lu jusqu'à présent, par ex. Htonl transforme l'entier en ordre d'octets du réseau.

int length_bytes = 0; 
while(length_bytes < 4){ 
    int read = recv(sock, ((char*)&nLength)+length_bytes, 4-length_bytes, 0); 
    if (read == -1) some_error_occurred_check_errno(); 
    length_bytes += read; 
} 
+0

Quel est le gars suivant? http://tangentsoft.net/wskfaq/examples/packetize.html Est-ce la même chose? – akif

+0

et quelle erreur de gestion dois-je faire? Je veux dire que vous donnez un long à recevoir cela va-t-il exploser? ou crash? – akif

+1

Dans l'exemple de code que vous donnez, il existe un tampon qui peut contenir des données futures (c'est-à-dire des messages ultérieurs). Cela complique les choses, mais peut aider la performance car elle évite les petites lectures (ce qui ne devrait pas vraiment importer). En ce qui concerne la gestion des erreurs: il peut arriver que le premier recv vous donne seulement 2 octets, pas quatre. Alors, nlength ne sera pas complètement initialisé, et vous passerez une taille bidon à malloc. Cela peut en effet planter. Plus probablement, vous allez allouer trop de données, puis essayez également de lire beaucoup plus de données que vous obtiendrez, ce qui vous rendra avec un non-sens dans la chaîne. –

0

Je ne sais pas si c'est ce que vous demandez, mais je l'envoyer comme une chaîne d'une longueur fixe (disons 4 caractères, longueur réelle à vous) . En d'autres termes, si la longueur des données est 164, envoyez la chaîne "0164". Cela surmonte tout problème d'ordre des octets à l'autre extrémité, et est facile à lire et à déboguer.

Pour produire ces chaînes, vous pouvez utiliser un stringstream:

#include <sstream> 
#include <iomanip> 

std::string MakeLenStr(int n) { 
    std::ostringstream os; 
    os << std::setw(4) << std::setfill('0') << n; 
    return os.str(); 
} 

Pour envoyer:

std::string s = MakeLenStr(len); 
send(sock, s.c_str(), 4); 

Pour le lire à l'autre bout, quelque chose comme:

char a[5] = {0}; 
recv(sock, a, 4); 
int n = atoi(a); 
1

Pseudocode (gestion des erreurs omise, attention):

expéditeur:

u_long converted = htonl(messageLength); // convert from local byte order to network byte order 
send(socket, (char*)&converted, sizeof(converted), 0); 

récepteur:

u_long messageLength; 
recv(socket, (char*)&messageLength, sizeof(messageLength), 0); 
messageLength = ntohl(messageLength); convert from network byte order to local byte order 
+0

Quel est le gars suivant? http://tangentsoft.net/wskfaq/examples/packetize.html Est-ce la même chose? ou est-ce différent – akif

Questions connexes