【问题标题】:How do I get a specific byte or bytes from a streambuf?如何从 streambuf 中获取特定字节或字节?
【发布时间】:2017-10-26 05:20:02
【问题描述】:

为了接收带有自定义标头以太网帧的原始协议,我正在使用 streambuf 缓冲区从以太网中读取字节。有效负载大部分都被成功复制,但我需要检查缓冲区中帧头的特定字节,以便我可以处理某些极端情况,但无法弄清楚如何获取特定字节以及如何获取它成整数。代码如下:

boost::asio::streambuf read_buffer;

boost::asio::streambuf::mutable_buffers_type buf = read_buffer.prepare(bytesToGet);
bytesRead = d_socket10->receive(boost::asio::buffer(buf, bytesToGet));
read_buffer.commit(bytesRead);

const char *readData = boost::asio::buffer_cast<const char*>( read_buffer.data() + 32 ); 

我需要获取地址 20 处的长度字节。我尝试使用 stringstream、memcpy 和强制转换来做一些事情,但我没有处理它,要么得到编译错误,要么它没有做什么我觉得应该可以。

如何从我需要的偏移量中获取字节并将其转换为字节或短字节?大小实际上是 2 个字节,但在这种特定情况下,其中一个字节应该为零,因此获得 1 个字节或 2 个字节将是理想的。

谢谢!

