gpt4 book ai didi

c++ - 通过网络构建和发送二进制数据

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:11:51 25 4
gpt4 key购买 nike

我正在为我的世界创建一个命令行客户端。可以在此处找到该协议(protocol)的完整规范:http://mc.kev009.com/Protocol .事先回答你的问题,是的,我是一个 C++ 菜鸟。

我在实现该协议(protocol)时遇到了各种问题,其中每一个都很关键。

  1. 协议(protocol)规定所有类型都是大端。我不知道应该如何检查我的数据是否为小端,如果是如何转换为大端。
  2. 字符串数据类型有点奇怪。它是一个修改后的 UTF-8 字符串,前面是一个包含字符串长度的短字符串。我不知道我应该如何将其打包到一个简单的 char[] 数组中,也不知道如何将我的简单字符串转换为修改后的 UTF-8 字符串。
  3. 即使我知道如何将我的数据转换为 big-endian 并创建修改后的 UTF-8 字符串,我仍然不知道如何将其打包到 char[] 数组中并将其作为一个包发送。我之前所做的只是简单的 HTTP 网络,它是纯 ASCII。

非常感谢解释、链接、相关函数名称和简短片段!

编辑

现在回答了 1 和 3。 1 由 user470379 在下面回答。 3 由这个很棒的线程回答,它很好地解释了我想做的事情:http://cboard.cprogramming.com/networking-device-communication/68196-sending-non-char*-data.html不过,我还不确定修改后的 UTF-8。

最佳答案

传统的做法是为每个协议(protocol)消息定义一个C++消息结构,并为其实现序列化和反序列化功能。例如Login Request可以这样表示:

#include <string>
#include <stdint.h>

struct LoginRequest
{
int32_t protocol_version;
std::string username;
std::string password;
int64_t map_seed;
int8_t dimension;
};

现在需要序列化函数。首先,它需要整数和字符串的序列化函数,因为这些是 LoginRequest 中的成员类型。

整数序列化函数需要在大端表示法之间进行转换。由于消息的成员被复制到缓冲区和从缓冲区复制,因此可以在复制时颠倒字节顺序:

#include <boost/detail/endian.hpp>
#include <algorithm>

#ifdef BOOST_LITTLE_ENDIAN

inline void xcopy(void* dst, void const* src, size_t n)
{
char const* csrc = static_cast<char const*>(src);
std::reverse_copy(csrc, csrc + n, static_cast<char*>(dst));
}

#elif defined(BOOST_BIG_ENDIAN)

inline void xcopy(void* dst, void const* src, size_t n)
{
char const* csrc = static_cast<char const*>(src);
std::copy(csrc, csrc + n, static_cast<char*>(dst));
}

#endif

// serialize an integer in big-endian format
// returns one past the last written byte, or >buf_end if would overflow
template<class T>
typename boost::enable_if<boost::is_integral<T>, char*>::type serialize(T val, char* buf_beg, char* buf_end)
{
char* p = buf_beg + sizeof(T);
if(p <= buf_end)
xcopy(buf_beg, &val, sizeof(T));
return p;
}

// deserialize an integer from big-endian format
// returns one past the last written byte, or >buf_end if would underflow (incomplete message)
template<class T>
typename boost::enable_if<boost::is_integral<T>, char const*>::type deserialize(T& val, char const* buf_beg, char const* buf_end)
{
char const* p = buf_beg + sizeof(T);
if(p <= buf_end)
xcopy(&val, buf_beg, sizeof(T));
return p;
}

对于字符串(处理 modified UTF-8 the same way as asciiz strings ):

// serialize a UTF-8 string
// returns one past the last written byte, or >buf_end if would overflow
char* serialize(std::string const& val, char* buf_beg, char* buf_end)
{
int16_t len = val.size();
buf_beg = serialize(len, buf_beg, buf_end);
char* p = buf_beg + len;
if(p <= buf_end)
memcpy(buf_beg, val.data(), len);
return p;
}

// deserialize a UTF-8 string
// returns one past the last written byte, or >buf_end if would underflow (incomplete message)
char const* deserialize(std::string& val, char const* buf_beg, char const* buf_end)
{
int16_t len;
buf_beg = deserialize(len, buf_beg, buf_end);
if(buf_beg > buf_end)
return buf_beg; // incomplete message
char const* p = buf_beg + len;
if(p <= buf_end)
val.assign(buf_beg, p);
return p;
}

还有几个辅助仿函数:

struct Serializer
{
template<class T>
char* operator()(T const& val, char* buf_beg, char* buf_end)
{
return serialize(val, buf_beg, buf_end);
}
};

struct Deserializer
{
template<class T>
char const* operator()(T& val, char const* buf_beg, char const* buf_end)
{
return deserialize(val, buf_beg, buf_end);
}
};

现在使用这些原始函数,我们可以轻松地序列化和反序列化 LoginRequest 消息:

template<class Iterator, class Functor>
Iterator do_io(LoginRequest& msg, Iterator buf_beg, Iterator buf_end, Functor f)
{
buf_beg = f(msg.protocol_version, buf_beg, buf_end);
buf_beg = f(msg.username, buf_beg, buf_end);
buf_beg = f(msg.password, buf_beg, buf_end);
buf_beg = f(msg.map_seed, buf_beg, buf_end);
buf_beg = f(msg.dimension, buf_beg, buf_end);
return buf_beg;
}

char* serialize(LoginRequest const& msg, char* buf_beg, char* buf_end)
{
return do_io(const_cast<LoginRequest&>(msg), buf_beg, buf_end, Serializer());
}

char const* deserialize(LoginRequest& msg, char const* buf_beg, char const* buf_end)
{
return do_io(msg, buf_beg, buf_end, Deserializer());
}

使用上面的辅助仿函数并将输入/输出缓冲区表示为 char 迭代器范围,只需要一个函数模板即可对消息进行序列化和反序列化。

把所有的放在一起,用法:

int main()
{
char buf[0x100];
char* buf_beg = buf;
char* buf_end = buf + sizeof buf;

LoginRequest msg;

char* msg_end_1 = serialize(msg, buf, buf_end);
if(msg_end_1 > buf_end)
; // more buffer space required to serialize the message

char const* msg_end_2 = deserialize(msg, buf_beg, buf_end);
if(msg_end_2 > buf_end)
; // incomplete message, more data required
}

关于c++ - 通过网络构建和发送二进制数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5303617/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com