使用Asio(Boost)通过网络发送灵活的数据量

little_planet

我有一个客户端和一个服务器应用程序,它们将通过使用Asio(独立)库相互发送数据。这两个应用程序都包含两个(逻辑)部分:

  1. 高级部分:处理复杂的对象,例如用户,权限,...
  2. 低级部分:在客户端和服务器之间通过网络发送数据

假设已经使用Protocoll Buffers对复杂对象进行了序列化,并且应用程序的底层部分从高层部分接收数据,如std :: string。我想使用Protocoll Buffers中的此功能来完成此工作:

bool SerializeToString(字符串*输出)const ;:对消息进行序列化并将字节存储在给定的字符串中。注意字节是二进制的,不是文本;我们仅将字符串类用作方便的容器。

并说我在客户端使用async_write传输此数据:

size_t dataLength = strlen(data);

//writes a certain number of bytes of data to a stream.
asio::async_write(mSocket,
                      asio::buffer(data, dataLength),
                      std::bind(&Client::writeCallback, this,
                                std::placeholders::_1,   
                                std::placeholders::_2)); 

如何在服务器端读取此数据?我不知道我将要读取多少数据。因此,这将不起作用(长度未知):

 asio::async_read(mSocket,
                     asio::buffer(mResponse, length),
                     std::bind(&Server::readCallback, this,
                               std::placeholders::_1,
                               std::placeholders::_2));

解决此问题的最佳方法是什么?我可以想到两种解决方案:

  1. 在“结尾”后面附加一个“特殊”字符data并读取,直到到达“数据信号结尾”为止。问题是,如果此字符以data某种方式出现怎么办?我不知道Protocoll Buffers如何序列化我的数据。
  2. 发送带有size_of_data + data而不是的二进制字符串data但是我不知道如何以独立于平台的方式序列化大小,将其添加到二进制数据并再次提取。

编辑:也许我可以用这个:

    uint64_t length = strlen(data);
    uint64_t nwlength = htonl(length);
    uint8_t len[8];
    len[0] = nwlength >> 56;
    len[1] = nwlength >> 48;
    len[2] = nwlength >> 40;
    len[3] = nwlength >> 32;
    len[4] = nwlength >> 24;
    len[5] = nwlength >> 16;
    len[6] = nwlength >> 8;
    len[7] = nwlength >> 0;

    std::string test(len);

    mRequest = data;
    mRequest.insert(0, test);

并发送mRequest到服务器?此代码有任何陷阱或警告吗?之后如何读取服务器端的长度和内容?可能是这样的:

void Server::readHeader(){

    asio::async_read(mSocket,
                     asio::buffer(header, HEADER_LENGTH),
                     std::bind(&Server::readHeaderCallback, this,
                               std::placeholders::_1,
                               std::placeholders::_2),
                     asio::transfer_exactly(HEADER_LENGTH));
}

void Server::readHeaderCallback(const asio::error_code& error,
                                        size_t bytes_transferred){

    if(!error && decodeHeader(header, mResponseLength)){
        //reading header finished, now read the content
        readContent();
    }
    else{
        if(error) std::cout << "Read failed: " << error.message() << "\n";
        else std::cout << "decodeHeader failed \n";       
    }
}

void Server::readContent(){

    asio::async_read(mSocket,
                     asio::buffer(mResponse, mResponseLength),
                     std::bind(&Server::readContentCallback, this,
                               std::placeholders::_1,
                               std::placeholders::_2),
                     asio::transfer_exactly(mResponseLength));
}

void Server::readContentCallback(const asio::error_code& error,
                                         size_t bytes_transferred){
    if (!error){
       //handle content
    }
    else{
        //@todo remove this cout
        std::cout << "Read failed: " << error.message() << "\n";      
    }
}

请注意,我尝试使用transfer_exactly这样行吗?

坦纳·桑斯伯里(Tanner Sansbury)

