C++ 强制转换无符号int+;无符号字符向量的字符串

C++ 强制转换无符号int+;无符号字符向量的字符串,c++,sockets,casting,char,opcode,C++,Sockets,Casting,Char,Opcode,我正在使用NetLink套接字库(),我希望以我指定的格式通过网络发送一些二进制数据 我计划的格式非常简单,如下所示: 字节0和1:uint16_t类型的操作码(即,无符号整数的长度始终为2字节) 字节2以后:任何其他必要的数据,如字符串、整数、它们的组合等。。另一方将根据操作码解释该数据。例如,如果操作码为0,表示“登录”,则该数据将由一个字节的整数组成,告诉您用户名的长度,后跟一个包含用户名的字符串,后跟一个包含密码的字符串。对于操作码1“发送聊天信息”,此处的全部数据可能只是聊天信息的字

我正在使用NetLink套接字库(),我希望以我指定的格式通过网络发送一些二进制数据

我计划的格式非常简单,如下所示:

  • 字节0和1:uint16_t类型的操作码(即,无符号整数的长度始终为2字节)

  • 字节2以后:任何其他必要的数据,如字符串、整数、它们的组合等。。另一方将根据操作码解释该数据。例如,如果操作码为0,表示“登录”,则该数据将由一个字节的整数组成,告诉您用户名的长度,后跟一个包含用户名的字符串,后跟一个包含密码的字符串。对于操作码1“发送聊天信息”,此处的全部数据可能只是聊天信息的字符串

以下是图书馆为我提供的发送数据的工具:

void send(const string& data);
void send(const char* data);
void rawSend(const vector<unsigned char>* data);
void发送(常量字符串和数据);
无效发送(常量字符*数据);
void rawSend(常量向量*数据);
我假设我想用rawSend()来实现这个。。但是rawSend()接受未签名字符,而不是指向内存的void*指针?如果我尝试将某些类型的数据强制转换为一个无符号字符数组,这里是否会有一些数据丢失?如果我错了,请纠正我。。但如果我是对的,这是否意味着我应该寻找另一个支持真正二进制数据传输的库

假设这个库确实为我的目的服务,我将如何准确地将各种数据类型转换并连接到一个std::vector中?我试过的是这样的:

#define OPCODE_LOGINREQUEST 0

std::vector<unsigned char>* loginRequestData = new std::vector<unsigned char>();
uint16_t opcode = OPCODE_LOGINREQUEST;
loginRequestData->push_back(opcode);
// and at this point (not shown), I would push_back() the individual characters of the strings of the username and password.. after one byte worth of integer telling you how many characters long the username is (so you know when the username stops and the password begins)
socket->rawSend(loginRequestData);
#define OPCODE_LOGINREQUEST 0 
#define OPCODE_MESSAGE 1

void addRaw(std::vector<unsigned char> &v, const void *data, const size_t len)
{
    const unsigned char *ptr = static_cast<const unsigned char*>(data);
    v.insert(v.end(), ptr, ptr + len);
}

void addUint8(std::vector<unsigned char> &v, uint8_t val)
{
    v.push_back(val);
}

void addUint16(std::vector<unsigned char> &v, uint16_t val)
{
    val = htons(val);
    addRaw(v, &val, sizeof(uint16_t));
}

void addStringLen(std::vector<unsigned char> &v, const std::string &val)
{
    uint8_t len = std::min(val.length(), 255);
    addUint8(v, len);
    addRaw(v, val.c_str(), len);
}

void addStringRaw(std::vector<unsigned char> &v, const std::string &val)
{
    addRaw(v, val.c_str(), val.length());
}

void sendLogin(const std::string &user, const std::string &pass)
{
    std::vector<unsigned char> data(
        sizeof(uint16_t) +
        sizeof(uint8_t) + std::min(user.length(), 255) +
        sizeof(uint8_t) + std::min(pass.length(), 255)
    );
    addUint16(data, OPCODE_LOGINREQUEST);
    addStringLen(data, user);
    addStringLen(data, pass);
    socket->rawSend(&data);
}

