繁体   English   中英

Boost :: asio :: async_read()缓冲区损坏问题

[英]Boost::asio:: async_read() buffer corruption issue

我从chat_server的例子中获取了boost的代码。

enum eTransactionType{
    eBuy=0,
    eSell=1
};
struct stOrderPacket{
    int     ID;
    int     MarketID;
    char    m_strSignalName[22];
    char    m_strTradeSymbol[22];
    int     m_iQty;
    float   m_fPrice;
    eTransactionType m_eTransactionType;
};

stOrderPacket是TCP客户端和TCPServer共享的结构。

class chat_message
{
public:

  enum { max_body_length = sizeof(stOrderPacket) };

  chat_message()
    : body_length_(0)
  {
  }

  const char* data() const
  {
    return data_;
  }

  char* data()
  {
    return data_;
  }

  size_t length() const
  {
    return body_length_;
  }

  void SetData(char* msg, int len)   
  {           
      memset(data_,0x00,len);memcpy(data_,msg,len);
  }
  void SetOrderParams(stOrderPacket a_stOrderParams);
  size_t body_length() const
  {
    return body_length_;
  }
  void ClearPacket()
 {
      memset(data_,0x00,max_body_length);    
 }
 void body_length(size_t length);
private:
    char data_[sizeof(stOrderPacket)];
    size_t body_length_;        

};

chat_message类是一个保存要写入或读取的消息的类,数据只存储在一个大小等于结构stOrderPacket大小的char数组中。

class chat_session{
    void start()
    {                

        boost::asio::async_read(socket_,boost::asio::buffer(read_msg_.data(),sizeof(stOrderPacket)),
               boost::bind(&chat_session::handle_read_body,shared_from_this(),placeholders::error, placeholders::bytes_transferred()));

    }

chat_session类中的上述函数启动与连接客户端的会话。

     void handle_read_body(const boost::system::error_code& error,std::size_t bytes_transferred)
    {

        if (0!=error)
        {
          // handle Close connection. 
          return;
        }    

        /// stub for parsing the packet
        memcpy(&m_stOrderPacket,&m_pvBuffer,sizeof(m_stOrderPacket));

        read_msg_.ClearPacket();
        boost::asio::async_read(
        socket_, buffer(read_msg_.data(),sizeof(structs::stOrderParameters)),
            boost::bind(&chat_session::handle_read_body,shared_from_this(),placeholders::error,placeholders::bytes_transferred()));

    }
};

从客户端发送的数据包如下:

 ID   | MarketID | Symbol     | SignalName        | TradeType | Qty | EntryPrice |

| 3021 |       1030320 | RELIANCEEQ | MU_30_INLE_4097_3 | Long      | 285 |     1121.1 |

| 3022 |       1030321 | RELIANCEEQ | MU_30_INLE_4097_3 | Long      | 178 |       1121 |

| 3038 |       1030505 | RELIANCEEQ | AS_15_SE_53       | Short     | 340 

|    1116.95 |

但是当从mem_opgs到结构stOrderPacket时,从read_msgs_.data读取的值以这样的方式接收:

  • a)第一个数据包是正确的

  • b)在第二个数据包中,前4个字节是垃圾值,然后我能够获得ID为3022的值,我才知道cz这个值被分配给stOrderPacket.MarketID。

  • c)可以从索引读取第三个值,该索引为0 + 2 * sizeof(int)

所以基本上对于每个收到的数据包,起始(n-1)* 4字节另外是垃圾优先然后信息开始。 对于所有3个数据包,bytes_transferred的值也是64。

注意:我在x86_64架构上的CentOS 7上运行此代码。

请帮助,如果有人可以。

在发送结构的原始数据时构建网络协议是不好的做法,也是非常不安全的。 结构内存在所有机器上都不完全相同,这可能导致错误。 一种简单的方法是使用序列化库从您的结构构建数据缓冲区,另一方面获取数据缓冲区并构建结构。 Boost Serialization是一个非常好的选择, Google Protocol Buffers也是如此。 这是一个例子:

SENDER

std::vector<unsigned char> buffer;
stOrderPacket packet;
Serialize(packet, buffer);
boost::asio::write(socket, boost::asio::buffer(buffer), boost::asio::transfer_all());

接收器

std::vector<unsigned char> buffer;
buffer.resize(buffer_size);
stOrderPacket packet;
boost::asio::read(socket, boost::asio::buffer(buffer), boost::asio::transfer_exactly(buffer_size));
Deserialize(packet, buffer);

缓冲区的大小将根据数据而有所不同,因此您需要在协议中添加大小传输。 这将包括序列化数据,然后告诉接收器期望的大小。 接收器然后将读取该大小,然后反序列化数据。

基于所描述的行为,其中应用程序协议使用固定大小的消息( 64字节),并且当服务器读取消息时,消息n的起始点偏移4 * (n - 1)个字节,那么最可能的情况是客户端每个消息发送68字节的数据,其中前64个字节包含stOrderPacket ,客户端只需要每个消息64个字节。 以下是一些需要检查的方面:

  • 验证客户端和服务器是否使用相同的应用程序协议。 例如,服务器可能正在使用从末尾删除4字节字段的较新版本,或者客户端正在使用在末尾添加4字节字段的较新版本。
  • 使用网络分析器工具(如Wireshark )验证线路上的应用程序协议。 TCP是一种数据流,因此不能依赖于网络分析工具来根据应用协议进行适当的成帧。 要进行分析,可能需要发送一些消息,将来自多个TCP数据包的数据连接在一起,然后推断出帧和字段。 分析后,如果每条消息都是68字节,则需要更新客户端或服务器以使用相同的应用程序协议。 另一方面,如果消息是64字节,则服务器中可能存在错误(错误逻辑,未定义行为等)。
  • 如果客户端每个消息发送64个字节,则验证服务器是否正在从套接字读取:
    • socket_不是线程安全的,因此验证没有对它进行并发调用。
    • 提供给boost::asio::async_read()的缓冲区( read_msg_.data() )必须至少在调用完成处理程序之前保持有效。
    • async_read()完成之前, async_read()socket_执行其他读取操作。

此外,当使用标准布局结构来定义应用程序协议的任何部分时,可以发现通过在可能的情况下使用精确宽度类型和/或绘制协议来提高可读性和安全性:

//     0                   1                   2                   3
//     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// 00 +-------+-------+-------+-------+-------+-------+-------+------+
//    |                              ID                              |
// 04 +-------+-------+-------+-------+-------+-------+-------+------+
//    |                           Market ID                          |
// 08 +-------+-------+-------+-------+-------+-------+-------+------+
//    |                          Signal Name                         |
//    /                                                              /
// 28 +              ...              +-------+-------+-------+------+
//    |                               |         Trade Symbol ...     |
// 32 +-------+-------+-------+-------+                              +
//    /                                                              /
// 48 +                              ...                             +
//    |                                                              |
// 52 +-------+-------+-------+-------+-------+-------+-------+------+
//    |                           Quantity                           |
// 56 +-------+-------+-------+-------+-------+-------+-------+------+
//    |                             Price                            |
// 60 +-------+-------+-------+-------+-------+-------+-------+------+
//    |                         Transaction Type                     |
// 64 +-------+-------+-------+-------+-------+-------+-------+------+
struct stOrderPacket
{
  int32_t  ID;
  int32_t  MarketID;
  char     m_strSignalName[22];
  char     m_strTradeSymbol[22];
  int32_t  m_iQty;
  float    m_fPrice;
  eTransactionType m_eTransactionType;
};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM