سؤال

لقد واجهت ما أعتقد هو مشكلة مع BinaryReader.ReadChars الأسلوب ().عندما لف BinaryReader حول الخام مأخذ NetworkStream أحيانا أحصل على تيار الفساد فيها تيار قراءتها يحصل متزامنة.تيار في السؤال يحتوي على رسائل في ثنائي التسلسل البروتوكول.

لقد تتبعت هذا إلى ما يلي

  • هذا يحدث فقط عند قراءة سلسلة unicode (المشفرة باستخدام الترميز.BigEndian)
  • هذا يحدث فقط عندما تكون السلسلة في السؤال هو الانقسام عبر اثنين من الحزم tcp (تأكيد استخدام يريشارك)

أعتقد أن ما يحدث هو التالي (في سياق المثال أدناه)

  • BinaryReader.ReadChars() يسمى طالبا لقراءة 3 أحرف (سلسلة أطوال المشفرة قبل السلسلة نفسها)
  • أول حلقة داخليا يطلب قراءة من 6 بايت (3 المتبقية الشخصيات * 2 بايت/شار) خارج الشبكة تيار
  • تيار الشبكة فقط 3 وحدات البايت المتوفرة
  • 3 بايت قراءة في المحلية العازلة
  • العازلة سلمت فك
  • فك يترجم 1 شار ويبقي الأخرى بايت في نفسه الداخلية العازلة
  • الحلقة الثانية داخليا يطلب قراءة من 4 بايت!(2 الأحرف المتبقية * 2 بايت/شار)
  • تيار الشبكة لديه كل 4 بايت المتاحة
  • 4 بايت قراءة في المحلية العازلة
  • العازلة سلمت فك
  • فك يترجم 2 شار ، ويحافظ على ما تبقى 4 بايت داخليا
  • سلسلة فك كاملة
  • التسلسل رمز محاولات unmarshal البند التالي وينعق بسبب تيار الفساد.

    char[] buffer = new char[3];
    int charIndex = 0;
    
    Decoder decoder = Encoding.BigEndianUnicode.GetDecoder();
    
    // pretend 3 of the 6 bytes arrives in one packet
    byte[] b1 = new byte[] { 0, 83, 0 };
    int charsRead = decoder.GetChars(b1, 0, 3, buffer, charIndex);
    charIndex += charsRead;
    
    // pretend the remaining 3 bytes plus a final byte, for something unrelated,
    // arrive next
    byte[] b2 = new byte[] { 71, 0, 114, 3 };
    charsRead = decoder.GetChars(b2, 0, 4, buffer, charIndex);
    charIndex += charsRead;
    

أعتقد أن الجذر هو خلل في .صافي التعليمات البرمجية التي تستخدم charsRemaining * بايت/شار كل حلقة لحساب المتبقي من وحدات البايت المطلوبة.بسبب بايت إضافية مخبأة في فك هذا الحساب يمكن أن يكون أي مما تسبب إضافية بايت إلى أن تستهلك من تيار الإدخال.

هنا .NET framework الرمز في السؤال

    while (charsRemaining>0) { 
        // We really want to know what the minimum number of bytes per char 
        // is for our encoding.  Otherwise for UnicodeEncoding we'd have to
        // do ~1+log(n) reads to read n characters. 
        numBytes = charsRemaining;
        if (m_2BytesPerChar)
            numBytes <<= 1;

        numBytes = m_stream.Read(m_charBytes, 0, numBytes);
        if (numBytes==0) { 
            return (count - charsRemaining); 
        } 
        charsRead = m_decoder.GetChars(m_charBytes, 0, numBytes, buffer, index);

        charsRemaining -= charsRead;
        index+=charsRead;
    }

لست متأكدا إذا كان هذا هو خطأ أو إساءة استخدام API.لعمل جولة في هذه المسألة أنا فقط حساب وحدات البايت المطلوبة نفسي قراءتها ثم تشغيل byte[] من خلال الترميز.GetString().ولكن هذا لن يعمل شيء مثل UTF-8.

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

تحرير:نشر للتواصل ربط تتبع البند

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

المحلول

لقد استنسخت المشكلة التي ذكرتها مع BinaryReader.ReadChars.

