2011-10-07 137 views
0

我正在使用NetLink套接字庫(https://sourceforge.net/apps/wordpress/netlinksockets/),並且我想通過網絡以我指定的格式發送一些二進制數據。將unsigned int +字符串轉換爲unsigned char向量

我已計劃的格式是非常簡單的並且是如下:

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

遇到了一些例外,不過,就當我試圖解釋數據的另一端。我是否接近全部錯誤?我會通過轉換爲無符號的字符來丟失數據嗎?

在此先感謝。

回答

1

我喜歡他們是如何讓你創建一個向量(其中必須使用堆,因此在不可預知的時間執行),而不是剛剛回落到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)並且如果「向後」修復它。

+0

而不是所有的條件編譯,你應該使用'hton_s'來將數字轉換爲網絡順序。 –

+0

有條件的編譯是用來以更簡潔的形式解釋你的選項,而不是更多段落的文本。我完全期望OP選擇一個選項並刪除其餘部分。 –

+0

這看起來不錯,但static_cast調用都拋出了一個編譯錯誤: 錯誤C2440:'static_cast':無法從'uint16_t *'轉換爲'const unsigned char *' – Josh1billion

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

大多數系統不會發出警告鑄造了下來。 –

+0

@Dietrich Epp:太糟糕了,因爲我可以預測警告會上升,因爲價值可能會被截斷。也許是時候考慮提高警戒水平。 –

+0

它與警告級別無關。由於「通常的算術轉換」,誤報的數量不可能高。例如,'(unsigned char)1 +(unsigned char)1'的類型是什麼?如果你說'unsigned char'你錯了 - 正確的答案是'int'。 –

0

這將工作:

#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工作。

0

是的,使用rawSend自發送可能會期望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); 

的結構方法是發送和接收的最好,最巧妙的方法,但佈局是固定在編譯時。 如果消息的格式沒有決定,直到運行時,使用一個字符數組:

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

'void *'爲你做的唯一事情就是避免編譯器在你隱式地從其他一些指向'void *'的指針中抱怨。 –

1

我會用這樣的:

#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

爲什麼不只是有一個超載'add'方法?或者只是製作一個包裝矢量的類?通過一個類,你可以做一個'cout'風格重載的'operator <<'。附:由於該參數是按值傳遞的,而不是指針或引用,所以'addRaw'中的'size_t len'前面不需要'const'。 –

相關問題