سؤال

لدي مصفوفة بايت تمثل حزمة TCP/IP كاملة.للتوضيح، يتم ترتيب مصفوفة البايت على النحو التالي:

(رأس IP - 20 بايت) (رأس TCP - 20 بايت) (الحمولة - X بايت)

انا املك Parse دالة تقبل مصفوفة بايت وترجع a TCPHeader هدف.تبدو هكذا:

TCPHeader Parse( byte[] buffer );

بالنظر إلى مصفوفة البايت الأصلية، فإليك الطريقة التي سأستدعي بها هذه الوظيفة الآن.

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

هل توجد طريقة مناسبة لتمرير مصفوفة بايت TCP، أي البايتات 20-39 من حزمة TCP/IP الكاملة، إلى Parse هل تعمل دون استخراجها إلى صفيف بايت جديد أولاً؟

في C++، يمكنني القيام بما يلي:

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

هل هناك أي شيء مماثل في C#؟أريد تجنب الإنشاء وجمع البيانات المهملة اللاحقة لمصفوفة البايت المؤقتة إن أمكن.

هل كانت مفيدة؟

المحلول

وهناك ممارسة شائعة يمكنك ان ترى في .NET Framework، والتي أوصي باستخدام هنا، هو تحديد الإزاحة والطول. وهكذا جعل وظيفة تحليل الخاص بقبول أيضا الإزاحة في مجموعة مرت، وعدد من العناصر للاستخدام.

وبطبيعة الحال، وتنطبق نفس القواعد كما لو كنت لتمرير مؤشر كما في C ++ - لا ينبغي أن يتم تعديل مجموعة وإلا فإنه قد يؤدي إلى سلوك غير معرف إذا لم تكن متأكدا متى سيتم استخدام البيانات بالضبط. ولكن هذه ليست مشكلة إذا كنت لم تعد على وشك أن تعديل مجموعة.

نصائح أخرى

وأود أن تمرير ArraySegment<byte> في هذه القضية.

هل تغيير طريقة Parse لهذا:

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

وبعد ذلك تقوم بتغيير الدعوة إلى هذا:

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

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

وباستخدام ArraySegment<T> لن نسخ مجموعة، وانها لن تفعل حدود فحص لك في منشئ (بحيث لم تقم بتحديد حدود غير صحيحة). ثم قمت بتغيير طريقة Parse للعمل مع حدود محددة في هذا الجزء، ويجب أن تكون على ما يرام.

ويمكنك حتى إنشاء الزائد الراحة التي ستقبل صفيف بايت كاملة:

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

وإذا كان IEnumerable<byte> غير مقبولة كمدخل بدلا من byte[]، وكنت تستخدم C # 3.0، ثم هل يمكن أن يكتب:

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

لاحظ أن هذا لا يزال يخصص الحالات العداد تحت الأغطية، لذلك لم يكن الهروب تخصيص تماما، وذلك لعدد صغير من وحدات البايت قد يكون في الواقع أبطأ من تخصيص مجموعة جديدة ونسخ بايت في ذلك.

وأود أن لا تقلق كثيرا حول تخصيص وGC صفائف مؤقتة صغيرة لنكون صادقين مع ذلك. بيئة .NET القمامة التي تم جمعها هي فعالة للغاية في هذا النوع من نمط تخصيص، وخاصة إذا كان صفائف قصيرة الأجل، لذلك إلا إذا كنت قد لمحة أنها وجدت GC وجود مشكلة ثم كنت اكتبها في الطريقة الأكثر بديهية و إصلاح عن مشكلات في الأداء عندما كنت أعلم أنك لهم.

إذا كنت حقا بحاجة هذا النوع من الرقابة، وكنت فلدي نظرة على ميزة unsafe من C #. انها تسمح لك الحصول على مؤشر ويعلقون عليه بحيث GC لا يتحرك فيه:

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

ولكن هذه الممارسة لا اقترح للعمل مع رمز فقط تمكن إذا كانت هناك أية مشكلات في الأداء. هل يمكن تمرير وطول تعويض كما هو الحال في الطبقة Stream.

إذا يمكنك تغيير طريقة تحليل ()، تغييره إلى قبول تعويض حيث يجب أن تبدأ المعالجة. TCPHeader تحليل ([] بايت العازلة، كثافة العمليات الإزاحة)؛

