mostrar - matriz nxm en c#
Trabajando con matrices de bytes en C# (11)
No creo que puedas hacer algo así en C #. Puede hacer que la función Parse () use un desplazamiento, o crear arreglos de 3 bytes para comenzar; uno para el encabezado IP, uno para el encabezado TCP y otro para la carga útil.
Tengo una matriz de bytes que representa un paquete TCP / IP completo. Para aclarar, la matriz de bytes se ordena de esta manera:
(Encabezado IP - 20 bytes) (Encabezado TCP - 20 bytes) (Carga útil - X bytes)
Tengo una función Parse
que acepta una matriz de bytes y devuelve un objeto TCPHeader
. Se parece a esto:
TCPHeader Parse( byte[] buffer );
Dado el conjunto de bytes original, aquí está la forma en que llamo a esta función en este momento.
byte[] tcpbuffer = new byte[ 20 ];
System.Buffer.BlockCopy( packet, 20, tcpbuffer, 0, 20 );
TCPHeader tcp = Parse( tcpbuffer );
¿Hay una forma conveniente de pasar la matriz de bytes TCP, es decir, los bytes 20-39 del paquete TCP / IP completo, a la función Parse
sin extraerla a una nueva matriz de bytes primero?
En C ++, podría hacer lo siguiente:
TCPHeader tcp = Parse( &packet[ 20 ] );
¿Hay algo similar en C #? Quiero evitar la creación y la posterior recolección de elementos no utilizados de la matriz de bytes temporal si es posible.
No hay forma de usar código verificable para hacer esto. Si su método Parse puede tratar con tener un <byte> de IEnumerable, entonces puede usar una expresión LINQ
TCPHeader tcp = Parse(packet.Skip(20));
Puede usar LINQ para hacer algo como:
tcpbuffer.Skip(20).Take(20);
Pero System.Buffer.BlockCopy / System.Array.Copy son probablemente más eficientes.
Si puede cambiar el método de análisis (), cámbielo para aceptar el desplazamiento donde debe comenzar el procesamiento. TCPHeader Parse (byte [] buffer, int offset);
Si realmente necesita este tipo de control, debe observar la característica unsafe
de C #. Le permite tener un puntero y fijarlo para que GC no lo mueva:
fixed(byte* b = &bytes[20]) {
}
Sin embargo, esta práctica no se recomienda para trabajar con código solo administrado si no hay problemas de rendimiento. Podría pasar el desplazamiento y la longitud como en la clase Stream
.
Si un IEnumerable<byte>
es aceptable como entrada en lugar de byte[]
, y está usando C # 3.0, entonces podría escribir:
tcpbuffer.Skip(20).Take(20);
Tenga en cuenta que esto todavía asigna instancias de enumerador debajo de las cubiertas, por lo que no escapa a la asignación por completo, por lo que para un número pequeño de bytes, en realidad puede ser más lento que asignar una nueva matriz y copiar los bytes en ella.
Sin embargo, no me preocuparía demasiado por la asignación y el GC de pequeños arreglos temporales. El entorno .NET basura recogida es extremadamente eficiente en este tipo de patrón de asignación, sobre todo si las matrices son de corta duración, por lo que a menos que haya perfilado y encontró GC a ser un problema, entonces lo escribiría de la manera más intuitiva y arregle los problemas de rendimiento cuando sepa que los tiene.
Una práctica común que puede ver en .NET framework, y que recomiendo usar aquí, es especificar el desplazamiento y la longitud. Por lo tanto, haga que su función Parse también acepte el desplazamiento en la matriz pasada y la cantidad de elementos que se utilizarán.
Por supuesto, se aplican las mismas reglas que si pasara un puntero como en C ++: la matriz no debería modificarse o, de lo contrario, puede dar como resultado un comportamiento indefinido si no está seguro de cuándo se usarán exactamente los datos. Pero esto no es problema si ya no vas a modificar la matriz.
Pasaría un ArraySegment<byte>
en este caso.
Cambiarías tu método Parse
por esto:
// Changed TCPHeader to TcpHeader to adhere to public naming conventions.
TcpHeader Parse(ArraySegment<byte> buffer)
Y luego cambiarías la llamada a esto:
// Create the array segment.
ArraySegment<byte> seg = new ArraySegment<byte>(packet, 20, 20);
// Call parse.
TcpHeader header = Parse(seg);
El uso de ArraySegment<T>
no copiará la matriz, y hará que los límites se verifiquen en el constructor (para que no especifique límites incorrectos). Luego cambia su método Parse
para que funcione con los límites especificados en el segmento, y debería estar bien.
Incluso puede crear una sobrecarga de conveniencia que acepte la matriz de bytes completa:
// Accepts full array.
TcpHeader Parse(byte[] buffer)
{
// Call the overload.
return Parse(new ArraySegment<byte>(buffer));
}
// Changed TCPHeader to TcpHeader to adhere to public naming conventions.
TcpHeader Parse(ArraySegment<byte> buffer)
Algunas personas que respondieron
tcpbuffer.Skip(20).Take(20);
lo hizo mal. Esta es una excelente solución, pero el código debería verse así:
packet.Skip(20).Take(20);
Debería utilizar los métodos Omitir y Tomar en su paquete principal, y tcpbuffer no debería existir en el código que publicó. Además, no tiene que usar System.Buffer.BlockCopy
.
JaredPar era casi correcto, pero olvidó el método Take
TCPHeader tcp = Parse(packet.Skip(20));
Pero él no se equivocó con tcpbuffer . Su última línea de código publicado debería verse así:
TCPHeader tcp = Parse(packet.Skip(20).Take(20));
Pero si desea utilizar System.Buffer.BlockCopy de todas formas, en lugar de Skip y Take, porque tal vez sea mejor en rendimiento, ya que Steven Robbins respondió: "Pero System.Buffer.BlockCopy / System.Array.Copy es probablemente más eficiente", o su La función Parse no puede tratar con IEnumerable<byte>
, o usted está más acostumbrado a System.Buffer.Block en su pregunta publicada, entonces yo recomendaría simplemente hacer que tcpbuffer no sea una variable local , sino privada o protegida o pública o interna y estática o no campo (en otras palabras, debe definirse y crearse fuera del método donde se ejecuta el código publicado). Por lo tanto, tcpbuffer se creará una sola vez y sus valores (bytes) se establecerán cada vez que pase el código que publicó en la línea System.Buffer.BlockCopy.
De esta manera su código puede verse así:
class Program
{
//Your defined fields, properties, methods, constructors, delegates, events and etc.
private byte[] tcpbuffer = new byte[20];
Your unposted method title(arguments/parameters...)
{
//Your unposted code before your posted code
//byte[] tcpbuffer = new byte[ 20 ]; No need anymore! this line can be removed.
System.Buffer.BlockCopy( packet, 20, this.tcpbuffer, 0, 20 );
TCPHeader tcp = Parse( this.tcpbuffer );
//Your unposted code after your posted code
}
//Your defined fields, properties, methods, constructors, delegates, events and etc.
}
o simplemente solo la parte necesaria:
private byte[] tcpbuffer = new byte[20];
...
{
...
//byte[] tcpbuffer = new byte[ 20 ]; No need anymore! This line can be removed.
System.Buffer.BlockCopy( packet, 20, this.tcpbuffer, 0, 20 );
TCPHeader tcp = Parse( this.tcpbuffer );
...
}
Si lo hiciste:
private byte[] tcpbuffer;
en su lugar, debes agregarle a tu constructor / s la línea:
this.tcpbuffer = new byte[20];
o
tcpbuffer = new byte[20];
Sabes que no tienes que escribir esto. antes de tcpbuffer, es opcional, pero si lo definiste estático, entonces no puedes hacer eso. En su lugar, deberá escribir el nombre de la clase y luego el punto ''.'', O dejarlo (simplemente escriba el nombre del campo y listo).
Así es como lo resolví viniendo de ser programador de CA a ac # programmer. Me gusta usar MemoryStream para convertirlo a una secuencia y luego a BinaryReader para separar el bloque binario de datos. Tuve que agregar las dos funciones auxiliares para convertir de orden de red a little endian. También para construir un byte [] para enviar ver ¿Hay alguna manera de devolverle un objeto al tipo original sin especificar cada caso? que tiene una función que permite convertir de una matriz de objetos a un byte [].
Hashtable parse(byte[] buf, int offset )
{
Hashtable tcpheader = new Hashtable();
if(buf.Length < (20+offset)) return tcpheader;
System.IO.MemoryStream stm = new System.IO.MemoryStream( buf, offset, buf.Length-offset );
System.IO.BinaryReader rdr = new System.IO.BinaryReader( stm );
tcpheader["SourcePort"] = ReadUInt16BigEndian(rdr);
tcpheader["DestPort"] = ReadUInt16BigEndian(rdr);
tcpheader["SeqNum"] = ReadUInt32BigEndian(rdr);
tcpheader["AckNum"] = ReadUInt32BigEndian(rdr);
tcpheader["Offset"] = rdr.ReadByte() >> 4;
tcpheader["Flags"] = rdr.ReadByte() & 0x3f;
tcpheader["Window"] = ReadUInt16BigEndian(rdr);
tcpheader["Checksum"] = ReadUInt16BigEndian(rdr);
tcpheader["UrgentPointer"] = ReadUInt16BigEndian(rdr);
// ignoring tcp options in header might be dangerous
return tcpheader;
}
UInt16 ReadUInt16BigEndian(BinaryReader rdr)
{
UInt16 res = (UInt16)(rdr.ReadByte());
res <<= 8;
res |= rdr.ReadByte();
return(res);
}
UInt32 ReadUInt32BigEndian(BinaryReader rdr)
{
UInt32 res = (UInt32)(rdr.ReadByte());
res <<= 8;
res |= rdr.ReadByte();
res <<= 8;
res |= rdr.ReadByte();
res <<= 8;
res |= rdr.ReadByte();
return(res);
}
¿Por qué no voltear el problema y crear clases que se superponen al búfer para extraer bits?
// member variables
IPHeader ipHeader = new IPHeader();
TCPHeader tcpHeader = new TCPHeader();
// passing in the buffer, an offset and a length allows you
// to move the header over the buffer
ipHeader.SetBuffer( buffer, 0, 20 );
if( ipHeader.Protocol == TCP )
{
tcpHeader.SetBuffer( buffer, ipHeader.ProtocolOffset, 20 );
}