Question

J'ai un tableau d'octets qui représente un paquet TCP / IP complet. Pour plus de précision, le tableau d'octets est ordonné comme suit:

(en-tête IP - 20 octets) (en-tête TCP - 20 octets) (charge utile - X octets)

J'ai une fonction Parse qui accepte un tableau d'octets et renvoie un objet TCPHeader. Cela ressemble à ceci:

TCPHeader Parse( byte[] buffer );

Étant donné le tableau d'octets d'origine, voici comment j'appelle cette fonction pour le moment.

byte[] tcpbuffer = new byte[ 20 ];
System.Buffer.BlockCopy( packet, 20, tcpbuffer, 0, 20 );
TCPHeader tcp = Parse( tcpbuffer );

Existe-t-il un moyen pratique de transmettre le tableau d'octets TCP, c'est-à-dire les octets 20 à 39 du paquet TCP / IP complet, à la fonction <=> sans l'extraire d'abord vers un nouveau tableau d'octets?

En C ++, je pourrais effectuer les opérations suivantes:

TCPHeader tcp = Parse( &packet[ 20 ] );

Y a-t-il quelque chose de similaire en C #? Je souhaite éviter la création et le ramassage ultérieur du tableau d'octets temporaire, si possible.

Était-ce utile?

La solution

Une pratique courante que vous pouvez voir dans le framework .NET, et que je recommande d'utiliser ici, spécifie le décalage et la longueur. Donc, assurez-vous que votre fonction Parse accepte également le décalage dans le tableau passé et le nombre d’éléments à utiliser.

Bien sûr, les mêmes règles s'appliquent que si vous deviez passer un pointeur comme en C ++ - le tableau ne doit pas être modifié, sinon un comportement non défini peut survenir si vous ne savez pas exactement quand les données seront utilisées. Mais ce n’est pas un problème si vous ne modifiez plus le tableau.

Autres conseils

Je passerais dans ce cas un ArraySegment<byte> .

Vous souhaitez modifier votre Parse méthode par ceci:

// Changed TCPHeader to TcpHeader to adhere to public naming conventions.
TcpHeader Parse(ArraySegment<byte> buffer)

Ensuite, vous modifiez l'appel comme suit:

// Create the array segment.
ArraySegment<byte> seg = new ArraySegment<byte>(packet, 20, 20);

// Call parse.
TcpHeader header = Parse(seg);

L'utilisation de ArraySegment<T> ne copie pas le tableau et effectue la vérification des limites pour vous dans le constructeur (afin que vous ne spécifiiez pas de limites incorrectes). Ensuite, vous modifiez votre <=> méthode pour qu'elle fonctionne avec les limites spécifiées dans le segment, et tout devrait bien se passer.

Vous pouvez même créer une surcharge commode qui acceptera le tableau d'octets complet:

// 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)

Si un IEnumerable<byte> est une entrée acceptable plutôt que byte[] et que vous utilisez C # 3.0, vous pouvez écrire:

tcpbuffer.Skip(20).Take(20);

Notez que cela alloue toujours les occurrences de l'énumérateur sous les couvertures. Par conséquent, vous n'échapperez pas complètement à l'allocation. Cela peut donc être plus lent pour un petit nombre d'octets que d'allouer un nouveau tableau et d'y copier les octets.

Pour être honnête, je ne m'inquiéterais pas trop de l'allocation et de la GC des petits tableaux temporaires. L’environnement .NET garbage collect est extrêmement efficace avec ce type de modèle d’allocation, en particulier si les tableaux sont de courte durée. Par conséquent, à moins que vous ne le définissiez et que GC ne soit un problème, vous l’écririez de la manière la plus intuitive possible. corrigez les problèmes de performances lorsque vous savez que vous les avez.

Si vous avez vraiment besoin de ce type de contrôle, vous devez examiner la unsafe fonctionnalité de C #. Il vous permet d’avoir un pointeur et de l’épingler pour que GC ne le déplace pas:

fixed(byte* b = &bytes[20]) {
}