void sendMsg(const std::string &msg)
{
    std::vector<unsigned char> data(
      sizeof(uint16_t) +
      msg.length()
    );
    addUint16(data, OPCODE_MESSAGE);
    addStringRaw(data, msg);
    socket->rawSend(&data);
}
#定义操作码_登录请求0
std::vector*loginRequestData=new std::vector();
uint16\u t操作码=操作码\u登录请求;
loginRequestData->推回(操作码);
//在这一点上(未显示),我将把用户名和密码字符串中的单个字符向后推。。一个字节的整数告诉您用户名的长度(这样您就知道用户名何时停止,密码何时开始)
socket->rawSend(loginRequestData);
但是,在另一端,当我试图解释数据时,遇到了一些异常。我是不是走错路了?通过强制转换到未签名字符是否会丢失数据

提前感谢。

这将起作用:

std::vector<unsigned char>* loginRequestData = new std::vector<unsigned char>();
uint16_t opcode = OPCODE_LOGINREQUEST;
loginRequestData->push_back(opcode);
#define OPCODE_LOGINREQUEST 0

std::vector<unsigned char>* loginRequestData = new std::vector<unsigned char>();
uint16_t opcode = OPCODE_LOGINREQUEST;
unsigned char *opcode_data = (unsigned char *)&opcode;
for(int i = 0; i < sizeof(opcode); i++)
    loginRequestData->push_back(opcode_data[i]);
