2012-12-27 37 views
3

我目前正在使用網絡的項目。我必須發送一個結構通過C++中的套接字的數據

struct Header 
    { 
    uint32_t magic; 
    uint32_t checksum; 
    uint32_t timestamp; 
    uint16_t commandId; 
    uint16_t dataSize; 
    }; 

    struct Packet 
    { 
    struct Header header; 
    char  data[128]; 
    }; 

我試圖從一個套接字發送結構數據包到另一個使用TCP。我試圖把我的結構一樣,

 send(socket, &my_struct, sizeof(my_struct), 0); 

但它不工作,所以我已經試過*

unsigned char    *Serialization::serialize_uint32(unsigned char *buffer, uint32_t arg) 
{ 
buffer[3] = (arg >> 24); 
buffer[2] = (arg >> 16); 
buffer[1] = (arg >> 8); 
buffer[0] = (arg); 
return (buffer + sizeof(uint32_t)); 
} 

unsigned char    *Serialization::serialize_uint16(unsigned char *buffer, uint16_t arg) 
{ 
buffer[1] = (arg >> 8); 
buffer[0] = (arg); 
return (buffer + sizeof(uint16_t)); 
} 

    unsigned char       *Serialization::deserialize_uint32(unsigned char *buffer, uint32_t *arg) 
    { 
     memcpy((char*)arg, buffer, sizeof(uint32_t)); 
     return (buffer + sizeof(uint32_t)); 
    } 

    unsigned char       *Serialization::deserialize_uint16(unsigned char *buffer, uint16_t *arg) 
    { 
    memcpy((char*)arg, buffer, sizeof(uint16_t)); 
    return (buffer + sizeof(uint16_t)); 
    } 

即使在客戶端symply發送結構序列化我的結構到一個char當我讀取服務器端時,標頭數據已損壞 爲什麼數據損壞?

客戶端發送環路

TcpSocket      tcp; 
    Packet      p; 
    std::stringstream    ss; 
    int       cpt = 0; 
    int       ret = 0; 
    char       *serialized; 

    tcp.connectSocket("127.0.0.1", 4242); 
    while (getchar()) 
    { 
     ss.str(""); 
     ss.clear(); 
    ss << cpt++; 
    p.header.magic = 0; 
    p.header.checksum = 1; 
    p.header.timestamp = 2; 
    p.header.commandId = 3; 
    p.header.dataSize = ss.str().length(); 
    memset(p.data, 0, 128); 
    memcpy(p.data, ss.str().c_str(), ss.str().length()); 
    serialized = new char[sizeof(Header) + ss.str().length()]; 
    bzero(serialized, sizeof(Header) + ss.str().length()); 
    Serialization::serialize_packet(serialized, p); 
    hexDump("serialized", serialized+1, sizeof(Header) + ss.str().length()); 
    ret = tcp.write(serialized+1, sizeof(Header) + ss.str().length()); 
} 

服務器的recv循環:

buff = new char[bav]; 
    socket->read(buff, bav); 
    hexdump("buff", buff, bav); 

(通過select()調用fonction)插座 - >閱讀():

int      TcpSocket::read(char *buff, int len) 
    { 
     int     ret; 

     ret = recv(this->_socket, buff, len, 0); 
     return (ret); 
    } 

當我運行那些方案:

./server 
    [Server] new connexion :: [5] 
    recv returns : 17 
    buff serialized: 
     0000 00 00 00 00 14 00 00 00 1c 00 00 00 1a 00 00 00 ................ 
     0010 1b 

    ./client 
    serialized data: 
     0000 00 00 00 00 00 00 01 00 00 00 02 00 03 00 01 30 ...............0 
     0010 00 
    send returns : 17 
+1

我們是否假定客戶端是用適當的反向算法解壓縮的(注意你應該使用'unsigned char'作爲你的包緩衝區)。另外,第一種情況下的結構打包以及機器端格式*將起作用。 – WhozCraig

+0

對於uint16和uint32,您可以使用ntohl,ntohs,htonl和htons。簡短的答案是這樣的:您必須在字節級精確定義數據格式,並且在發送和接收時正確地將數據格式轉換爲「有線格式」或從該格式轉換而來。 –

+0

@DavidSchwartz我同意,但是當我在幾天前提出這個問題的時候,由於它不在「標準」中,所以我總結了一個問題。仍然從那一個刺痛(我仍然會用POSIX函數來做,無論順便說一句,就像你可能會那樣)。 – WhozCraig

回答

4

所以,這是錯誤的,它肯定會引起錯誤。

buff = new char[bav]; 
socket->read(buff, bav); 
hexdump("buff", buff, bav); 
socket->read() : 

int TcpSocket::read(char *buff, int len) 
{ 
    return recv(this->_socket, buff, len, 0); 
} 

recv()的返回值不能被忽略。

man 2 recv

 
RETURN VALUES 
    These calls return the number of bytes received, or -1 if an error 
    occurred. 

    For TCP sockets, the return value 0 means the peer has closed its half 
    side of the connection. 

那麼,有多少字節你收到?如果您丟棄recv()的結果,則無法判斷。也許recv()失敗,你永遠不會發現,如果你沒有檢查返回值。也許它只填滿了你的緩衝區的一部分。 您必須檢查recv()的返回碼。這是編寫使用TCP的程序時人們犯的頭號錯誤。

你需要改變你的代碼來處理以下情況:

  1. recv()呼叫可以完全填滿你的緩衝區。

  2. recv()調用可能會部分填充緩衝區。

  3. recv()呼叫可能返回0,表示發送方已關閉連接。

  4. recv()呼叫可能指示EINTR,因爲它被系統調用中斷。

  5. recv()呼叫可能指示ECONNRESET,因爲發件人突然關閉了連接或已經消失。

  6. recv()調用可能會遇到一些其他錯誤。

記住:使用TCP的時候,只是因爲你send() 16個字節並不意味着其他同行將recv() 16字節 - 可以分解成塊。 TCP是一個流協議。與UDP不同,可以任意連接或拆分相鄰的數據塊。

+0

@WhozCraig:我認爲沒有粗體 –

+0

我的客戶端發送的返回值是17,而我的服務器recv返回值是17:( –

+0

@CamilleTolsa:我想你誤會了,沒有意義告訴我返回值是什麼,因爲**返回值可能不同每一次**你必須修改你的程序,使它對於'recv()'可以給出的每一個返回值都正確的行爲 –

2
  1. 您需要每次都掩蓋僅低8位:

    buffer[3] = (arg >> 24) & 0xff; 
    buffer[2] = (arg >> 16) & 0xff; 
    buffer[1] = (arg >> 8) & 0xff; 
    buffer[0] = (arg) & 0xff; 
    
  2. 做同樣的,當你反序列化

+0

你能解釋我爲什麼請 –

+0

因爲'緩衝區'可能會被簽名。 –

+0

我已經嘗試過但仍不能正常工作 –

0

如果我是你,我不會另起爐竈。在那裏有很多有據可查的文檔和測試過的庫/協議,完全符合您的要求。這只是我想到一個小清單:

+0

我知道,但我正在學校項目,我們不允許使用任何圖書館,我們不能使用文本協議:( –

+0

請定義'圖書館'?C圖書館是一個圖書館嗎?(例如你允許使用htonX/ntohX函數?) –

+0

我們被允許使用libc htons/htonl&ntohX是允許的,但我已經使用它們,我不知道如何 –