简体   繁体   English

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

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

I have taken the code from chat_server example of boost. 我从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;
};

The stOrderPacket is the structure shared by TCP client and TCPServer. 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_;        

};

The chat_message class is a class which holds the message to be written or read, and the data is stored in simply a char array of size equal to the size of structure stOrderPacket. 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()));

    }

The above function in chat_session class starts a session with a connected client. 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()));

    }
};

The packets which are being sent from client side are as follows: 从客户端发送的数据包如下:

 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 |

But the values read from read_msgs_.data, when memcopied into structure stOrderPacket are received in such a way that: 但是当从mem_opgs到结构stOrderPacket时,从read_msgs_.data读取的值以这样的方式接收:

  • a) The first packet is correct a)第一个数据包是正确的

  • b) In the second packet the first 4 bytes are garbage values and then I am able to get the value of ID as 3022, which I came to know cz this value got assigned to stOrderPacket.MarketID. b)在第二个数据包中,前4个字节是垃圾值,然后我能够获得ID为3022的值,我才知道cz这个值被分配给stOrderPacket.MarketID。

  • c) The Third value can be read correct from the index which is 0+2* sizeof(int) c)可以从索引读取第三个值,该索引为0 + 2 * sizeof(int)

So basically for every packet received, starting (n-1)*4 bytes are additionally are garbage first and then the information starts. 所以基本上对于每个收到的数据包,起始(n-1)* 4字节另外是垃圾优先然后信息开始。 Also the value of bytes_transferred is 64 for all the 3 packets. 对于所有3个数据包,bytes_transferred的值也是64。

Note: I am running this code on CentOS 7 on x86_64 architecture. 注意:我在x86_64架构上的CentOS 7上运行此代码。

Please help, if anyone can. 请帮助,如果有人可以。

It is bad practice and very unsafe to build a network protocol around sending the raw data of a structure. 在发送结构的原始数据时构建网络协议是不好的做法,也是非常不安全的。 A structures memory is not the exact same on all machines and this can lead to errors. 结构内存在所有机器上都不完全相同,这可能导致错误。 An easy way to accomplish this is to use a serialization library to build you a data buffer from your structure and on the other side take a data buffer and build the structure. 一种简单的方法是使用序列化库从您的结构构建数据缓冲区,另一方面获取数据缓冲区并构建结构。 Boost Serialization is a very good option and so is Google Protocol Buffers . Boost Serialization是一个非常好的选择, Google Protocol Buffers也是如此。 Here is an example: 这是一个例子:

SENDER SENDER

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

RECEIVER 接收器

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);

The size of the buffer will vary depending on the data so you will need to add size transmission to your protocol. 缓冲区的大小将根据数据而有所不同,因此您需要在协议中添加大小传输。 This will consist of serializing the data and then telling the receiver the size to expect. 这将包括序列化数据,然后告诉接收器期望的大小。 The receiver will then read for that size and then deserialize the data. 接收器然后将读取该大小,然后反序列化数据。

Based on the described behavior where the application protocol uses a fixed size message ( 64 bytes), and when the server reads a message, the starting point for message n is offset by 4 * (n - 1) bytes, then the most likely case is that the client is sending 68 bytes of data per message, where the first 64 bytes contain a stOrderPacket , and the client is only expecting 64 bytes per message. 基于所描述的行为,其中应用程序协议使用固定大小的消息( 64字节),并且当服务器读取消息时,消息n的起始点偏移4 * (n - 1)个字节,那么最可能的情况是客户端每个消息发送68字节的数据,其中前64个字节包含stOrderPacket ,客户端只需要每个消息64个字节。 Here are a few areas to check: 以下是一些需要检查的方面:

  • Verify that client and server are using the same application protocol. 验证客户端和服务器是否使用相同的应用程序协议。 For instance, perhaps the server is using a newer version that removed a 4 byte field from the end, or the client is using a newer version that added a 4 byte field to the end. 例如,服务器可能正在使用从末尾删除4字节字段的较新版本,或者客户端正在使用在末尾添加4字节字段的较新版本。
  • Use a network analyzer tool, such as Wireshark , to verify that application protocol on the wire. 使用网络分析器工具(如Wireshark )验证线路上的应用程序协议。 TCP is a stream of data, so one cannot depend on the network analyzer tool to do proper framing based on the application protocol. TCP是一种数据流,因此不能依赖于网络分析工具来根据应用协议进行适当的成帧。 To analyze, one may need to send a few messages, concatenate data from multiple TCP packets together, then deduce framing and fields. 要进行分析,可能需要发送一些消息,将来自多个TCP数据包的数据连接在一起,然后推断出帧和字段。 After analysis, if each message is 68 bytes, then the client or server need to be updated to use the same application protocol. 分析后,如果每条消息都是68字节,则需要更新客户端或服务器以使用相同的应用程序协议。 On the other hand, if the message is 64 bytes, then the error likely exists within the server (bad logic, undefined behavior, etc.). 另一方面,如果消息是64字节,则服务器中可能存在错误(错误逻辑,未定义行为等)。
  • If the client is sending 64 bytes per message, then verify that the server is properly reading from the socket: 如果客户端每个消息发送64个字节,则验证服务器是否正在从套接字读取:
    • socket_ is not thread safe so verify there are no concurrent calls being made to it. socket_不是线程安全的,因此验证没有对它进行并发调用。
    • The buffer ( read_msg_.data() ) provided to boost::asio::async_read() must remain valid at least until the completion handler is called. 提供给boost::asio::async_read()的缓冲区( read_msg_.data() )必须至少在调用完成处理程序之前保持有效。
    • No other read operations are being performed on socket_ until async_read() has completed. async_read()完成之前, async_read()socket_执行其他读取操作。

Also, when using a standard-layout struct to define any part of an application protocol, one may find that it increases readability and safety by using exact-width types where possible and/or diagraming the protocol: 此外,当使用标准布局结构来定义应用程序协议的任何部分时,可以发现通过在可能的情况下使用精确宽度类型和/或绘制协议来提高可读性和安全性:

//     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