一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術(shù)|正則表達式|

服務(wù)器之家 - 編程語言 - JAVA教程 - 使用java實現(xiàn)Xmodem協(xié)議

使用java實現(xiàn)Xmodem協(xié)議

2020-07-15 12:08容華謝后 JAVA教程

這篇文章主要介紹了使用java實現(xiàn)Xmodem協(xié)議的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下

1.介紹

Xmodem是一種在串口通信中廣泛使用的異步文件傳輸協(xié)議,分為Xmodem(使用128字節(jié)的數(shù)據(jù)塊)和1k-Xmodem(使用1024字節(jié)即1k字節(jié)的數(shù)據(jù)塊)協(xié)議兩種。
本文實現(xiàn)的是128字節(jié)數(shù)據(jù)塊的Xmodem協(xié)議,采用CRC16校驗,在項目中應(yīng)用時,發(fā)送端和接收端可根據(jù)具體情況修改雙方的協(xié)議。
如果你對串口通信還不太了解,可以看下我寫的這篇博客使用Java實現(xiàn)串口通信

2.實現(xiàn)

在和嵌入式同學(xué)調(diào)試的過程中,發(fā)現(xiàn)發(fā)送端發(fā)送數(shù)據(jù)過快,導(dǎo)致接收端處理不過來,所以在send方法中開啟了一個子線程來處理數(shù)據(jù)發(fā)送邏輯,方便加入延時處理。
接收方法中,發(fā)送C是表示以CRC方式校驗。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
public class Xmodem {
 
 // 開始
 private final byte SOH = 0x01;
 // 結(jié)束
 private final byte EOT = 0x04;
 // 應(yīng)答
 private final byte ACK = 0x06;
 // 重傳
 private final byte NAK = 0x15;
 // 無條件結(jié)束
 private final byte CAN = 0x18;
 
 // 以128字節(jié)塊的形式傳輸數(shù)據(jù)
 private final int SECTOR_SIZE = 128;
 // 最大錯誤(無應(yīng)答)包數(shù)
 private final int MAX_ERRORS = 10;
 
 // 輸入流,用于讀取串口數(shù)據(jù)
 private InputStream inputStream;
 // 輸出流,用于發(fā)送串口數(shù)據(jù)
 private OutputStream outputStream;
 
 public Xmodem(InputStream inputStream, OutputStream outputStream) {
 this.inputStream = inputStream;
 this.outputStream = outputStream;
 }
 
