簡體   English   中英

將unsigned int +字符串轉換為unsigned char向量

[英]Casting an unsigned int + a string to an unsigned char vector

我正在使用NetLink套接字庫( https://sourceforge.net/apps/wordpress/netlinksockets/ ),我想以我指定的格式通過網絡發送一些二進制數據。

我計划的格式非常簡單,如下:

  • 字節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);

我假設我想為此使用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);

但是,在我試圖解釋數據的另一端時,有些例外。 我接近演員都錯了嗎? 我是否會通過轉換為未簽名的字符來丟失數據?

提前致謝。

我喜歡他們如何讓你創建一個向量( 必須使用堆,從而在不可預測的時間內執行),而不是只回到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?!
}

這使用了insert ,它應該優於使用push_back()的手寫循環。 如果rawSend拋出異常,它也不會泄漏緩沖區。

注意:字節順序必須與此連接兩端的平台匹配。 如果沒有,你需要選擇一個字節順序並堅持下去(Internet標准通常這樣做,你使用htonlhtons函數)或者你需要檢測字節順序(“本機”或“向后”)來自接收者的POV)並在“向后”修復它。

我會用這樣的東西:

#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);
}
std::vector<unsigned char>* loginRequestData = new std::vector<unsigned char>();
uint16_t opcode = OPCODE_LOGINREQUEST;
loginRequestData->push_back(opcode);

如果unsigned char長度為8位 - 在大多數系統中都是 - ,每次推送時,您將從opcode丟失高8位。 你應該收到警告。

rawSend采用vector的決定很奇怪,通用庫可以在不同的抽象級別工作。 我只能猜測是這樣的,因為rawSend會傳遞傳遞數據的副本,並保證其生命周期直到操作完成。 如果沒有,那么這只是一個糟糕的設計選擇; 再加上它通過指針獲取參數的事實......你應該將這些data視為原始內存的容器,有一些問題可以解決,但這里是你應該如何使用這種情況下的pod類型:

data->insert( data->end(), reinterpret_cast< char const* >( &opcode ), reinterpret_cast< char const* >( &opcode ) + sizeof( 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);

這也適用於任何POD類型。

是的,請使用rawSend,因為send可能需要一個NULL終止符。

通過強制轉換為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方法是最好的,最好的發送和接收方式,但布局在編譯時是固定的。 如果在運行時之前未確定消息的格式,請使用char數組:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM