سؤال

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

على سبيل المثال إذا كنت أريد أن أرسل

0x1234abcd 0xabcd3f56 ...

كيف أتأكد من أن الجهاز لا يبدأ القراءة في المكان الخطأ ويحصل على الكلمة الأولى كـ:

0xabcdabcd

هل هناك طريقة ذكية للقيام بذلك؟لقد فكرت في استخدام علامة لبدء الرسالة، ولكن ماذا لو أردت إرسال الرقم الذي اخترته كبيانات؟

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

المحلول

لماذا لا ترسل أ start-of-message بايت تليها أ length-of-data بايت إذا كنت تعرف حجم البيانات سيكون؟

وبدلاً من ذلك، افعل مثل البروتوكولات الثنائية الأخرى وأرسل فقط أحجامًا ثابتة من الحزم ذات الرأس الثابت.لنفترض أنك سترسل 4 بايت فقط، فأنت تعلم أنه سيكون لديك بايت واحد أو أكثر من الرأس قبل محتوى البيانات الفعلي.

يحرر: أعتقد أنك تسيء فهمي.ما أعنيه هو أنه من المفترض أن ينظر العميل دائمًا إلى البايتات كرأس أو بيانات، لا يعتمد على القيمة بل يعتمد على الموضع في الدفق.لنفترض أنك ترسل أربعة بايت من البيانات، فإن البايت الواحد سيكون هو بايت الرأس.

+-+-+-+-+-+
|H|D|D|D|D|
+-+-+-+-+-+

سيكون العميل بعد ذلك عبارة عن آلة حالة أساسية جدًا، على غرار:

int state = READ_HEADER;
int nDataBytesRead = 0;
while (true) {
  byte read = readInput();
  if (state == READ_HEADER) {
    // process the byte as a header byte
    state = READ_DATA;
    nDataBytesRead = 0;
  } else {
    // Process the byte as incoming data
    ++nDataBytesRead;
    if (nDataBytesRead == 4) 
    {
      state = READ_HEADER;
    }
  }
} 

الشيء في هذا الإعداد هو أن ما يحدد ما إذا كانت البايت هي بايت رأس ليس المحتوى الفعلي للبايت، بل الموضع في الدفق.إذا كنت تريد أن يكون لديك عدد متغير من بايتات البيانات، أضف بايتًا آخر إلى الرأس للإشارة إلى عدد بايتات البيانات التي تتبعه.بهذه الطريقة، لن يهم إذا كنت ترسل نفس قيمة الرأس في دفق البيانات نظرًا لأن عميلك لن يفسرها أبدًا على أنها أي شيء سوى البيانات.

نصائح أخرى

netstring

لهذا التطبيق ، ولعل بسيطة نسبيا "netstring"شكل كاف.

على سبيل المثال النص "مرحبا العالم!" بترميز:

12:hello world!,

سلسلة فارغة بترميز ثلاثة أحرف:

0:,

والتي يمكن أن تكون ممثلة على النحو سلسلة بايت

'0' ':' ','

كلمة 0x1234abcd في netstring (باستخدام شبكة ترتيب بايت) ، تليها كلمة 0xabcd3f56 في آخر netstring, بترميز سلسلة بايت

'\n' '4' ':' 0x12 0x34 0xab 0xcd ',' '\n'
'\n' '4' ':' 0xab 0xcd 0x3f 0x56 ',' '\n'

(حرف السطر الجديد ' ' قبل وبعد كل netstring هو اختياري ولكن يسهل الاختبار و التصحيح).

الإطار التزامن

كيف يمكنني التأكد من أن الجهاز لا تبدأ القراءة في المكان الخطأ

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

على سبيل المثال ، إذا كان المسلسل تم توصيل كابل في منتصف الطريق من خلال أول netstring, المتلقي يرى سلسلة البايت:

0xab 0xcd ',' '\n' '\n'  '4' ':' 0xab 0xcd 0x3f 0x56 ',' '\n'

لأن المتلقي هو ذكي بما يكفي لانتظار ':' قبل أن تتوقع التالي بايت صالحة البيانات المتلقي قادرا على تجاهل أول جزئية الرسالة ثم تتلقى الرسالة الثانية بشكل صحيح.

في بعض الحالات, تعرف قبل الوقت ما الرسالة صالحة طول(s) سوف يكون ؛ هذا يجعل الأمر أسهل على المتلقي للكشف عن أنها بدأت القراءة في المكان الخطأ.

إرسال بدء الرسالة علامة البيانات

فكرت باستخدام علامة على بداية الرسالة ، ولكن ماذا لو كنت ترغب في إرسال عدد اخترت البيانات ؟

بعد إرسال netstring رأس الارسال يرسل البيانات الخام كما هو -- حتى لو كان ذلك يحدث أن تبدو وكأنها تبدأ من رسالة العلامة.

في الحالة الطبيعية ، المتلقي بالفعل الإطار المزامنة.على netstring محلل بالفعل قراءة "طول" و ":" رأس ، حتى netstring محلل يضع البيانات الخام بايت مباشرة إلى الموقع الصحيح في المخزن المؤقت -- حتى لو كانت تلك البيانات بايت يحدث أن تبدو وكأنها ":" رأس بايت أو "," تذييل بايت.

شبة الكود

// netstring parser for receiver
// WARNING: untested pseudocode
// 2012-06-23: David Cary releases this pseudocode as public domain.

const int max_message_length = 9;
char buffer[1 + max_message_length]; // do we need room for a trailing NULL ?
long int latest_commanded_speed = 0;
int data_bytes_read = 0;

int bytes_read = 0;
int state = WAITING_FOR_LENGTH;

