1. Introduction
XMODEM est un protocole de transfert de fichiers asynchrone largement utilisé dans la communication série, divisé en deux protocoles: XModem (en utilisant des blocs de données 128 octets) et 1k-xmodem (en utilisant des blocs de données 1024 octets, c'est-à-dire des blocs de données 1k-byte).
Cet article met en œuvre le protocole XModem de blocs de données de 128 octets, qui adopte la vérification CRC16. Lorsqu'il est appliqué dans le projet, la fin d'envoi et de réception peut modifier l'accord entre les deux parties en fonction des circonstances spécifiques.
Si vous ne savez pas grand-chose sur la communication série, vous pouvez lire le blog que j'ai écrit pour utiliser Java pour implémenter la communication série.
2. Implémentation
Au cours du processus de débogage avec des étudiants intégrés, il a été constaté que le côté envoyé envoyait des données trop rapidement, ce qui a provoqué la réception de ne pas être en mesure de les traiter. Par conséquent, un fil d'enfant a été ouvert dans la méthode d'envoi pour traiter la logique d'envoi de données, ce qui facilite l'ajout de traitement de retard.
Dans la méthode de réception, l'envoi de C signifie la vérification dans CRC.
classe publique xmodem {// Démarrer l'octet final privé soh = 0x01; // terminer l'octet final privé eot = 0x04; // Répondez à l'octet final privé ACK = 0x06; // retransmit l'octet final privé Nak = 0x15; // Fin inconditionnel octet final Privy Can = 0x18; // Transfert des données dans 128 Blocs d'octets Private Final int sector_size = 128; // Erreur maximale (pas de réponse) Nombre de paquets privés final int max_errors = 10; // Stream d'entrée, utilisé pour lire les données de port série privées InputStream InputStream; // Stream de sortie, utilisé pour envoyer des données de port série Private OutputSortStream OutputStream; public xmodem (inputStream inputStream, outputStream OutputStream) {this.inputStream = inputStream; this.OutputStream = outputStream; } / ** * Envoyer des données * * @param filepath * Path du fichier * / public void Send (Final String filepath) {new Thread () {public void run () {try {// nombre de paquets d'erreur int ErrorCount; // Numéro de package BlockNumber BlockNumber = 0x01; // la somme de contrôle de la somme de contrôle; // Nombre d'octets lus dans le tampon int nbytes; // Initialise le tampon de données Byte [] Section = nouveau octet [sector_size]; // Lire le fichier Initialisation DataRailStream inputStream = new DataRainputStream (new FileInputStream (filepath)); while ((nbytes = inputStream.read (secteur))> 0) {// Si le dernier paquet de données est inférieur à 128 octets, remplissez-le de 0xff if (nbytes <sectoral_size) {for (int i = nbytes; i <sector_size; i ++) {sector [i] = (byte) 0xff; }} // Le même paquet de données est envoyé jusqu'à 10 fois ErrorCount = 0; while (errorCount <max_errors) {// paquet de groupe // caractères de contrôle + numéro de paquet + code inverse du numéro de paquet + données + chèque putData (soh); putdata (blindal); putData (~ Blocknumber); Checksum = CRC16.CALC (secteur) & 0x00FFFF; Putchar (secteur, (courte) somme de contrôle); outputStream.flush (); // Obtenez des données de réponse aux données d'octet = getData (); // Si la réponse est reçue, le prochain paquet de données sautera et enverra le paquet suivant de données // aucune réponse n'est reçue, le nombre de paquets incorrectement +1, continuez à renvoyer si (data == ack) {break; } else {++ errorCount; }} // Le nombre de paquets est augmenté par Blocknumber = (byte) ((++ blocknumber)% 256); } // Une fois toutes les données envoyées, le drapeau d'envoi Boolean isack = false; tandis que (! Isack) {putData (eot); isack = getData () == ack; }} catch (exception e) {e.printStackTrace (); }}; }.commencer(); } / ** * recevoir des données * * @param filepath * Path du fichier * @return si la réception est terminée * @throws ioException * exception * / public boolean reçoit (String filepath) lève une exception {// paquet d'erreur introrCount = 0; // Numéro de package BlockNumber BlockNumber = 0x01; // Donte des données de données; // la somme de contrôle de la somme de contrôle; // Initialise le tampon de données Byte [] Section = nouveau octet [sector_size]; // Écrivez dans le fichier pour initialiser DataOutputStream OutputStream = new DataOutputStream (new FileOutputStream (filePath)); // Envoyer le caractère C et la vérification CRC putData ((octet) 0x43); while (true) {if (errorCount> max_errors) {outputStream.close (); retourne false; } // obtenir des données de réponse Data = GetData (); if (data! = eot) {try {// Déterminez si l'identifiant de démarrage reçu if (data! = soh) {errorCount ++; continuer; } // Obtenez les données de numéro de série Packet = GetData (); // Déterminez si le numéro de série du paquet est correct si (data! = BlockNumber) {errorCount ++; continuer; } // Obtenez le code inverse du numéro de série du paquet BYTE _BLOCKNUMBER = (BYTE) ~ GetData (); // Déterminez si le code inverse du numéro de série du paquet est correct si (data! = _Blocknumber) {errorCount ++; continuer; } // Obtenez les données pour (int i = 0; i <sector_size; i ++) {secteur [i] = getData (); } // Obtenez la somme de contrôle de contrôle = (getData () & 0xff) << 8; THECKSUM | = (getData () & 0xff); // Déterminez si la somme de contrôle est correcte INT CRC = CRC16.CALC (secteur); if (CRC! = Checksum) {errorCount ++; continuer; } // Envoyer la réponse putData (ack); // Le numéro de paquet est incrémenté par BlockNumber ++; // Écrivez des données sur la sortie locale Sortie.Write (secteur); // errorCount est réinitialisé à zéro = 0; } catch (exception e) {e.printStackTrace (); } Enfin {// Si une erreur se produit, envoyez l'identifiant de retransmission if (errorCount! = 0) {putData (nak); }}} else {break; }} // Fermez le flux de sortie outputStream.close (); // Envoyez la réponse putdata (ack); Retour Vrai; } / ** * Obtenez des données * * @return data * @throws ioException * exception * / private byte getData () lève ioException {return (byte) inputStream.read (); } / ** * Envoyer des données * * @param data * data * @throws ioException * exception * / private void putData (int data) lève ioException {outputStream.write ((byte) data); } / ** * Envoyer des données * * @param data * data * @param Checksum * Checksum * @throws ioException * exception * / private void putchar (byte [] data, courte chèque) lève ioException {bytebuffer bb = bytebuffer.alllocate (data.length + 2) .Order (byteErder.big_endian); bb.put (données); bb.putshort (somme de contrôle); outputStream.write (bb.array ()); }}L'algorithme de vérification CRC16 utilise la méthode de recherche de table.
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, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 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, 0x5cc5, 0x7c87, 0x4ce4, 0x5cc5, 0x7c87, 0x4ce4, 0x5cc5, 0x7c87, 0x4ce4, 0x5cc5, 0x7c87, 0x4ce4, 0x5cc5 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, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 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, 0x7e36, 0x4e55, 0x5e74, 0x7e36, 0x4e55, 0x5e74, 0x7e36, 0x4e55, 0x5e74, 0x7e36, 0x4e55, 0x5e74, 0x7e36, 0x4e55, 0x5e74, 0x7e36, 0x4e55, 0x5e74, 0x7e36, 0x4e55, 0x5e74, 0x3eb2, 0x0ed1, 0x1ef0}; Public Static char calc (octet [] octets) {char crc = 0x0000; pour (octet b: octets) {crc = (char) ((Crc << 8) ^ crctable [((Crc >> 8) ^ b) & 0x00ff]); } return (char) (CRC); }}3. Utiliser
// serialport est l'objet de port série xmodem xmodem = new xmodem (serialport.getInputStream (), serialport.getOutputStream ()); // filepath est le chemin de fichier // ./bin/xxx.binxModem.send(FilePath);
4. Écrivez à la fin
Téléchargement complet du code
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.