هل يمكن استخدام LINQ أن تفعل شيئا مثل:

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

ولكن System.Buffer.BlockCopy / System.Array.Copy ربما تكون أكثر كفاءة.

وهذه هي الطريقة I حلها القادمة من كونه مبرمج ج إلى د # مبرمج. أود أن استخدام MemoryStream لتحويله إلى تيار ومن ثم BinaryReader لتحطيم كتلة ثنائي من البيانات. كان لإضافة وظائف المساعد اثنين لتحويل من أجل شبكة لendian قليلا. أيضا لبناء بايت [] لارسال رؤية هل هناك طريقة يلقي كائن إلى ذلك النوع الأصلي دون specifing كل حالة؟ التي لديه وظيفة تسمح لتحويل من مجموعة من الكائنات إلى [] بايت.

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

وأنا لا أعتقد أنك تستطيع أن تفعل شيئا من هذا القبيل في C #. هل يمكن أن تجعل أي وظيفة تحليل () استخدام إزاحة، أو إنشاء 3 صفائف بايت لتبدأ. واحد للرأس IP، واحدة للرأس TCP واحدة لالحمولة.

وليس هناك طريقة باستخدام رمز التحقق منه للقيام بذلك. إذا الأسلوب في التوزيع الخاص بك يمكن أن تتعامل مع وجود IEnumerable <بايت> ثم يمكنك استخدام تعبير LINQ

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

بعض الناس الذين أجابوا

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

فعلت ذلك خطأ.هذا هو ممتاز الحل، ولكن يجب أن يبدو الرمز كما يلي:

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

يجب عليك استخدام أساليب التخطي والأخذ في الملف الرئيسي الخاص بك رزمة, ، و com.tcpbuffer لا ينبغي أن تكون موجودة في الكود الذي نشرته.كما أنه ليس عليك استخدامه بعد ذلك System.Buffer.BlockCopy.

جاريد بار كان على حق تقريبًا، لكنه نسي طريقة الأخذ

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

لكنه لم يخطئ com.tcpbuffer.يجب أن يبدو السطر الأخير من الكود المنشور كما يلي:

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

ولكن إذا كنت تريد استخدام System.Buffer.BlockCopy على أي حال بدلاً من Skip and Take، لأنه ربما يكون أفضل في الأداء كما أجاب ستيفن روبنز:"لكن System.Buffer.BlockCopy / System.Array.Copy ربما تكون أكثر كفاءة"، أو وظيفة التحليل الخاصة بك لا تستطيع التعامل مع IEnumerable<byte>, ، أو أنك معتاد على System.Buffer.Block في سؤالك المنشور، فأنا أوصي بذلك ببساطة فقط جعل tcpbuffer ليست محلية متغير ولكن خاص أو محمي أو عام أو داخلي و ثابتة أم لا مجال (وبعبارة أخرى ينبغي تعريفه وإنشاؤه الخارج الطريقة التي يتم بها تنفيذ التعليمات البرمجية المنشورة).وبالتالي سيتم إنشاء tcpbuffer فقط مرة واحدة, ، وسيتم تعيين قيمه (البايتات) في كل مرة تقوم فيها بتمرير الكود الذي نشرته على سطر System.Buffer.BlockCopy.

بهذه الطريقة يمكن أن يبدو الكود الخاص بك كما يلي:

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

أو ببساطة الجزء الضروري فقط:

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

اذا فعلت:

private byte[] tcpbuffer;

بدلاً من ذلك، يجب عليك إضافة السطر على المنشئ/المنشئات الخاصة بك:

this.tcpbuffer = new byte[20];

أو

tcpbuffer = new byte[20];

أنت تعلم أنه ليس عليك الكتابة هذا. قبل tcpbuffer، كان اختياريًا، ولكن إذا قمت بتعريفه بشكل ثابت، فهذا يعني أنك لا تستطيع إفعل ذلك.بدلاً من ذلك، سيتعين عليك كتابة اسم الفصل ثم النقطة "."، أو اتركها (فقط اكتب اسم الحقل وهذا كل شيء).

لماذا لا نقف المشكلة وإنشاء الفئات التي تراكب المخزن المؤقت لسحب بت بها؟

// 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 );
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top