boost::asio与标准C套接字接口之间的合作

Cooperation between boost::asio and standard C socket interface

本文关键字:之间 接口 asio 标准 boost 套接字      更新时间:2023-10-16

我目前正在做一个小项目:有一个通过标准C接口实现的UDP发送一些字符串的协议。

虽然它工作得很好,但我想用一些更复杂的C++重写它(考虑一下练习)。

目前是这样的:客户端想要这个字符串,所以它发送以下struct:

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

在新的实现中,我想使用boost::asio,所以在服务器中我有以下代码:

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

我的问题是:我试图通过这种方式接收的"密钥"值是错误的——我的意思是我收到了一些我没有发送的东西。

以下是我的怀疑:

  1. __bswap64不转换网络到主机(little-endian)字节顺序
  2. 我误解了如何将boost::asio::streambuf与streams一起使用
  3. 旧的C接口和boost之间有些不兼容(但我不这么认为因为我发现boost函数只是它的包装器)

编辑:嗯,他们说"在你结束之前不要赞美福特"。现在,我在代码的另一个地方遇到了一个非常相似的问题。我有一个下面的结构,它是作为对上述请求的回复发送的:

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

以及以下代码:http://pastebin.com/eTzq6AWQ.不幸的是,它还有另一个错误,我又读到了一些我没有发送的东西——replyMsg.parts和replyMsg.index总是0,尽管旧的实现说它们是3和10。这次怎么了?正如您所看到的,我负责填充,并使用read而不是运算符>>。如果你想知道我为什么一个字段接一个字段地阅读这个结构,这里有一个答案:服务器发送两个不同的结构,都以msg_id开头,如果成功,则发送一个,如果失败,则发送另一个。现在,我根本不知道该怎么做。

您使用的是格式化输入,就好像发送的数据是文本的一样——您需要un格式化输入。请阅读有关std::istream::read成员函数的信息,因为它是您应该使用的函数,而不是operator>>

请注意,如果您在每次提取后都检查流状态,那么这一点就会非常明显,就像在非一次性代码中一样。

您忘记了填充。您的请求结构可能由编译器在第一个和第二个成员之间插入了至少三个字节,如下所示:

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

你可以用属性(参见GCC手册)修复这个问题,比如在GCC中:

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

是的,我确实错过了你试图阅读文本而不是二进制的事实。首先解决这个问题,稍后被对齐/填充所咬:)