2009-01-29 31 views
6

Tengo una matriz de bytes que estoy leyendo desde un NetworkStream. Los primeros dos bytes indican la longitud del paquete que sigue y luego el paquete se lee en una matriz de bytes de esa longitud. Los datos que necesito leer de la matriz NetworkStream/byte tienen algunas cadenas, es decir, datos de longitud variable terminados por nuevos caracteres de línea, y algunos campos de ancho fijo como bytes y longs. Por lo tanto, algo como esto:Leer línea de matriz de bytes (no convertir matriz de bytes a cadena)

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

que conozco (y tienen algo que decir en) el formato del paquete de datos que está llegando a través, y lo que tengo que hacer es leer una "línea" para cada valor de cadena, pero lea un número fijo de bytes para los bytes y longs. Hasta ahora, mi solución propuesta es usar un bucle while para leer bytes en una matriz de bytes temporales hasta que haya un carácter de nueva línea. Luego, convierta los bytes a una cadena. Esto me parece kludgy, pero no veo otra manera obvia. Me doy cuenta de que podría usar StreamReader.ReadLine(), pero eso implicaría otra transmisión y ya tengo un NetworkStream. Pero si esa es la mejor solución, lo intentaré.

La otra opción que he considerado es hacer que mi equipo de backend escriba un byte o dos para esas longitudes de los valores de cadena para que pueda leer la longitud y luego leer la cadena en función de la longitud especificada.

Por lo tanto, como puede ver, tengo algunas opciones sobre cómo hacer esto, y me gustaría su opinión sobre lo que consideraría la mejor manera de hacerlo. Aquí está el código que tengo ahora para leer todo el paquete como una cadena. El siguiente paso es dividir los diversos campos del paquete y hacer el trabajo de programación real que se necesita hacer, crear objetos, actualizar la interfaz de usuario, etc. en función de los datos del paquete.

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

Respuesta

9

Personalmente

  1. Poner Int16 en el inicio de las cuerdas, para que sepa cuánto tiempo que van a ser, y
  2. Utilice la clase IO.BinaryReader a haga la lectura, "leerá", ints, cadenas, caracteres etc. en variables, por ejemplo BinReader.ReadInt16() leerá dos bytes, devolverá el int16 que representan y moverá dos bytes en la secuencia

Espero que esto ayude.

P.S. Tenga cuidado al utilizar el método ReadString, supone que la cadena se antepone con enteros personalizados de 7 bits, es decir, que fue escrita por la clase BinaryWriter. El siguiente es de esta CodeGuru posterior

La clase tiene dos métodos BinaryWriter para escribir cadenas: el método sobrecargado Write() y el WriteString() método. El primero escribe la cadena como una secuencia de bytes de acuerdo con la codificación que está usando la clase. El método WriteString() también utiliza la codificación especificada , pero prefija la secuencia de bytes de la cadena con la longitud real de la cadena. Tales cadenas con prefijo se leen en via BinaryReader.ReadString().

Lo interesante del valor de longitud lo que el menor número de bytes como sea posible se utilizan para mantener este tamaño, es almacenado como un tipo llamado un 7 bits número entero codificado. Si la longitud encaja en 7 bits se usa un byte, si es mayor que este, entonces el bit alto en se establece el primer byte y se crea un segundo byte cambiando el valor por 7 bits. Esto se repite con bytes sucesivos hasta que haya bytes suficientes para mantener el valor. Este mecanismo se utiliza para asegurarse de que la longitud no se convierta en una parte significativa del tamaño tomado por la serie serializada. BinaryWriter y BinaryReader tienen métodos para leer y escribir enteros codificados de 7 bits, pero están protegidos y por lo tanto puede usarlos solo si deriva de estas clases.

+0

No había encontrado BinaryReader antes. Parece que será inmensamente útil para todas estas cosas en las que estoy trabajando. ¡Gracias! – jxpx777

+0

Yah, el BinaryReader no funcionó para nosotros porque una aplicación Java está escribiendo los datos en el hilo. Terminé "forzándolo en bruto" leyendo en la matriz de bytes y luego maldiciéndolo en base a nuestra estructura de paquetes de datos. – jxpx777

5

Me gustaría ir con cadenas de longitud con prefijo. Se le hará la vida mucho más simple, y eso significa que puede representar cadenas con saltos de línea en algunos comentarios sobre su código aunque:.

  • no utilice Stream.DataAvailable. El hecho de que no haya datos disponibles ahora no significa que haya leído el final de la transmisión.
  • A menos que esté absolutamente seguro de que nunca necesitará texto más allá de ASCII, no use ASCIIEncoding.
  • No suponga que Stream.Read leerá todos los datos que le solicite. Siempre compruebe el valor de retorno.
  • BinaryReader hace mucho de esto mucho más fácil (incluyendo cadenas de longitud prefijada y una Leer que se repite hasta que se ha leído lo que se ha pedido a)
  • que está llamando BitConverter.ToUInt16 dos veces en los mismos datos. ¿Por qué?
Cuestiones relacionadas