1. Introducción
XMODEM es un protocolo de transferencia de archivos asíncrono ampliamente utilizado en la comunicación en serie, dividido en dos protocolos: XModem (usando bloques de datos de 128 bytes) y 1k-xmodem (usando bloques de datos de 1024 bytes, es decir, bloques de datos de 1k byte).
Este artículo implementa el protocolo XModem de bloques de datos de 128 bytes, que adopta la verificación CRC16. Cuando se aplica en el proyecto, el final de envío y recepción puede modificar el acuerdo entre ambas partes de acuerdo con las circunstancias específicas.
Si no sabe mucho sobre la comunicación en serie, puede leer el blog que escribí para usar Java para implementar la comunicación en serie.
2. Implementación
Durante el proceso de depuración con estudiantes integrados, se descubrió que el lado de envío envió datos demasiado rápido, lo que hizo que el lado receptor no pudiera procesarlo. Por lo tanto, se abrió un hilo infantil en el método de envío para procesar la lógica de envío de datos, que facilita la adición de procesamiento de retraso.
En el método de recepción, enviar C significa verificación en CRC.
clase pública xModem {// Iniciar byte final privado soh = 0x01; // finalizar el byte final privado eot = 0x04; // Responda el byte final privado ack = 0x06; // retransmite el byte final privado nak = 0x15; // Byte final privado incondicional puede = 0x18; // transferir datos en 128 bloques de bytes finales ints sector_size = 128; // Error máximo (sin respuesta) Número de paquetes privados finales int max_errors = 10; // flujo de entrada, utilizado para leer datos de puerto serie privado EntryStream InputStream; // flujo de salida, utilizado para enviar datos de puerto serie, salida privada de salida de salida de súplica; public XModem (InputStream InputStream, OutputStream OutputStream) {this.InputStream = inputStream; this.outputStream = outputStream; } / *** Enviar datos** @param filepath* ruta de archivo* / public void send (final cadena filepath) {new Thread () {public void run () {try {// número de paquetes de error int error de error; // número de paquete byte blockNumber = 0x01; // CHECKS SUM INT CHECKSUM; // Número de bytes leídos al búfer int nbytes; // Inicializa el byte de búfer de datos [] sección = nuevo byte [sector_size]; // Lea la inicialización del archivo DataInputStream InputStream = new DataInputStream (new FileInputStream (FilePath)); while ((nbytes = inputStream.read (sector))> 0) {// Si el último paquete de datos es inferior a 128 bytes, llénelo con 0xff if (nbytes <sector_size) {for (int i = nbytes; i <sector_size; i ++) {sector [i] = (byte) 0xff; }} // El mismo paquete de datos se envía hasta 10 veces ErrorCount = 0; while (ErrorCount <max_errors) {// Paquete de grupo // caracteres de control + número de paquete + código inverso del número de paquete + datos + checksum putData (SOH); putdata (bloqueo); putdata (~ blocknumber); checksum = CRC16.calc (sector) y 0x00ffff; putchar (sector, (corta) de verificación); outputStream.Flush (); // Obtener datos de respuestas byte data = getData (); // Si se recibe la respuesta, el siguiente paquete de datos saltará y enviará el siguiente paquete de datos // no se recibe respuesta, el número de paquetes incorrectamente +1, continúa reenviando si (data == ack) {break; } else {++ ErrorCount; }} // El número de paquete aumenta en blocknumber = (byte) ((++ blocknumber) % 256); } // Después de que se envían todos los datos, la bandera de envío final boolean isack = false; while (! isack) {putData (eot); isack = getData () == ack; }} catch (Exception e) {E.PrintStackTrace (); }}; }.comenzar(); } / *** Recibe datos** @param FilePath* Ruta del archivo* @return si la recepción está completa* @throws ioException* Exception* / public boolean Recibe (String filePath) lanza la excepción { / del paquete de error int ErrorCount = 0; // número de paquete byte blockNumber = 0x01; // datos de bytes de datos; // CHECKS SUM INT CHECKSUM; // Inicializa el byte de búfer de datos [] sección = nuevo byte [sector_size]; // Escribir en el archivo para inicializar DataOutputStream OutputStream = new DataOutputStream (nuevo FileOutputStream (FilePath)); // Enviar la verificación C y CRC PutData ((byte) 0x43); while (true) {if (errorCount> max_errors) {outputStream.Close (); devolver falso; } // Obtenga datos de datos de respuesta = getData (); if (data! = eot) {try {// determinar si el identificador de inicio recibido if (data! = soh) {errorCount ++; continuar; } // Obtener el número de serie de paquetes datos = getData (); // Determinar si el número de serie del paquete es correcto si (data! = Blocknumber) {errorCount ++; continuar; } // Obtenga el código inverso del número de serie del paquete byte _blockNumber = (byte) ~ getData (); // Determinar si el código inverso del número de serie del paquete es correcto si (data! = _Blocknumber) {ErrorCount ++; continuar; } // Obtenga los datos para (int i = 0; i <sector_size; i ++) {sector [i] = getData (); } // Obtenga la suma de chechsum = (getData () y 0xff) << 8; checksum | = (getData () y 0xff); // determinar si la suma de verificación es correcta int crc = CRC16.calc (sector); if (crc! = checksum) {errorCount ++; continuar; } // Envía la respuesta PutData (ACK); // El número de paquete se incrementa por blocknumber ++; // Escribir datos al OutputStream.Write (sector) local; // ErrorCount se restablece a cero = 0; } catch (Exception e) {E.PrintStackTrace (); } Finalmente {// Si ocurre un error, envíe el identificador de retransmisión if (errorCount! = 0) {putData (nak); }}} else {break; }} // Cierre la transmisión de salida OutputStream.Close (); // Envía la respuesta PutData (ACK); devolver verdadero; } / *** Obtener datos** @return data* @throws ioexception* excepción* / private byte getData () lanza ioexception {return (byte) inputStream.read (); } / *** Enviar datos** @param data* data* @throws ioException* excepción* / private void putData (int data) lanza ioexception {outputStream.write ((byte) data); } / *** Enviar datos** @param data* data* @param checksum* checksum* @throws ioexception* excepción* / private void putchar (byte [] data, breve checksum) lanza ioexception {bytebuffer bb = bytebuffer.allocate (data.length + 2) .order (byteorder.big_Enciense); bb.put (datos); bb.putShort (suma de verificación); outputStream.Write (bb.array ()); }}El algoritmo de verificación CRC16 utiliza el método de búsqueda de tabla.
public class CRC16 { private static final char crctable[] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3365 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x87338, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5. 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4, 0x5c5c5, 0x5ce 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd733c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xeAt 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbbb9a, 0x4a75, 0x5a54, 0x5a54, 0x5a54. 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3Ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0}; public static char cal (byte [] bytes) {char crc = 0x0000; para (byte b: bytes) {crc = (char) ((crc << 8) ^ crctable [((crc >> 8) ^ b) y 0x00ff]); } return (char) (CRC); }}3. Use
// Serialport es el objeto de puerto serie xModem xModem = new XModem (Serialport.getInputStream (), serialport.getOutputStream ()); // filepath es la ruta del archivo // ./bin/xxx.binxmodem.send(filePath);
4. Escribe al final
Descargar el código completo
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.