reset_buffer()
    bytes_read = 0; // reset buffer index to start-of-buffer
    state = WAITING_FOR_LENGTH;

void check_for_incoming_byte()
    if( inWaiting() ) // Has a new byte has come into the UART?
        // If so, then deal with this new byte.
        if( NEW_VALID_MESSAGE == state )
            // oh dear. We had an unhandled valid message,
            // and now another byte has come in.
            reset_buffer();
        char newbyte = read_serial(1); // pull out 1 new byte.
        buffer[ bytes_read++ ] = newbyte; // and store it in the buffer.
        if( max_message_length < bytes_read )
            reset_buffer(); // reset: avoid buffer overflow

        switch state:
            WAITING_FOR_LENGTH:
                // FIXME: currently only handles messages of 4 data bytes
                if( '4' != newbyte )
                    reset_buffer(); // doesn't look like a valid header.
                else
                    // otherwise, it looks good -- move to next state
                    state = WAITING_FOR_COLON;
            WAITING_FOR_COLON:
                if( ':' != newbyte )
                    reset_buffer(); // doesn't look like a valid header.
                else
                    // otherwise, it looks good -- move to next state
                    state = WAITING_FOR_DATA;
                    data_bytes_read = 0;
            WAITING_FOR_DATA:
                // FIXME: currently only handles messages of 4 data bytes
                data_bytes_read++;
                if( 4 >= data_bytes_read )
                    state = WAITING_FOR_COMMA;
            WAITING_FOR_COMMA:
                if( ',' != newbyte )
                    reset_buffer(); // doesn't look like a valid message.
                else
                    // otherwise, it looks good -- move to next state
                    state = NEW_VALID_MESSAGE;

void handle_message()
    // FIXME: currently only handles messages of 4 data bytes
    long int temp = 0;
    temp = (temp << 8) | buffer[2];
    temp = (temp << 8) | buffer[3];
    temp = (temp << 8) | buffer[4];
    temp = (temp << 8) | buffer[5];
    reset_buffer();
    latest_commanded_speed = temp;
    print( "commanded speed has been set to: " & latest_commanded_speed );
}

void loop () # main loop, repeated forever
    # then check to see if a byte has arrived yet
    check_for_incoming_byte();
    if( NEW_VALID_MESSAGE == state ) handle_message();
    # While we're waiting for bytes to come in, do other main loop stuff.
    do_other_main_loop_stuff();

المزيد من النصائح

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

الإطار التزامن (مرة أخرى)

فكرت باستخدام علامة على بداية الرسالة ، ولكن ماذا لو كنت ترغب في إرسال عدد اخترت البيانات ؟

نتحدث في الحالة التي يكون فيها المتلقي بالفعل الإطار المزامنة.الحالة التي يكون فيها المتلقي لا يملك إلا أن الإطار مزامنة جدا فوضوي.

أبسط حل هو إرسال لإرسال سلسلة من مؤذية بايت (ربما أسطر أو الفضاء حرفا) ، طول أقصى حد ممكن صالحة الرسالة ، كما ديباجة قبل كل netstring.بغض النظر عن الدولة المتلقي عند كبل تسلسلي هو في الوتر ، تلك مؤذية بايت في نهاية المطاف دفع المتلقي إلى "WAITING_FOR_LENGTH" الدولة.ثم عندما tranmitter يرسل رأس الحزمة (طول تليها ":"), المتلقي بشكل صحيح وتعترف بأنها حزمة رأس تعافى الإطار المزامنة.

(إنه ليس من الضروري إرسال لإرسال تلك الديباجة قبل كل الحزمة.ولعل الارسال يمكن أن ترسل إلى 1 من أصل 20 الحزم.ثم المتلقي هو ضمان استعادة الإطار متزامنة في 20 الحزم (عادة أقل) بعد المسلسل كبل توصيل).

البروتوكولات الأخرى

تستخدم أنظمة أخرى بسيطة فليتشر-32 الاختباري أو شيء أكثر تعقيدا للكشف عن أنواع كثيرة من الأخطاء التي netstring شكل لا يمكن الكشف عن ( a, ب ), و يمكن مزامنة حتى من دون ديباجة.

العديد من البروتوكولات استخدام خاص "بداية من حزمة" ماركر ، واستخدام مجموعة متنوعة من "الهروب" تقنيات لتجنب إرسال فعلا الحرفي "بداية من حزمة" بايت في البيانات المرسلة ، حتى لو كانت بيانات حقيقية نريد أن نرسل يحدث لها تلك القيمة.( يتفق النفقات العامة بايت حشو, بت حشو, علامات اقتباس خلفية قابل للطباعة وغيرها من أنواع الثنائية إلى ترميز النص, وما إلى ذلك).

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

العديد من تنسيقات أخرى ، بما في ذلك netstring صيغة تسمح أي قيمة البايت إلى أن تنتقل البيانات.لذا الاستقبال يجب أن تكون أكثر ذكاء حول التعامل مع بدء من رأس بايت ذلك قد تكون فعلية تبدأ من رأس ، أو قد تكون البيانات بايت -- ولكن على الأقل أنها لا تضطر إلى التعامل مع "الهروب" أو من المستغرب كبيرة العازلة المطلوبة, في أسوأ الأحوال, إلى عقد "ثابت 64-بايت رسالة البيانات" بعد هروبه.

اختيار نهج واحد ليس أبسط من غيرها ... إنه فقط يدفع التعقيد إلى مكان آخر ، كما كان متوقعا من قبل مائي نظرية.

هلا القشط على مناقشة مختلف سبل التعامل مع بدء من رأس بايت, بما في ذلك اثنين من هذه الطرق ، المسلسل البرمجة Wikibook, وتحرير هذا الكتاب لجعله أفضل ؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top