简体   繁体   中英

Boost::asio:: async_read() buffer corruption issue

I have taken the code from chat_server example of 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.

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.

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.

     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:

  • a) The first packet is correct

  • 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.

  • c) The Third value can be read correct from the index which is 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. Also the value of bytes_transferred is 64 for all the 3 packets.

Note: I am running this code on CentOS 7 on x86_64 architecture.

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 . Here is an example:

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. 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.
  • Use a network analyzer tool, such as Wireshark , to verify that application protocol on the wire. TCP is a stream of data, so one cannot depend on the network analyzer tool to do proper framing based on the application protocol. To analyze, one may need to send a few messages, concatenate data from multiple TCP packets together, then deduce framing and fields. After analysis, if each message is 68 bytes, then the client or server need to be updated to use the same application protocol. On the other hand, if the message is 64 bytes, then the error likely exists within the server (bad logic, undefined behavior, etc.).
  • If the client is sending 64 bytes per message, then verify that the server is properly reading from the socket:
    • socket_ is not thread safe so verify there are no concurrent calls being made to it.
    • The buffer ( read_msg_.data() ) provided to boost::asio::async_read() must remain valid at least until the completion handler is called.
    • No other read operations are being performed on socket_ until async_read() has completed.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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