开发者

Cooperation between boost::asio and standard C socket interface

I'm currently working on a small project: there's a protocol for sending some strings via UDP implemented with standard C interface.

Although it works pretty fine, I'd like to rewrite it with some more sophisticated C++ (consider it exercise).

Currently it's something like that: A client wants that string so it sends the following struct:

struct request {
  uint8_t msg_type;// == 1
  uint64_t key; // generated randomly to identify each request
}

In new implementation, I want to use boost::asio so in server I have a following piece of code:

boost::asio::io_service io_service;
boost::asio::ip::udp::endpoint client_endpoint;
boost::asio::ip::udp::socket socket(io_service,
        boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(),
        m_serverPort));
boost::asio::streambuf sb;
boost::asio::streambuf::mutable_buffers_type mutableBuf =
        sb.prepare(sizeof(request));
size_t received_bytes = socket.receive_from(mutableBuf, client_endpoint);
sb.commit(received_bytes);

request r;
std::istream is(&sb);
is >> msg_type;
is >> key;
key = __bswap64(key); // I'm using network byteorder for numbers sent with this protocol
                      // and there's no ntohll function on Snow Leopard (at least I can't
                      // find one)
sb.consume(received_bytes);

And here's my problem: the "key" value which I try to receive this way is wrong - I mean I get something that I did not send.

Here are my suspicions:

  1. __bswap64 does not convert network to host (little-endian) byteorder
  2. I misunderstood how to use boost::asio::streambuf with streams
  3. There's some incompatibility between old C interface and boost (but I don't think so cause I've found out that boost functions are just wrappers for it)

EDIT: hmm they say "don't praise a ford till you get over". Now I have a very similar issue in another place of my code. I have a following struct which is sent as a reply for request metioned above:

struct __attribute__ ((packed)) CITE_MSG_T
{
        uint8_t msg_id;
        uint64_t key; // must be the same as in request
        uint16_t index; // part number
        uint16_t parts; // number of all parts
    CITE_PART_T text; // message being sent
};

//where CITE_PART_T is:
struct __attribute__ ((packed)) CITE_PART_T
{ 
        uint16_t data_length;
        char* data;
};

and following piece of code: http://pastebin.com/eTzq6AWQ. Unfortunately there's another bug in it and again I read something I haven't sent - replyMsg.parts and replyMsg.index is always 0 although old implementation says they're for example 3 and 10. What's wrong this time? As you can see I take care of padding and I use read instead of operator>>. If you wonder why I read that struct field by fie开发者_如何学JAVAld here's an answer: A server sends two different structures, both beginning with msg_id, one if it succeceeds and another if it fails. Right now, I simply have no idea how to do it other way.


You're using formatted input, as though the data being sent were textual -- you need unformatted input. Read about the std::istream::read member function, as it's what you should be using rather than operator>>.

Note that this would have been immediately obvious if you had been checking the stream state after each extraction, as one always should in non-throw-away code.


You forgot about padding. Your request structure probably has at least three bytes inserted by the compiler between the first and the second member, as in:

struct request {
    uint8_t msg_type;
    char __pad__[3]; // or 7 on 64-bit machine.
    uint64_t key;
};

You can fix that, say in GCC, with attributes (see the GCC manual):

struct __attribute__ ((__packed__)) request { ...

And yes, I did miss the fact that you are trying to read text instead of binary. Fix that first, get bitten by alignment/padding later :)

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