当我尝试发送超过 16kb 的文件时,我的 ZModem 实现出现问题。
我跟踪了与 WireShark 的通信,并将我的数据与 TeraTerm 发送的数据进行了比较。
例如,TeraTerm 能够发送各种文件大小。我面临的问题是,在某些时候接收设备向我发送了一个 ZPOS 请求。
当我尝试从给定的偏移量重新发送数据时,我的数据流会损坏。每个进一步的数据包都会导致该 ZPOS 响应。
这是我使用 WireShark 跟踪的完整通信。
rz\r
用命令调用接收器。
0000 1b 00 30 b5 45 fb 01 a2 ff ff 00 00 00 00 09 00 ..0µEû.¢ÿÿ......
0010 00 03 00 14 00 01 03 03 00 00 00 72 7a 0d ...........rz.
启动会话
0000 1b 00 70 b1 f0 15 02 a2 ff ff 00 00 00 00 09 00 ..p±ð..¢ÿÿ......
0010 00 03 00 14 00 01 03 14 00 00 00 2a 2a 18 42 30 ...........**.B0
0020 30 30 30 30 30 30 30 30 30 30 30 30 30 0d 0a 0000000000000..
来自设备的响应
0000 1b 00 90 01 5b fb 01 a2 ff ff 00 00 00 00 09 00 ....[û.¢ÿÿ......
0010 01 03 00 14 00 81 03 15 00 00 00 2a 2a 18 42 30 ...........**.B0
0020 31 30 30 30 30 30 30 32 33 62 65 35 30 0d 8a 11 100000023be50...
然后我发送带有文件名和文件信息的文件头。
0000 1b 00 b0 fa b3 00 02 a2 ff ff 00 00 00 00 09 00 ..°ú³..¢ÿÿ......
0010 00 03 00 14 00 01 03 0a 00 00 00 2a 18 41 04 00 ...........*.A..
0020 00 01 01 aa 16 ...ª.
0000 1b 00 b0 f1 a1 08 02 a2 ff ff 00 00 00 00 09 00 ..°ñ¡..¢ÿÿ......
0010 00 03 00 14 00 01 03 20 00 00 00 73 61 6d 70 6c ....... ...sampl
0020 65 2e 74 78 74 00 32 31 35 30 31 20 31 33 33 37 e.txt.21501 1337
0030 37 35 30 31 33 33 32 18 6b 83 e5 7501332.k.å
设备的响应告诉我它已准备好接收文件。
0000 1b 00 90 01 5b fb 01 a2 ff ff 00 00 00 00 09 00 ....[û.¢ÿÿ......
0010 01 03 00 14 00 81 03 15 00 00 00 2a 2a 18 42 30 ...........**.B0
0020 39 30 30 30 30 30 30 30 30 61 38 37 63 0d 8a 11 900000000a87c...
之后,我发送 ZData 标头和所有子数据包。
0000 1b 00 b0 f1 a1 08 02 a2 ff ff 00 00 00 00 09 00 ..°ñ¡..¢ÿÿ......
0010 00 03 00 14 00 01 03 0b 00 00 00 2a 18 41 18 4a ...........*.A.J
0020 00 00 00 00 46 ae ....F®
当我尝试上传更大的文件时,我会在上传期间收到 ZRPOS 响应。关于 ZModem 规范,接收器告诉我在给定的偏移量处恢复。
0000 1b 00 90 01 5b fb 01 a2 ff ff 00 00 00 00 09 00 ....[û.¢ÿÿ......
0010 01 03 00 14 00 81 03 15 00 00 00 2a 2a 18 42 30 ...........**.B0
0020 39 30 30 34 30 30 30 30 30 62 35 64 31 0d 8a 11 900400000b5d1...
但是每个进一步的子数据包都会从接收设备获得相同的响应。
这是我负责发送文件的实现。
private void SendZDATAPackets(byte[] src, CRC16CCIT crcCalculator)
{
// Chunk size
var chunkSize = 1024;
// Create ZDATA header
var zdataHeaderQueue = new Queue<byte>();
var zdataHeaderCommand = Utils.BuildDataCommand(HeaderType.ZDATA, 0, 0, 0, 0);
foreach (var c in zdataHeaderCommand)
{
zdataHeaderQueue.Enqueue((byte)c);
}
// Send ZDATA header
SendCommand(zdataHeaderQueue.ToArray());
ResponseHeader zdataResponse = null;
// Slice binary data into chunks.
for (int i = 0; i < src.Length; i += chunkSize)
{
// Slice data
var dataSlice = src
.Skip(i)
.Take(chunkSize)
.ToArray();
// Send ZCRCG, if its the last part of the data send ZCRCE
var requiredSequence = (i + dataSlice.Length) < src.Length ? ZDLESequence.ZCRCG : ZDLESequence.ZCRCE;
if (zdataResponse?.ZHeader == HeaderType.ZRPOS)
{
/*
* A ZRPOS header resets the sender's file offset to the correct position.
* If possible, the sender should purge its output buffers and/or networks
* of all unprocessed output data, to minimize the amount of unwanted data
* the receiver must discard before receiving data starting at the correct
* file offset. The next transmitted data frame should be a ZCRCW frame
* followed by a wait to guarantee complete flushing of the network's memory.
*/
SerialPort.DiscardOutBuffer();
dataSlice = src
.Skip(zdataResponse.RequestedOffset)
.Take(chunkSize)
.ToArray();
requiredSequence = ZDLESequence.ZCRCW;
}
// Create data queue for the sliced data.
var queue = new Queue<byte>(dataSlice);
// Calculate CRC
var dataForCrcCalculation = dataSlice.Concat(new byte[] { (byte)requiredSequence }).ToArray();
var crc = crcCalculator.ComputeChecksumAsBytes(dataForCrcCalculation);
// Add crc
queue.Enqueue((byte)ControlBytes.ZDLE);
queue.Enqueue((byte)requiredSequence);
queue.Enqueue(crc[0]);
queue.Enqueue(crc[1]);
// Send data
var zdataCommand = queue.ToArray();
zdataResponse = SendCommand(zdataCommand);
}
}
有没有人在实施过程中遇到过同样的问题,并且可以指出我解决这个问题的一些方向?
谢谢!