1. مقدمة
Xmodem هو بروتوكول غير متزامن نقل الملفات يستخدم على نطاق واسع في الاتصال التسلسلي ، مقسومًا إلى بروتوكولات: XModem (باستخدام كتل بيانات 128 بايت) و 1 كيلو فرسوديم (باستخدام كتل بيانات 1024 بايت ، أي 1 كيلو بايت كتل البيانات).
تنفذ هذه المقالة بروتوكول XMODEM لكتل بيانات 128 بايت ، والتي تعتمد التحقق من CRC16. عند تطبيقه في المشروع ، يمكن للمرسل والاستقبال تعديل الاتفاق بين كلا الطرفين وفقًا للظروف المحددة.
إذا كنت لا تعرف الكثير عن الاتصالات التسلسلية ، فيمكنك قراءة المدونة التي كتبتها لاستخدام Java لتنفيذ الاتصالات التسلسلية.
2. التنفيذ
أثناء عملية تصحيح الأخطاء مع الطلاب المضمّن ، وجد أن الجانب المرسل قد أرسل البيانات بسرعة كبيرة ، مما تسبب في عدم قدرة جانب الاستلام على معالجتها. لذلك ، تم فتح مؤشر ترابط الطفل في طريقة إرسال لمعالجة المنطق إرسال البيانات ، مما يسهل إضافة معالجة التأخير.
في طريقة الاستلام ، يعني إرسال C التحقق في اتفاقية حقوق الطفل.
الفئة العامة Xmodem {// ابدأ Byte Byte Soh = 0x01 ؛ // end private final byte eot = 0x04 ؛ // الإجابة الخاصة بالبايت النهائي ACK = 0x06 ؛ // Retransmit Private Final Byte Nak = 0x15 ؛ // CAN Byte Final Byte غير المشروط = 0x18 ؛ // نقل البيانات في 128 BYTE COLLES PRIGARE Final Int Sector_size = 128 ؛ // الحد الأقصى للخطأ (بدون استجابة) عدد الحزم النهائية الخاصة int max_errors = 10 ؛ // دفق الإدخال ، يستخدم لقراءة بيانات المنفذ التسلسلي InputStream InputStream ؛ // دفق الإخراج ، يستخدم لإرسال بيانات المنفذ التسلسلي OutputStream ؛ XModem العامة (InputStream inputStream ، OutputStream OutputStream) {this.inputStream = inputStream ؛ this.outputStream = outputStream ؛ } / *** إرسال البيانات** param filepath* مسار الملف* / public void send (Final String filepath) {new thread () {public void run () {try {// number of error error error ؛ // رقم الحزمة بايت blocknumber = 0x01 ؛ // checksum int checksum ؛ // عدد البايتات قراءة إلى المخزن المؤقت int nbytes ؛ // تهيئة القسم المخزن المؤقت للبيانات [] = بايت جديد [Sector_size] ؛ // قراءة الملف تهيئة datainputStream inputStream = جديد datainputStream (FileInputStream جديد (FilePath)) ؛ بينما ((nbytes = inputStream.Read (Sector))> 0) {// إذا كانت الحزمة الأخيرة من البيانات أقل من 128 بايت ، فاملأها بـ 0xFF if (nbytes <sector_size) {for (int i = nbytes ؛ i <sector_size ؛ i ++) {setctor [i] = (byte) 0xff ؛ }} // يتم إرسال حزمة البيانات نفسها حتى 10 مرات errorcount = 0 ؛ بينما (errorcount <max_errors) {// Group Packet // Control Troftions + Packet Number + رمز عكسي لرقم الحزمة + البيانات + checksum putData (SOH) ؛ portdata (blocknumber) ؛ portdata (~ blocknumber) ؛ checksum = crc16.calc (القطاع) و 0x00ffff ؛ Putchar (القطاع ، (قصير) الاختبارات) ؛ outputStream.flush () ؛ // الحصول على بيانات بايت بيانات الإجابة = getData () ؛ // إذا تم استلام الإجابة ، فستقفز الحزمة التالية من البيانات وإرسال الحزمة التالية من البيانات // لم يتم استلام أي استجابة ، فإن عدد الحزم بشكل غير صحيح +1 ، تستمر في إعادة الالتزام إذا (data == ack) {break ؛ } آخر {++ errorcount ؛ }} // يتم زيادة رقم الحزمة بواسطة blocknumber = (byte) ((++ blocknumber) ٪ 256) ؛ } // بعد إرسال جميع البيانات ، إرسال ind flag boolean isack = false ؛ بينما (! isack) {putdata (eot) ؛ isack = getData () == ack ؛ }} catch (استثناء e) {E.PrintStackTrace () ؛ }} ؛ }.يبدأ()؛ } / *** تلقي البيانات** param filepath* مسار الملف* RETURN ما إذا كان الاستقبال قد اكتمل* throws ioException* استثناء* / استلام BOOLEAN العام (سلسلة FILEPATH) استثناء {// error packet intercount = 0 ؛ // رقم الحزمة بايت blocknumber = 0x01 ؛ // بيانات بايت البيانات ؛ // checksum int checksum ؛ // تهيئة القسم المخزن المؤقت للبيانات [] = بايت جديد [Sector_size] ؛ // اكتب إلى ملف لتهيئة DataOutputStream OutputStream = جديد DataOutputStream (FileOutputStream جديد (FilePath)) ؛ // إرسال حرف C و CRC Putdata ((بايت) 0x43) ؛ بينما (صحيح) {if (errorcount> max_errors) {outputStream.close () ؛ العودة كاذبة } // الحصول على بيانات الرد = getData () ؛ if (data! = eot) {try {// حدد ما إذا كان معرف البدء المستلم if (data! = soh) {errorcount ++ ؛ يكمل؛ } // الحصول على بيانات الرقم التسلسلي الحزمة = getData () ؛ // حدد ما إذا كان الرقم التسلسلي للحزمة صحيحًا إذا (البيانات! = blocknumber) {errorcount ++ ؛ يكمل؛ } // احصل على الكود العكسي للرقم التسلسلي للحزمة _blocknumber = (byte) ~ getData () ؛ // حدد ما إذا كان الكود العكسي للرقم التسلسلي للحزمة صحيحًا إذا (البيانات! = _blocknumber) {errorcount ++ ؛ يكمل؛ } // احصل على البيانات لـ (int i = 0 ؛ i <sector_size ؛ i ++) {sector [i] = getData () ؛ } // الحصول على checksum checksum = (getData () & 0xff) << 8 ؛ checksum | = (getData () & 0xff) ؛ // تحديد ما إذا كان checksum صحيح int crc = crc16.calc (القطاع) ؛ if (crc! = checksum) {errorcount ++ ؛ يكمل؛ } // إرسال الرد putData (ACK) ؛ // يتم زيادة رقم الحزمة بواسطة blocknumber ++ ؛ // كتابة البيانات إلى OutputStream.write المحلي (القطاع) ؛ // errorcount تتم إعادة تعيين إلى الصفر = 0 ؛ } catch (استثناء e) {E.PrintStackTrace () ؛ } أخيرًا {// في حالة حدوث خطأ ، أرسل معرف إعادة الإرسال إذا (errorcount! = 0) {putData (nak) ؛ }}} آخر {break ؛ }} // أغلق دفق الإخراج outputStream.Close () ؛ // إرسال الرد putdata (ACK) ؛ العودة صحيح. } / *** احصل على بيانات** return* throws ioException* استثناء* / خاص بايت getData () يلقي ioException {return (byte) inputStream.Read () ؛ } / *** إرسال البيانات** param data* data* throws ioException* استثناء* / private void putData (int data) يلقي iOexception {outputStream.write ((byte) data) ؛ } / *** إرسال البيانات** param data* data* param checksum* checksum* throws ioException* استثناء* / private void putchar (byte [] data ، checksum القصير) يلقي ioException {bytebuffer bb = bytebuffer.allocate (data.length + 2). bb.put (البيانات) ؛ bb.putshort (checksum) ؛ outputStream.write (bb.array ()) ؛ }}تستخدم خوارزمية التحقق من CRC16 طريقة بحث الجدول.
الفئة العامة CRC16 {Private Static Final Char Crctable [] = {0x0000 ، 0x1021 ، 0x2042 ، 0x3063 ، 0x4084 ، 0x50a5 ، 0x60c6 ، 0x70e7 ، 0x8108 ، 0x9129 ، 0xa14a ، 0xb16b ، 0xf1ef ، 0x1231 ، 0x0210 ، 0x3273 ، 0x2252 ، 0x52b5 ، 0x4294 ، 0x72f7 ، 0x62d6 ، 0x9339 ، 0x8318 ، 0xb37b ، 0xa35a ، 0xd3bd ، 0x2462 ، 0x3443 ، 0x0420 ، 0x1401 ، 0x64e6 ، 0x74c7 ، 0x44a4 ، 0x5485 ، 0xa56a ، 0xb54b ، 0x8528 ، 0x9509 ، 0xe5ee ، 0xf5cf ، 0xc5. 0x2672 ، 0x1611 ، 0x0630 ، 0x76d7 ، 0x66f6 ، 0x5695 ، 0x46b4 ، 0xb75b ، 0xa77a ، 0x9719 ، 0x8738 ، 0xf7df ، 0xe7fe ، 0xd79d ، 0xc7bc 0x6886 ، 0x78a7 ، 0x0840 ، 0x1861 ، 0x2802 ، 0x3823 ، 0xc9cc ، 0xd9ed ، 0xe98e ، 0xf9af ، 0x8948 ، 0x9969 ، 0xa90a ، 0xb92b ، 0x5af5 ، 0x4. 0x6a96 ، 0x1a71 ، 0x0a50 ، 0x3a33 ، 0x2a12 ، 0xdbfd ، 0xcbdc ، 0xfbbf ، 0xeb9e ، 0x9b79 ، 0x8b58 ، 0xbb3b ، 0xab1a ، 0x6ca6 ، 0x2c22 ، 0x3c03 ، 0x0c60 ، 0x1c41 ، 0xedae ، 0xfd8f ، 0xcdec ، 0xddcd ، 0xad2a ، 0xbd0b ، 0x8d68 ، 0x9d49 ، 0x7e97 ، 0x6eb6 ، 0x2e32 ، 0x1e51 ، 0x0e70 ، 0xff9f ، 0xefbe ، 0xdfdd ، 0xcffc ، 0xbf1b ، 0xaf3a ، 0x9f59 ، 0x8f78 ، 0x9188 ، 0x81a9 ، 0xb1ca ، 0xf14e ، 0xe16f ، 0x1080 ، 0x00a1 ، 0x30c2 ، 0x20e3 ، 0x5004 ، 0x4025 ، 0x7046 ، 0x6067 ، 0x83b9 ، 0x9398 ، 0xa3fb ، 0xb3da ، 0xf35e ، 0x02b1 ، 0x1290 ، 0x22f3 ، 0x32d2 ، 0x4235 ، 0x5214 ، 0x6277 ، 0x7256 ، 0xb5ea ، 0xa5cb ، 0x95a8 ، 0x8589 ، 0xf56e ، 0xe54. 0x34e2 ، 0x24c3 ، 0x14a0 ، 0x0481 ، 0x7466 ، 0x6447 ، 0x5424 ، 0x4405 ، 0xa7db ، 0xb7fa ، 0x8799 ، 0x97b8 ، 0xe75f ، 0x26d3 ، 0x36f2 ، 0x0691 ، 0x16b0 ، 0x6657 ، 0x7676 ، 0x4615 ، 0x5634 ، 0xd94c ، 0xc96d ، 0xf90e ، 0xe92f ، 0x99c8 ، 0x89e9 ، 0xB98a ، 0x4865 ، 0x7806 ، 0x6827 ، 0x18c0 ، 0x08e1 ، 0x3882 ، 0x28a3 ، 0xcb7d ، 0xdb5c ، 0xeb3f ، 0xfb1e ، 0x8bf9 ، 0x9bd8 ، 0x6a37 ، 0x7a16 ، 0x0af1 ، 0x1ad0 ، 0x2ab3 ، 0x3a92 ، 0xfd2e ، 0xed0f ، 0xdd6c ، 0xcd4d ، 0xbdaa ، 0xad8b ، 0x9de8 ، 0x8dc9 0x4c45 ، 0x3ca2 ، 0x2c83 ، 0x1ce0 ، 0x0cc1 ، 0xef1f ، 0xff3e ، 0xcf5d ، 0xdf7c ، 0xaf9b ، 0xbfba ، 0x8fd9 ، 0x9ff8 ، 0x6e17 ، 0x2e93 ، 0x3eb2 ، 0x0ed1 ، 0x1ef0} ؛ calc char char public (byte [] bytes) {char crc = 0x0000 ؛ لـ (byte b: bytes) {crc = (char) ((crc << 8) ^ crctable [((crc >> 8) ^ b) & 0x00ff]) ؛ } return (char) (CRC) ؛ }}3. الاستخدام
// SerialPort هو كائن المنفذ التسلسلي Xmodem Xmodem = New Xmodem (serialport.getInputStream () ، serialport.getoutputstream ()) ؛ // filepath هو مسار الملف // ./bin/xx.binxmodem.send(filepath) ؛
4. اكتب في النهاية
تنزيل رمز كامل
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.