我正在尝试为 Netduino 板编写一个 NRPE 解释器。这是一个运行 .NET Micro Framework 4.3 的 Arduino 型板。我在计算协议要求的 CRC 时遇到问题,如下所示(原始 C++ 头文件片段):
typedef struct packet_struct {
int16_t packet_version;
int16_t packet_type;
uint32_t crc32_value;
int16_t result_code;
char buffer[1024];
} packet;
肯定存在字节排序问题,因为我正在从大端(网络)转移到小端(Netduino/.Net)。当 Int16 和 Uint32 进出我的结构时,我一直在尝试小心地反转和重新反转它们。当我重新输出从电线读取的数据包时,它是相同的,所以我相信大部分处理得当。但我计算的 CRC 不是。我调用的例程是来自 Micro 框架的 Utility.ComputeCRC
其他人在这方面也遇到过类似的问题,所以我很幸运能够获得一些线索,可能是什么问题:
Stack Overflow 上关于 Python 中 CRC'ing NRPE 帖子的帖子
例如,似乎很清楚原始消息是 1034 字节,填充到 1036。我不太幸运的是我在受约束的 Micro 环境中,我能找到的所有 CRC 示例代码通常都涉及模板、Linq ,或我无权访问的其他库。
所有帮助表示赞赏。这是一些示例代码,我尝试从现有的有效数据包中重新计算 CRC 失败。
代码输出:
Original 1036 bytes: 0002000174D13FD5426E5F4E5250455F434845434B0000000000000000...
Original CRC: 3FD574D1
1036 bytes with zeroed out checksum: 0002000100000000426E5F4E5250455F434845434B00000000000000....
Re-computed checksum (0xFFFF seed): F5B1C55A
实际代码:
using System;
using System.Text;
// .NET Micro Framework 4.3
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
namespace TestApp
{
public class Program
{
/// <summary>
/// These are the bytes as-received from the wire, hex encoded for readability here.
/// </summary>
private const string OriginalNetworkBytes = "0002000174D13FD5426E5F4E5250455F434845434B00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
/// <summary>
/// Index the CRC starts at in the original message
/// </summary>
private const int CrcIndex = 4;
public static void Main()
{
byte[] bufferBytes = StringToByteArrayFastest(OriginalNetworkBytes);
PrintBytesInHex("Original " + bufferBytes.Length + " bytes: ", bufferBytes);
UInt32 originalCrc = ParseSwappedUInt32(bufferBytes, CrcIndex);
Debug.Print("Original CRC: " + originalCrc.ToString("X"));
// Zero out CRC, then attempt to recompute the CRC
ZeroOutChecksum(bufferBytes);
PrintBytesInHex(bufferBytes.Length + " bytes with zeroed out checksum: ", bufferBytes);
uint computedCrc = Utility.ComputeCRC(bufferBytes, 0, bufferBytes.Length, 0xFFFF);
Debug.Print("Re-computed checksum (0xFFFF seed): " + computedCrc.ToString("X"));
}
/// <summary>
/// From this fine Stack Overflow post:
/// https://stackoverflow.com/questions/321370/convert-hex-string-to-byte-array
/// Because as the author points out, "also works on .NET Micro Framework where (in SDK4.3) byte.Parse(string) only
/// permits integer formats."
/// </summary>
/// <param name="hex"></param>
/// <returns></returns>
public static byte[] StringToByteArrayFastest(string hex)
{
if (hex.Length%2 == 1)
throw new Exception("The binary key cannot have an odd number of digits");
var arr = new byte[hex.Length >> 1];
for (int i = 0; i < hex.Length >> 1; ++i)
{
arr[i] = (byte) ((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1])));
}
return arr;
}
public static int GetHexVal(char hex)
{
int val = hex;
//For uppercase A-F letters:
return val - (val < 58 ? 48 : 55);
//For lowercase a-f letters:
//return val - (val < 58 ? 48 : 87);
//Or the two combined, but a bit slower:
//return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
}
public static UInt32 ParseSwappedUInt32(byte[] byteArray, int arrayIndex)
{
byte[] swappedBytes = ByteSwapper(byteArray, arrayIndex, 4);
return BitConverter.ToUInt32(swappedBytes, 0);
}
public static byte[] ByteSwapper(byte[] array, int incomingArrayIndex, int countOfBytesToSwap)
{
if (countOfBytesToSwap%2 != 0)
{
throw new Exception("Bytes to be swapped must be divisible by 2; you requested " + countOfBytesToSwap);
}
int outgoingArrayIndex = 0;
byte lastByte = 0;
var arrayToReturn = new byte[countOfBytesToSwap];
int finalArrayIndex = incomingArrayIndex + countOfBytesToSwap;
for (int arrayIndex = incomingArrayIndex; arrayIndex < finalArrayIndex; arrayIndex++)
{
bool isEvenIndex = arrayIndex%2 == 0 || arrayIndex == 0;
byte currentByte = array[arrayIndex];
if (isEvenIndex)
{
// Store current byte for next pass through
lastByte = currentByte;
}
else
{
// Swap two bytes, put into outgoing array
arrayToReturn[outgoingArrayIndex] = currentByte;
arrayToReturn[outgoingArrayIndex + 1] = lastByte;
outgoingArrayIndex += 2;
}
}
return arrayToReturn;
}
private static void ZeroOutChecksum(byte[] messageBytesToClear)
{
messageBytesToClear[CrcIndex] = 0;
messageBytesToClear[CrcIndex + 1] = 0;
messageBytesToClear[CrcIndex + 2] = 0;
messageBytesToClear[CrcIndex + 3] = 0;
}
/// <summary>
/// Debug function to output the message as a hex string
/// </summary>
public static void PrintBytesInHex(string messageLabel, byte[] messageBytes)
{
string hexString = BytesToHexString(messageBytes);
Debug.Print(messageLabel + hexString);
}
private static string BytesToHexString(byte[] messageBytes)
{
var sb = new StringBuilder();
foreach (byte b in messageBytes)
{
sb.Append(b.ToString("X2"));
}
string hexString = sb.ToString();
return hexString;
}
}
}