我目前正在开发一个小项目:有一个通过使用标准 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);
是我的问题:我尝试以这种方式接收的“键”值是错误的 - 我的意思是我收到了一些我没有发送的东西。
这是我的怀疑:
- __bswap64 不会将网络转换为主机(小端)字节顺序
- 我误解了如何将 boost::asio::streambuf 与流一起使用
- 旧的 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。这次出了什么问题?正如您所看到的,我负责填充,并使用读取而不是运算符>>。如果您想知道为什么我逐个字段地读取该结构,这里有一个答案:服务器发送两个不同的结构,都以 msg_id 开头,一个如果成功,另一个如果失败。现在,我根本不知道如何以其他方式做到这一点。
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:
- __bswap64 does not convert network to host (little-endian) byteorder
- I misunderstood how to use boost::asio::streambuf with streams
- 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 field 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.
发布评论
评论(2)
您正在使用格式化输入,就好像发送的数据是文本一样 - 您需要非格式化输入。了解
std::istream::read
成员函数,因为您应该使用它而不是operator>>
。请注意,如果您在每次提取后都检查流状态,那么这一点会立即显而易见,就像在非一次性代码中总是应该的那样。
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 thanoperator>>
.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.
您忘记了填充。您的请求结构可能至少有编译器在第一个和第二个成员之间插入的三个字节,如下所示:
您可以使用属性(例如在 GCC 中)修复该问题(请参阅 GCC 手册):
是的,我确实错过了您正在尝试读取文本而不是二进制的事实。首先解决这个问题,稍后再被对齐/填充所困扰:)
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:
You can fix that, say in GCC, with attributes (see the GCC manual):
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 :)