 /**
 * 發(fā)送數(shù)據(jù)
 *
 * @param filePath
 *  文件路徑
 */
 public void send(final String filePath) {
 new Thread() {
  public void run() {
  try {
   // 錯誤包數(shù)
   int errorCount;
   // 包序號
   byte blockNumber = 0x01;
   // 校驗和
   int checkSum;
   // 讀取到緩沖區(qū)的字節(jié)數(shù)量
   int nbytes;
   // 初始化數(shù)據(jù)緩沖區(qū)
   byte[] sector = new byte[SECTOR_SIZE];
   // 讀取文件初始化
   DataInputStream inputStream = new DataInputStream(
    new FileInputStream(filePath));
 
   while ((nbytes = inputStream.read(sector)) > 0) {
   // 如果最后一包數(shù)據(jù)小于128個字節(jié),以0xff補齊
   if (nbytes < SECTOR_SIZE) {
    for (int i = nbytes; i < SECTOR_SIZE; i++) {
    sector[i] = (byte) 0xff;
    }
   }
 
   // 同一包數(shù)據(jù)最多發(fā)送10次
   errorCount = 0;
   while (errorCount < MAX_ERRORS) {
    // 組包
    // 控制字符 + 包序號 + 包序號的反碼 + 數(shù)據(jù) + 校驗和
    putData(SOH);
    putData(blockNumber);
    putData(~blockNumber);
    checkSum = CRC16.calc(sector) & 0x00ffff;
    putChar(sector, (short) checkSum);
    outputStream.flush();
 
    // 獲取應(yīng)答數(shù)據(jù)
    byte data = getData();
    // 如果收到應(yīng)答數(shù)據(jù)則跳出循環(huán),發(fā)送下一包數(shù)據(jù)
    // 未收到應(yīng)答,錯誤包數(shù)+1,繼續(xù)重發(fā)
    if (data == ACK) {
    break;
    } else {
    ++errorCount;
    }
   }
   // 包序號自增
   blockNumber = (byte) ((++blockNumber) % 256);
   }
 
   // 所有數(shù)據(jù)發(fā)送完成后,發(fā)送結(jié)束標(biāo)識
   boolean isAck = false;
   while (!isAck) {
   putData(EOT);
   isAck = getData() == ACK;
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
  };
 }.start();
 }
 
 /**
 * 接收數(shù)據(jù)
 *
 * @param filePath
 *  文件路徑
 * @return 是否接收完成
 * @throws IOException
 *  異常
 */
 public boolean receive(String filePath) throws Exception {
 // 錯誤包數(shù)
 int errorCount = 0;
 // 包序號
 byte blocknumber = 0x01;
 // 數(shù)據(jù)
 byte data;
 // 校驗和
 int checkSum;
 // 初始化數(shù)據(jù)緩沖區(qū)
 byte[] sector = new byte[SECTOR_SIZE];
 // 寫入文件初始化
 DataOutputStream outputStream = new DataOutputStream(
  new FileOutputStream(filePath));
 
 // 發(fā)送字符C,CRC方式校驗
 putData((byte) 0x43);
 
 while (true) {
  if (errorCount > MAX_ERRORS) {
  outputStream.close();
  return false;
  }
 
  // 獲取應(yīng)答數(shù)據(jù)
  data = getData();
  if (data != EOT) {
  try {
   // 判斷接收到的是否是開始標(biāo)識
   if (data != SOH) {
   errorCount++;
   continue;
   }
 
   // 獲取包序號
   data = getData();
   // 判斷包序號是否正確
   if (data != blocknumber) {
   errorCount++;
   continue;
   }
 
   // 獲取包序號的反碼
   byte _blocknumber = (byte) ~getData();
   // 判斷包序號的反碼是否正確
   if (data != _blocknumber) {
   errorCount++;
   continue;
   }
 
   // 獲取數(shù)據(jù)
   for (int i = 0; i < SECTOR_SIZE; i++) {
   sector[i] = getData();
   }
 
   // 獲取校驗和
   checkSum = (getData() & 0xff) << 8;
   checkSum |= (getData() & 0xff);
   // 判斷校驗和是否正確
   int crc = CRC16.calc(sector);
   if (crc != checkSum) {
   errorCount++;
   continue;
   }
 
   // 發(fā)送應(yīng)答
   putData(ACK);
   // 包序號自增
   blocknumber++;
   // 將數(shù)據(jù)寫入本地
   outputStream.write(sector);
   // 錯誤包數(shù)歸零
   errorCount = 0;
 
  } catch (Exception e) {
   e.printStackTrace();
 
  } finally {
   // 如果出錯發(fā)送重傳標(biāo)識
   if (errorCount != 0) {
   putData(NAK);
   }
  }
  } else {
  break;
  }
 }
 
 // 關(guān)閉輸出流
 outputStream.close();
 // 發(fā)送應(yīng)答
 putData(ACK);
 
 return true;
 }
 
 /**
 * 獲取數(shù)據(jù)
 *
 * @return 數(shù)據(jù)
 * @throws IOException
 *  異常
 */
 private byte getData() throws IOException {
 return (byte) inputStream.read();
 }
 
 /**
 * 發(fā)送數(shù)據(jù)
 *
 * @param data
 *  數(shù)據(jù)
 * @throws IOException
 *  異常
 */
 private void putData(int data) throws IOException {
 outputStream.write((byte) data);
 }
 
 /**
 * 發(fā)送數(shù)據(jù)
 *
 * @param data
 *  數(shù)據(jù)
 * @param checkSum
 *  校驗和
 * @throws IOException
 *  異常
 */
 private void putChar(byte[] data, short checkSum) throws IOException {
 ByteBuffer bb = ByteBuffer.allocate(data.length + 2).order(
  ByteOrder.BIG_ENDIAN);
 bb.put(data);
 bb.putShort(checkSum);
 outputStream.write(bb.array());
 }
}

CRC16校驗算法,采用的是查表法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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,
  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,
  0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 };
 
 public static char calc(byte[] bytes) {
 char crc = 0x0000;
 for (byte b : bytes) {
  crc = (char) ((crc << 8) ^ crctable[((crc >> 8) ^ b) & 0x00ff]);
 }
 return (char) (crc);
 }
}

3.使用

?
1
2
3
4
5
// serialPort為串口對象
Xmodem xmodem = new Xmodem(serialPort.getInputStream(),serialPort.getOutputStream());
// filePath為文件路徑
// ./bin/xxx.bin
xmodem.send(filePath);

4.寫在最后

完整的代碼下載

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。

原文鏈接:http://blog.csdn.net/kong_gu_you_lan/article/details/53673236

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产第一页在线视频 | 久久国产影院 | 九九久久国产精品免费热6 九九精品视频一区二区三区 | 男男18视频免费网站 | 久久国产乱子伦精品免费不卡 | 91美女在线 | 免费看一级a一片毛片 | 国模李丽莎大尺度啪啪 | 国语对白做受xxxx | 艹b视频在线观看 | 91精品手机国产露脸 | 精品一区二区三区自拍图片区 | 高h全肉动漫在线观看免费 高h辣h双处全是肉军婚 | 国产亚洲精品第一综合另类 | 午夜影院在线免费观看 | 包臀裙女教师波多野结衣 | 国产精品一区三区 | 欧美精品1区2区 | 青草视频在线观看免费网站 | a片毛片在线免费看 | 无人在线视频高清免费播放 | 日韩在线观看一区二区不卡视频 | 好姑娘完整版在线观看中文 | darkside动漫在线观看 | 婷婷综合亚洲 | 免费观看小视频 | 91李宗精品72集在线观看 | 99国产小视频 | 国产精品刺激好大好爽视频 | 99视频都是精品热在线播放 | 美女脱了内裤张开腿亲吻男生 | 男人疯狂进女人下部视频动漫 | 农村妇女野外性生话免费视频 | 日韩性大片免费 | 日本中文字幕一区二区高清在线 | 日韩成人一区ftp在线播放 | 四虎影院永久在线 | 好大好粗好爽 | 西野翔全部作品在线观看 | 欧美日韩一品道 | 521色香蕉网站在线观看 |