【问题讨论】:

    标签: boost buffer ethernet gnuradio


    【解决方案1】:

    欢迎解析。

    欢迎使用二进制数据。

    欢迎使用便携式网络协议。

    这三个主题中的每一个都是他们自己需要处理的事情。

    最简单的方法是读入缓冲区并使用它。使用 Boost Endian 消除可移植性问题。

    这是我能想到的最简单的事情,只使用标准库的东西(忽略字节序):

    Live On Coliru

    #include <boost/asio.hpp>
    #include <istream>
    #include <iostream>
    
    namespace ba = boost::asio;
    
    void fill_testdata(ba::streambuf&);
    
    int main() {
        ba::streambuf sb;
        fill_testdata(sb);
    
        // parsing starts here
        char buf[1024];
        std::istream is(&sb);
        // read first including bytes 20..21:
        is.read(buf, 22);
        size_t actual = is.gcount();
    
        std::cout << "stream ok? " << std::boolalpha << is.good() << "\n";
        std::cout << "actual: " << actual << "\n";
        if (is && actual >= 22) { // stream ok, and not a short read
            uint16_t length = *reinterpret_cast<uint16_t const*>(buf + 20);
            std::cout << "length: " << length << "\n";
    
            std::string payload(length, '\0');
            is.read(&payload[0], length);
            actual = is.gcount();
    
            std::cout << "actual payload bytes: " << actual << "\n";
            std::cout << "stream ok? " << std::boolalpha << is.good() << "\n";
            payload.resize(actual);
    
            std::cout << "payload: '" << payload << "'\n";
        }
    }
    
    // some testdata
    void fill_testdata(ba::streambuf& sb) 
    {
        char data[] = { 
            '\x00', '\x00', '\x00', '\x00', '\x00', // 0..4
            '\x00', '\x00', '\x00', '\x00', '\x00', // 5..9
            '\x00', '\x00', '\x00', '\x00', '\x00', // 10..14
            '\x00', '\x00', '\x00', '\x00', '\x00', // 15..19
            '\x0b', '\x00', 'H'   , 'e'   , 'l'   , // 20..24
            'l'   , 'o'   , ' '   , 'w'   , 'o'   , // 25..29
            'r'   , 'l'   , 'd'   , '!'   ,         // 30..33
        };
        std::ostream(&sb).write(data, sizeof(data));
    }
    

    打印

    stream ok? true
    actual: 22
    length: 11
    actual payload bytes: 11
    stream ok? true
    payload: 'Hello world'
    

    \x0b 增加到\x0c 得到:

    stream ok? true
    actual: 22
    length: 12
    actual payload bytes: 12
    stream ok? true
    payload: 'Hello world!'
    

    将其增加到超过缓冲区中的值,例如 '\x0d 会导致读取失败(部分):

    stream ok? true
    actual: 22
    length: 13
    actual payload bytes: 12
    stream ok? false
    payload: 'Hello world!'
    

    让我们去专业

    要成为专业人士,我会使用一个库,例如提升精神。这了解字节顺序,进行验证,并且当您在解析器中获得分支时真正闪耀,例如

     record = compressed_record | uncompressed_record;
    

    或者

     exif_tags = .... >> custom_attrs;
    
     custom_attr  = attr_key >> attr_value;
     custom_attrs = repeat(_ca_count) [ custom_attrs ];
    
     attr_key = bson_string(64);     // max 64, for security
     attr_value = bson_string(1024); // max 1024, for security
    
     bson_string %= omit[little_dword[_a=_1]] 
                 >> eps(_a<=_r) // not exceeding maximum
                 >> repeat(_a) [byte_];
    

    但那是遥不可及的。让我们做一个更简单的演示:

    Live On Coliru ¹

    #include <boost/asio.hpp>
    
    #include <istream>
    #include <iostream>
    
    namespace ba = boost::asio;
    
    void fill_testdata(ba::streambuf&);
    
    struct FormatData {
        std::string signature, header; // e.g. 4 + 16 = 20 bytes - could be different, of course
        std::string payload;           // 16bit length prefixed
    };
    
    FormatData parse(std::istream& is);
    
    int main() {
        ba::streambuf sb;
        fill_testdata(sb);
    
        try {
            std::istream is(&sb);
            FormatData data = parse(is);
    
            std::cout << "actual payload bytes: " << data.payload.length() << "\n";
            std::cout << "payload: '" << data.payload << "'\n";
        } catch(std::runtime_error const& e) {
            std::cout << "Error: " << e.what() << "\n";
        }
    }
    
    // some testdata
    void fill_testdata(ba::streambuf& sb) 
    {
        char data[] = { 
            'S'   , 'I'   , 'G'   , 'N'   , '\x00'   , // 0..4
            '\x00', '\x00', '\x00', '\x00', '\x00'   , // 5..9
            '\x00', '\x00', '\x00', '\x00', '\x00'   , // 10..14
            '\x00', '\x00', '\x00', '\x00', '\x00'   , // 15..19
            '\x0b', '\x00', 'H'   , 'e'   , 'l'      , // 20..24
            'l'   , 'o'   , ' '   , 'w'   , 'o'      , // 25..29
            'r'   , 'l'   , 'd'   , '!'   , // 30..33
        };
        std::ostream(&sb).write(data, sizeof(data));
    }
    
    //#define BOOST_SPIRIT_DEBUG
    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    namespace qi = boost::spirit::qi;
    
    BOOST_FUSION_ADAPT_STRUCT(FormatData, signature, header, payload)
    
    template <typename It>
    struct FileFormat : qi::grammar<It, FormatData()> {
        FileFormat() : FileFormat::base_type(start) {
            using namespace qi;
    
            signature  = string("SIGN");     // 4 byte signature, just for example
            header     = repeat(16) [byte_]; // 16 byte header, same
    
            payload   %= omit[little_word[_len=_1]] >> repeat(_len) [byte_];
            start      = signature >> header >> payload;
    
            //BOOST_SPIRIT_DEBUG_NODES((start)(signature)(header)(payload))
        }
      private:
        qi::rule<It, FormatData()> start;
        qi::rule<It, std::string()> signature, header;
    
        qi::_a_type _len;
        qi::rule<It, std::string(), qi::locals<uint16_t> > payload;
    };
    
    FormatData parse(std::istream& is) {
        using it = boost::spirit::istream_iterator;
    
        FormatData data;
        it f(is >> std::noskipws), l;
        bool ok = parse(f, l, FileFormat<it>{}, data);
    
        if (!ok)
            throw std::runtime_error("parse failure\n");
    
        return data;
    }
    

    打印:

    actual payload bytes: 11
    payload: 'Hello world'
    

    ¹ 多么美好的时光啊!科利鲁同时淹没并放下魔杖!不得不为在线演示删除 Boost Asio,因为 IdeOne 没有链接 Boost System

    【讨论】:

    • 非常感谢!我使用了您的第一个示例,并对其进行了测试和工作。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-09
    • 2015-02-14
    • 2012-04-02
    • 2023-03-19
    • 1970-01-01
    • 2023-03-04
    • 1970-01-01
    相关资源
    最近更新 更多