你怎么解释这句话?

时间:2011-09-17 19:54:05

标签: tcp binary protocols checksum

你怎么解释这句话?

  

校验
  这是从标头到校验和的低8位加零的值。

使用此协议说明:

  

协定
  包括头(1字节)+数据长度(1字节)+   命令数据(13字节)+校验和(1字节)+连接ID(1   字节)。

(我真的复制了这个协议描述,所以我不知道为什么有1 bytes(复数形式)。但我可以告诉你它只有一个字节)

以下是此协议的一些示例TCP数据包:

HE:DL:------------Command Data -------------:CS:ID
02:0d:be:ef:03:06:00:19:d3:02:00:00:60:00:00:ed:01
02:0d:be:ef:03:06:00:cd:d2:02:00:00:20:00:00:7a:01
02:0d:be:ef:03:06:00:10:f6:02:00:ba:30:00:00:49:01
02:0d:be:ef:03:06:00:c8:d8:02:00:20:30:00:00:49:01
// Requested Packets
02:0d:be:ef:03:06:00:13:d3:01:00:02:30:01:00:21:01
02:0d:be:ef:03:06:00:c2:ff:02:00:90:10:00:00:d8:01

其中

  • HE是标题(固定为0x02
  • DL是DataLength(始终为0x0d,因为数据包的长度都相同)
  • CS是CheckSum(这是我的问题)
  • ID是连接ID(在我的测试中似乎总是01)

我无法弄清楚如何计算校验和。我希望我提供足够的信息。

提前致谢。

4 个答案:

答案 0 :(得分:3)

我认为说明中只有一个错误。看起来它们正在汇总标头和校验和之间的所有字节。校验和只是一个清除低8位的数字。因此,对于第一个示例,标头和校验和之间的所有字节总和为0x0313。或

0x0313 0000 0011 0001 0011
0x00ED 0000 0000 1110 1101

当它排成一行时,你可以清楚地看到你将零下8位归零并返回:

0x0400 0000 0100 0000 0000

您没有指定语言,但您也可以通过执行(0 XOR calculatedSum)+ 1快速计算自己的校验和。

答案 1 :(得分:2)

  1. 汇总校验和字段前的所有字节。
  2. 提取总和的低8位
  3. 找到值(“使值为零”),当添加到计算的总和时变为0(即,您解决“x +计算的总和= 0”)
  4. 无论如何,这个C代码计算了所有4个例子的正确总和:

    uint8_t silly_chksum(const uint8_t *data, size_t len)
    {
      size_t i;
      unsigned int chk = -1;
      for(i = 0; i < len ;i++)
        {
            chk += data[i];
        }
    
        return ~(chk & 0xff);
    }
    

答案 2 :(得分:2)

为了好玩,我把它作为练习使用Boost Spirit(c ++)编写一个简单的二进制解析器。我包括了问题格式和校验和验证的转换。

我让解析器识别实际数据长度和任意选择STL容器的数据包数据。输出如下:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace qi=boost::spirit::qi;
namespace karma=boost::spirit::karma;
namespace phx=boost::phoenix;

typedef unsigned char uchar;
static const auto inbyte  = qi::uint_parser<unsigned char, 16, 2, 2>();
static const auto outbyte = karma::right_align(2,'0') [ karma::hex ];

// for some reason the alignment doesn't 'take' with the above, so HACK:
#define outbyte karma::right_align(2,'0') [ karma::hex ]

struct packet_t
{
    enum { HEADER = 0x02 };
    uchar checksum, id;

    typedef std::string data_t;
    /// the following work without modification:
    // typedef std::vector<uchar> data_t; 
    // typedef std::list<int> data_t; 
    data_t data;

    uchar do_checksum() const 
    { 
        return (uchar) -std::accumulate(data.begin(), data.end(), 
            HEADER + data.size()); 
    }

    bool is_valid() const 
    { return checksum == do_checksum(); }
};

BOOST_FUSION_ADAPT_STRUCT(packet_t,
        (packet_t::data_t, data) (uchar, checksum) (uchar, id)); 

int main()
{
    static const std::string input = 
        "02:0d:be:ef:03:06:00:19:d3:02:00:00:60:00:00:ed:01\n"
        "02:0d:be:ef:03:06:00:cd:d2:02:00:00:20:00:00:7a:01\n"
        "02:0d:be:ef:03:06:00:10:f6:02:00:ba:30:00:00:49:01\n"
        "02:0d:be:ef:03:06:00:c8:d8:02:00:20:30:00:00:49:01\n"
        "02:08:c8:d8:02:00:20:30:00:00:49:01\n"; // failure test case

    // convert hex to bytes
    std::vector<std::vector<char> > rawpackets;
    if (!qi::parse(input.begin(), input.end(), (inbyte % ':') % qi::eol, rawpackets))
        { std::cerr << "bailing" << std::endl; return 255; }

    // std::cout << karma::format(karma::eps << outbyte % ':' % karma::eol, rawpackets) << std::endl;

    // analyze & checksum packets
    for (auto raw: rawpackets)
    {
        std::cout << karma::format(karma::eps << outbyte % ':', raw);

        using namespace qi;
        rule<decltype(raw.begin()), packet_t(), locals<uchar> > parser;
        parser %= byte_(packet_t::HEADER)
                > omit[ byte_ [ _a = _1 ] ] // datalen
                > repeat(_a)[byte_]         // data
                > byte_                     // checksum
                > byte_;                    // id

        packet_t packet;
        if (!parse(raw.begin(), raw.end(), parser, packet))
            { std::cerr << " bailing" << std::endl; return 255; }

        std::cout << " do_checksum():\t" << karma::format(outbyte, packet.do_checksum());
        std::cout << " is_valid():\t"    << std::boolalpha << packet.is_valid() << std::endl;
    }

    return 0;
}

输出

02:0d:be:ef:03:06:00:19:d3:02:00:00:60:00:00:ed:01 do_checksum():   ed is_valid():  true
02:0d:be:ef:03:06:00:cd:d2:02:00:00:20:00:00:7a:01 do_checksum():   7a is_valid():  true
02:0d:be:ef:03:06:00:10:f6:02:00:ba:30:00:00:49:01 do_checksum():   49 is_valid():  true
02:0d:be:ef:03:06:00:c8:d8:02:00:20:30:00:00:49:01 do_checksum():   49 is_valid():  true
02:08:c8:d8:02:00:20:30:00:00:49:01 do_checksum():  04 is_valid():  false

答案 3 :(得分:1)

如果在Header,DataLength,CommandData和CheckSum中添加所有十六进制数字,则会生成1024。

例如,这是第二个例子的总和(我跳过了0x00):

0x02+0x0d+0xbe+0xef+0x03+0x06+0xcd+0xd2+0x02+0x20+0x7a = 1024

截图(总和只是我写的一个javascript函数,用于自动对十六进制值求和):

Screenshot showing sum

修改 它不一定总和为1024,相反,如果它是有效的,它将总和为8个低位为0的数字,例如1024(100 <00> 00000000 ),768(11 <00> 00000000 )和1280(101 <00> 00000000 )。