Toutefois, cette pratique n'est pas recommandée pour travailler avec du code uniquement géré s'il n'y a pas de problème de performances. Vous pouvez transmettre le décalage et la longueur comme dans Stream la classe.

Si vous pouvez modifier la méthode parse (), modifiez-la pour accepter le décalage où le traitement doit commencer. TCPHeader Parse (byte [] buffer, int offset);

Vous pouvez utiliser LINQ pour faire quelque chose comme:

tcpbuffer.Skip(20).Take(20);

Mais System.Buffer.BlockCopy / System.Array.Copy sont probablement plus efficaces.

C’est ainsi que j’ai résolu le problème en passant de programmeur c à programmeur c #. J'aime utiliser MemoryStream pour le convertir en flux, puis BinaryReader pour séparer le bloc binaire de données. Il a fallu ajouter les deux fonctions d’aide pour convertir l’ordre du réseau en little endian. Aussi pour construire un octet [] pour envoyer voir Y a-t-il un moyen de redonner à un objet son type d'origine sans spécifier chaque cas? qui possède une fonction permettant la conversion à partir d'un tableau d'objets en octet [].

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

Je ne pense pas que vous puissiez faire quelque chose comme ça en C #. Vous pouvez soit faire en sorte que la fonction Parse () utilise un décalage, soit créer des tableaux de 3 octets pour commencer; un pour l'en-tête IP, un pour l'en-tête TCP et un pour la charge utile.

Il n’est pas possible d’utiliser un code vérifiable pour ce faire. Si votre méthode Parse peut traiter avec un IEnumerable & Lt; byte & Gt; alors vous pouvez utiliser une expression LINQ

TCPHeader tcp = Parse(packet.Skip(20));

Certaines personnes qui ont répondu

tcpbuffer.Skip(20).Take(20);

s'est trompé. C’est une excellente solution, mais le code doit ressembler à ceci:

packet.Skip(20).Take(20);

Vous devez utiliser les méthodes Ignorer et Prendre sur votre paquet principal, et tcpbuffer ne doit pas exister dans le code que vous avez publié. Aussi, vous n'avez pas à utiliser alors System.Buffer.BlockCopy.

JaredPar était presque correct, mais il a oublié la méthode Take

TCPHeader tcp = Parse(packet.Skip(20));

Mais il ne s'est pas trompé avec tcpbuffer . Votre dernière ligne de votre code posté devrait ressembler à ceci:

TCPHeader tcp = Parse(packet.Skip(20).Take(20));

Mais si vous voulez utiliser System.Buffer.BlockCopy au lieu de cela Skip and Take, car peut-être que ses performances sont meilleures, car Steven Robbins a répondu: & "; Mais System.Buffer.BlockCopy / System.Array.Copy sont probablement plus efficace " ;, ou votre fonction d'analyse ne peut pas traiter avec IEnumerable<byte>, ou vous êtes plus habitué à System.Buffer.Block dans votre question publiée, alors je vous recommande de il suffit simplement de faire en sorte que tcpbuffer soit non local , mais privé ou protégé ou public ou interne et statique ou non champ (en d'autres termes, il convient de définir et de créer la méthode outside où le code envoyé est exécuté ). Ainsi, tcpbuffer ne sera créé qu'une fois une fois et ses valeurs (octets) seront définies chaque fois que vous passerez le code que vous avez publié sur la ligne System.Buffer.BlockCopy.

Ainsi, votre code peut ressembler à:

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.
}

ou simplement la partie nécessaire:

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 vous l'avez fait:

private byte[] tcpbuffer;

à la place, vous devez ajouter à votre constructeur la ligne suivante:

this.tcpbuffer = new byte[20];

ou

tcpbuffer = new byte[20];

Vous savez que vous n'avez pas besoin de saisir this. avant tcpbuffer, il est facultatif, mais si vous le définissez comme statique, vous ne pouvez pas faire cela. Au lieu de cela, vous devrez taper le nom de la classe, puis le point '.', Ou le laisser (tapez simplement le nom du champ et le tour est joué).

Pourquoi ne pas inverser le problème et créer des classes superposant le tampon pour extraire les 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 );
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top