socket->rawSend(loginRequestData);
#定义操作码_登录请求0
std::vector*loginRequestData=new std::vector();
uint16\u t操作码=操作码\u登录请求;
无符号字符*操作码\数据=(无符号字符*)&操作码;
对于(int i=0;ipush_back(操作码_数据[i]);
socket->rawSend(loginRequestData);

这也适用于任何POD类型。

是的,使用rawSend,因为send可能需要空终止符

你不会因为施法到char而失去任何东西,而不是void*。记忆就是记忆。除了RTTI信息之外,类型永远不会存储在C++中的内存中。您可以通过强制转换到操作码指示的类型来恢复数据

如果您可以在编译时决定所有发送的格式,我建议使用结构来表示它们。我以前在专业领域做过,这是清楚地存储各种消息格式的最好方法。而且在另一边拆包非常容易;只需根据操作码将原始缓冲区强制转换到结构中

struct MessageType1 {
    uint16_t opcode;
    int myData1;
    int myData2;
};

MessageType1 msg;

std::vector<char> vec;
char* end = (char*)&msg + sizeof(msg);
vec.insert( vec.end(), &msg, end );

send(vec);
struct MessageType1{
uint16操作码;
int myData1;
int myData2;
};
MessageType1消息;
std::vec;
char*end=(char*)&msg+sizeof(msg);
插入向量(vec.end(),&msg,end);
发送(vec);
结构方法是最好、最简洁的发送和接收方式,但布局在编译时是固定的。 如果直到运行时才决定消息的格式,请使用字符数组:

char buffer[2048];

*((uint16_t*)buffer) = opcode;
// now memcpy into it
// or placement-new to construct objects in the buffer memory

int usedBufferSpace = 24; //or whatever

std::vector<char> vec;
const char* end = buffer + usedBufferSpace;
vec.insert( vec.end(), buffer, end );

send(&buffer);
char缓冲区[2048];
*((uint16_t*)缓冲区)=操作码;
//现在请记住
//或放置新对象以在缓冲内存中构造对象
int usedBufferSpace=24//或者别的什么
std::vec;
const char*end=buffer+usedBufferSpace;
vec.insert(vec.end(),buffer,end);
发送(&缓冲区);

我喜欢他们如何让你创建一个向量(它必须使用堆,因此在不可预测的时间内执行),而不是回到C标准
(const void*buffer,size\t len)
元组,它与所有东西都兼容,在性能上是无与伦比的。哦,好吧

你可以试试这个:

void send_message(uint16_t opcode, const void* rawData, size_t rawDataSize)
{
    vector<unsigned char> buffer;
    buffer.reserve(sizeof(uint16_t) + rawDataSize);
#if BIG_ENDIAN_OPCODE
    buffer.push_back(opcode >> 8);
    buffer.push_back(opcode & 0xFF);
#elseif LITTLE_ENDIAN_OPCODE
    buffer.push_back(opcode & 0xFF);
    buffer.push_back(opcode >> 8);
#else
    // Native order opcode
    buffer.insert(buffer.end(), reinterpret_cast<const unsigned char*>(&opcode), 
        reinterpret_cast<const unsigned char*>(&opcode) + sizeof(uint16_t));
#endif
    const unsigned char* base(reinterpret_cast<const unsigned char*>(rawData));
    buffer.insert(buffer.end(), base, base + rawDataSize);
    socket->rawSend(&buffer); // Why isn't this API using a reference?!
}
void发送消息(uint16操作码,const void*rawData,size\t rawDataSize)
{
向量缓冲区;
buffer.reserve(sizeof(uint16_t)+rawDataSize);
#如果是BIG_ENDIAN_操作码
缓冲区。推回(操作码>>8);
buffer.push_back(操作码&0xFF);
#elseif LITTLE_ENDIAN_操作码
buffer.push_back(操作码&0xFF);
缓冲区。推回(操作码>>8);
#否则
//本机顺序操作码
buffer.insert(buffer.end(),reinterpret_cast(&操作码),
重新解释铸造(操作码)+尺寸(uint16_t);
#恩迪夫
常量无符号字符*基(重新解释强制转换(原始数据));
insert(buffer.end(),base,base+rawDataSize);
socket->rawSend(&buffer);//为什么这个API不使用引用?!
}
这使用了
insert
,它应该比使用
push_back()
的手写循环优化得更好。如果
rawSend
抛出异常,它也不会泄漏缓冲区


注意:字节顺序必须与此连接两端的平台相匹配。如果没有,您需要选择一个字节顺序并坚持使用它(互联网标准通常会这样做,并且您使用
htonl
htons
函数),或者您需要检测字节顺序(“本机”或从接收器的POV中“向后”)并在“向后”时修复它。

我会使用这样的方法:

#define OPCODE_LOGINREQUEST 0

std::vector<unsigned char>* loginRequestData = new std::vector<unsigned char>();
uint16_t opcode = OPCODE_LOGINREQUEST;
loginRequestData->push_back(opcode);
// and at this point (not shown), I would push_back() the individual characters of the strings of the username and password.. after one byte worth of integer telling you how many characters long the username is (so you know when the username stops and the password begins)
socket->rawSend(loginRequestData);
#define OPCODE_LOGINREQUEST 0 
#define OPCODE_MESSAGE 1

void addRaw(std::vector<unsigned char> &v, const void *data, const size_t len)
{
    const unsigned char *ptr = static_cast<const unsigned char*>(data);
    v.insert(v.end(), ptr, ptr + len);
}

void addUint8(std::vector<unsigned char> &v, uint8_t val)
{
    v.push_back(val);
}

void addUint16(std::vector<unsigned char> &v, uint16_t val)
{
    val = htons(val);
    addRaw(v, &val, sizeof(uint16_t));
}

void addStringLen(std::vector<unsigned char> &v, const std::string &val)
{
    uint8_t len = std::min(val.length(), 255);
    addUint8(v, len);
    addRaw(v, val.c_str(), len);
}

void addStringRaw(std::vector<unsigned char> &v, const std::string &val)
{
    addRaw(v, val.c_str(), val.length());
}

void sendLogin(const std::string &user, const std::string &pass)
{
    std::vector<unsigned char> data(
        sizeof(uint16_t) +
        sizeof(uint8_t) + std::min(user.length(), 255) +
        sizeof(uint8_t) + std::min(pass.length(), 255)
    );
    addUint16(data, OPCODE_LOGINREQUEST);
    addStringLen(data, user);
    addStringLen(data, pass);
    socket->rawSend(&data);
}

void sendMsg(const std::string &msg)
{
    std::vector<unsigned char> data(
      sizeof(uint16_t) +
      msg.length()
    );
    addUint16(data, OPCODE_MESSAGE);
    addStringRaw(data, msg);
    socket->rawSend(&data);
}
#定义操作码_登录请求0
#定义操作码_消息1
void addRaw(标准::向量和向量,常量void*数据,常量大小)
{
const unsigned char*ptr=static_cast(数据);
v、 插入(v.end(),ptr,ptr+len);
}
虚空阿杜因