2009-01-29 7 views
6

J'ai un tableau d'octets que je lis depuis un NetworkStream. Les deux premiers octets indiquent la longueur du paquet qui suit et ensuite le paquet est lu dans un tableau d'octets de cette longueur. Les données que j'ai besoin de lire dans le tableau NetworkStream/byte ont quelques chaînes, c'est-à-dire des données de longueur variable terminées par de nouveaux caractères de ligne, et certains champs de largeur fixe comme des octets et des longs. Donc, quelque chose comme ceci:Lire la ligne du tableau d'octets (ne pas convertir le tableau d'octets en chaîne)

// I would have delimited these for clarity but I didn't want 
// to imply that the stream was delimited because it's not. 
StringbyteStringStringbytebytebytelonglongbytelonglong 

Je sais (et ont leur mot à dire) le format du paquet de données qui vient à travers, et ce que je dois faire est de lire une « ligne » pour chaque valeur de chaîne, mais lisez un nombre fixe d'octets pour les octets et les longs. Jusqu'à présent, ma solution proposée consiste à utiliser une boucle while pour lire des octets dans un tableau d'octets temp jusqu'à ce qu'il y ait un caractère de retour à la ligne. Ensuite, convertissez les octets en une chaîne. Cela me semble compliqué, mais je ne vois pas d'autre moyen évident. Je me rends compte que je pourrais utiliser StreamReader.ReadLine() mais cela impliquerait un autre flux et j'ai déjà un NetworkStream. Mais si c'est la meilleure solution, je vais essayer.

L'autre option que j'ai envisagée est d'avoir mon équipe backend écrire un octet ou deux pour les longueurs des valeurs String afin que je puisse lire la longueur et ensuite lire la chaîne en fonction de la longueur spécifiée. Donc, comme vous pouvez le voir, j'ai quelques options pour y arriver, et j'aimerais savoir ce que vous considérez comme la meilleure façon de le faire. Voici le code que j'ai en ce moment pour lire dans le paquet entier comme une chaîne. L'étape suivante consiste à séparer les différents champs du paquet et à effectuer le travail de programmation qui doit être effectué, en créant des objets, en mettant à jour l'interface utilisateur, etc. en fonction des données contenues dans le paquet.

string line = null; 
while (stream.DataAvailable) 
{ 
    //Get the packet length; 
    UInt16 packetLength = 0; 
    header = new byte[2]; 
    stream.Read(header, 0, 2); 
    // Need to reverse the header array for BitConverter class if architecture is little endian. 
    if (BitConverter.IsLittleEndian) 
     Array.Reverse(header); 
    packetLength = BitConverter.ToUInt16(header,0); 

    buffer = new byte[packetLength]; 
    stream.Read(buffer, 0, BitConverter.ToUInt16(header, 0)); 
    line = System.Text.ASCIIEncoding.ASCII.GetString(buffer); 
    Console.WriteLine(line); 
} 

Répondre

9

Personnellement, je

  1. Mettre Int16 au début des chaînes, vous savez combien de temps ils vont être et
  2. Utilisez la classe IO.BinaryReader à faire la lecture, il va "lire", ints, les chaînes, les caractères etc en variable par exemple BinReader.ReadInt16() va lire deux octets, retourner l'int16 qu'ils représentent, et déplacer deux octets dans le flux

Espérons que cela aide.

P.S. Faites attention en utilisant la méthode ReadString, elle suppose que la chaîne est précédée d'entiers personnalisés de 7 bits, c'est-à-dire qu'elle a été écrite par la classe BinaryWriter. Ce qui suit est de ce poste CodeGuru

La classe BinaryWriter a deux méthodes pour les chaînes d'écriture: la méthode et la WriteString() méthode surcharge Write(). Le premier écrit la chaîne sous la forme d'un flux d'octets en fonction du codage utilisé par la classe. La méthode WriteString() utilise également le codage spécifié , mais il préfixe le flux d'octets de la chaîne avec la longueur réelle de la chaîne. De telles chaînes préfixées sont relues via BinaryReader.ReadString().

La chose intéressante à propos de la longueur valeur ce que quelques octets possible sont utilisés pour maintenir cette taille, il est stocké comme un type appelé 7 bits entier codé. Si la longueur correspond à 7 bits, un seul octet est utilisé, s'il est supérieur à celui-ci alors le bit haut sur le premier octet est défini et un second octet est créé en décalant la valeur de 7 bits. Ceci est répété avec octets successifs jusqu'à ce qu'il y ait assez d'octets pour contenir la valeur. Ce mécanisme est utilisé pour s'assurer que la longueur ne devient pas une partie significative de la taille prise par la chaîne sérialisée. BinaryWriter et BinaryReader ont méthodes pour lire et écrire entiers codés 7 bits, mais ils sont protégés et donc vous pouvez les utiliser seulement si vous dérivez de ces classes.

+0

N'avait pas rencontré BinaryReader avant. Cela semble être extrêmement utile pour tout ce sur quoi je travaille. Merci! – jxpx777

+0

Yah, le BinaryReader n'a pas fonctionné pour nous parce que les données sont écrites dans le thread par une application Java. J'ai fini par "forcer brutalement" en lisant dans le tableau d'octets, puis en le maudissant en fonction de notre structure de paquets de données. – jxpx777

5

J'irais avec des chaînes préfixées en longueur. Il vous rendra la vie beaucoup plus simple, et cela signifie que vous pouvez représenter des chaînes avec des sauts de ligne dans quelques commentaires sur votre code cependant:.

  • Ne pas utiliser la Stream.DataAvailable. Juste parce qu'il n'y a pas de données disponibles maintenant ne signifie pas que vous avez lu la fin du flux.
  • Sauf si vous êtes absolument sûr que vous n'aurez jamais besoin de texte au-delà de l'ASCII, n'utilisez pas ASCIIEncoding.
  • Ne supposez pas que Stream.Read lira toutes les données que vous lui demandez. Toujours Vérifiez la valeur de retour.
  • BinaryReader fait beaucoup de cela beaucoup plus facile (y compris les chaînes de longueur préfixée et une lecture qui boucle jusqu'à ce qu'il soit lu ce que vous avez demandé à)
  • Vous appelez BitConverter.ToUInt16 deux fois sur les mêmes données. Pourquoi?
Questions connexes