通过基于流的协议发送可变长度消息时,通常存在三种指示消息边界的解决方案:

  • 使用定界符指定消息边界。这些async_read_until()操作提供了一种读取可变长度定界消息的便捷方法。使用定界符时,需要考虑定界符冲突的可能性,其中定界符出现在消息的内容之内,但并不表示边界。有多种处理定界符冲突的技术,例如转义字符或转义序列。
  • 将固定长度的标头与可变长度的主体协议一起使用。标头将提供有关消息的元信息,例如正文的长度。官方的Asio聊天示例演示了一种处理固定长度报头和可变长度主体协议的方法。

    如果要发送二进制数据,则需要考虑处理字节顺序hton()ntoh()系列函数可以用字节序帮助。例如,考虑一种协议,该协议将字段定义为网络字节顺序(big-endian)的两个字节,并且客户端将字段读取为uint16_t如果10发送了该值,并且little-endian机器读取了该值而不将其从网络顺序转换为本地顺序,则客户端会将值读取为2560Asio聊天示例通过将主体长度编码为字符串而不是二进制形式来避免处理字节序。

  • 使用连接的文件结尾来指示消息的结尾。虽然这使发送和接收消息变得容易,但将发件人限制为每个连接仅发送一条消息。要发送其他消息,则需要建立另一个连接。


关于代码的一些观察:

  • 协议缓冲区的SerializeToString()功能将消息序列化为二进制形式。应该避免在序列化字符串上使用基于文本的函数,例如strlen()例如,strlen()可能会错误地确定长度,因为它将把值为的第一个字节0视为终止的空字节,即使该字节是编码值的一部分。
  • 当通过提供一个明确大小的缓冲区给操作时asio::buffer(buffer, n),默认的完成条件的transfer_all功能与相同transfer_exactly(n)因此,可以删除重复使用的变量:

    asio::async_read(mSocket,
                     asio::buffer(header, HEADER_LENGTH),
                     std::bind(&Server::readHeaderCallback, this,
                              std::placeholders::_1,
                              std::placeholders::_2));
    
  • htonl()重载支持uint16_tuint_32tuint64_t

  • Asio支持分散/聚集操作,允许接收操作分散读取到多个缓冲区中,而发送操作可以从多个缓冲区收集-写入。因此,不一定必须将固定长度的标头和消息正文都包含在单个缓冲区中。

    std::string body_buffer;
    body.SerializeToString(&body_buffer);
    std::string header_buffer = encode_header(body_buffer.size());
    
    // Use "gather-write" to send both the header and data in a
    // single write operation.
    std::vector<boost::asio::const_buffer> buffers;
    buffers.push_back(boost::asio::buffer(header_buffer));
    buffers.push_back(boost::asio::buffer(body_buffer));
    boost::asio::write(socket_, buffers);
    

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何丢弃通过boost :: asio发送的数据?

来自分类Dev

通过boost :: asio发送原始数据

来自分类Dev

如何丢弃通过boost :: asio发送的数据?

来自分类Dev

使用boost :: asio通过UDP发送结构

来自分类Dev

为什么我能够通过蓝牙 4.2 发送超过明显的最大数据量?

来自分类Dev

使用javascript设置数据量属性

来自分类Dev

仅使用数据量选择元素

来自分类Dev

Boost asio网络单独发送和接收

来自分类Dev

如何衡量特定应用发送和接收的数据量?

来自分类Dev

如何检查MailKit中发送/接收的数据量?

来自分类Dev

Facebook GraphAPI通过限制减少数据量

来自分类Dev

使用$ In运算符查询并限制加载的数据量

来自分类Dev

如何使用JSON数据量重复模板

来自分类Dev

如何使用usbmon知道从usb(pendrive)传输的数据量?

来自分类Dev

MySQL不完全基于数据量使用索引

来自分类Dev

如何获得全天使用的Internet数据量?

来自分类Dev

使用 Invantive SQL 分析 API 调用的数据量

来自分类Dev

如何使用 fetch 限制从 JSON 文件返回的数据量?

来自分类Dev

更新Stripe数据量

来自分类Dev

尝试使用boost :: serialization通过boos :: asio套接字发送派生类

来自分类Dev

PHP脚本可以发送到MySQL TEXT字段的最大数据量?

来自分类Dev

PHP脚本可以发送到MySQL TEXT字段的最大数据量?

来自分类Dev

通过限制读取的数据量来从缓冲读取器读取数据

来自分类Dev

boost :: asio :: read阻止boost:asio :: write将数据发送到Java套接字

来自分类Dev

使用Boost :: Asio通过TCP读取二进制数据

来自分类Dev

如何设置docker mongo数据量

来自分类Dev

Python图簇:高数据量

来自分类Dev

Oracle查找每列的数据量

来自分类Dev

Python图簇:高数据量

Related 相关文章

热门标签

归档