على الرغم من أن المطور يحتاج دائما إلى حساب lookahead عند إنشاء الأشياء مثل الجداول و فك التشفير ، هذا يبدو إلى حد ما خلل كبير في BinaryReader لأن هذا الفصل هو المقصود من القراءة هياكل البيانات تتكون من أنواع مختلفة من البيانات.في هذه الحالة أوافق على أن ReadChars ينبغي أن يكون أكثر تحفظا في ما قرأت لتجنب فقدان التي بايت.

لا يوجد شيء خاطئ مع الحل من استخدام Decoder مباشرة بعد كل هذا ما ReadChars هل وراء الكواليس.

Unicode هي حالة بسيطة.إذا كنت تفكر التعسفي ترميز هناك حقا ليس الغرض العام طريقة لضمان أن العدد الصحيح من وحدات البايت التي يتم استهلاكها عند تمرير في عدد أحرف بدلا من البايت (التفكير متفاوتة الطول الشخصيات الحالات التي تنطوي تالف المدخلات).لهذا السبب ، وتجنب BinaryReader.ReadChars في صالح من قراءة عدد معين من وحدات البايت يوفر أكثر قوة ، الحل العام.

وأود أن أقترح عليك أن تجلب هذا أن مايكروسوفت عبر الاهتمام http://connect.microsoft.com/visualstudio.

نصائح أخرى

مثير للاهتمام ؛ هل يمكن أن تقرير هذا على "اتصال".كما وقف الفجوة ، هل يمكن أيضا محاولة التفاف مع BufferredStream, لكن أتوقع هذا هو ورق على الكراك (فإنه قد لا يزال يحدث ، ولكن في كثير من الأحيان أقل).

أما النهج الآخر ، بالطبع ، إلى ما قبل العازلة كامل الرسالة (ولكن ليس تيار بأكمله);ثم قراءة من شيء من هذا القبيل MemoryStream - على افتراض الخاص بك بروتوكول الشبكة وقد المنطقي (مثالي طول مسبوقة ، ليست كبيرة جدا) رسائل.ثم عندما فك جميع البيانات المتاحة.

يذكرني هذا واحد من بلدي الأسئلة (القراءة من HttpResponseStream فشل) حيث لدي مشكلة عند القراءة من استجابة HTTP تيار StreamReader يظن أنه قد بلغ نهاية تيار قبل الأوان إذا موزعي أن القنبلة بشكل غير متوقع.

مثل مارك اقترح مشكلتك حاولت أولا قبل التخزين المؤقت في MemoryStream الذي يعمل بشكل جيد ولكن يعني أنك قد تضطر إلى الانتظار لفترة طويلة إذا كان لديك ملف كبير إلى قراءة (خاصة من الشبكة/الإنترنت) قبل أن يمكنك أن تفعل أي شيء مفيد مع ذلك.لقد استقر في نهاية المطاف على خلق بلدي امتداد TextReader الذي يتجاوز قراءة أساليب ويحدد لهم باستخدام ReadBlock الطريقة (التي لا تسد قراءة أيفإنه ينتظر حتى أنه يمكن الحصول على بالضبط عدد من الشخصيات تسأل عنه)

مشكلتك ربما يرجع مثل الألغام إلى حقيقة أن قراءة أساليب ليست مقارعة إلى عودة عدد من الشخصيات تسأل عنه ، على سبيل المثال إذا كنت إلقاء نظرة على الوثائق الخاصة BinaryReader.Read (http://msdn.microsoft.com/en-us/library/ms143295.aspx) طريقة سترى أن الدول:

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

منذ BinaryReader لا ReadBlock أساليب مثل TextReader كل ما يمكنك القيام به هو اتخاذ نهج رصد وضع نفسك أو مارك قبل التخزين المؤقت.

أنا أعمل مع Unity3D/أحادية atm ReadChars-الأسلوب قد تحتوي على المزيد من الأخطاء.أنا قدمت سلسلة من هذا القبيل:

mat.name = new string(binaryReader.ReadChars(64));

mat.name حتى تضمن السلسلة الصحيحة لكن يمكن إضافة سلاسل قبل ذلك.كل شيء بعد سلسلة فقط disappered.حتى مع سلسلة.تنسيق.حل بلدي حتى الآن لا يستخدم ReadChars-الأسلوب ، ولكن قراءة البيانات من صفيف بايت وتحويله إلى سلسلة:

byte[] str = binaryReader.ReadBytes(64);
int lengthOfStr = Array.IndexOf(str, (byte)0); // e.g. 4 for "clip\0"
mat.name = System.Text.ASCIIEncoding.Default.GetString(str, 0, lengthOfStr);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top