2011-02-04 124 views
4

我收到的字節流,我需要分裂出來的消息,例如TCP客戶端的消息處理

Message1\nMessage2\nMessage3\nMess 

每個消息將通過「\ n」字符被附加,但是當一個完整的消息不能適合進入緩衝區時,它會獲得一部分消息,並在下一次調用recv時調用另一部分消息,這可能需要重新分配內存來追加消息。

我這樣做是否正確或將有任何更好的方式來處理消息,而不是重新分配緩衝區?

回答

4

你可以在郵件的前面加上消息的長度,然後先閱讀。然後分配一個足夠大的緩衝區來接收內容,然後recv直到它被讀取所需的字節數。

例如

int len = 0; 
if(recv(socket, reinterpret_cast<char*>(&len), sizeof(int), 0) == sizeof(int)) 
{ 
    std::vector<char> buffer; 
    buffer.resize(len); 

    int bytesRead = 0; 
    while(bytesRead < len) 
    { 
     //read as much as we can. note: byteInc may not == len-bytesRead. 
     int byteInc = recv(socket, &buffer[bytesRead], len-bytesRead, 0); 
     if(byteInc != SOCKET_ERROR) 
     { 
      bytesRead += byteInc; 
     } 
     else 
     { 
      //should probably handle this error properly 
      break; 
     } 
    } 

    //buffer now contains the complete message. 
    some_processing_function(buffer); 
} 
+4

我會認真考慮在C++應用程序中使用`std :: vector `,而不是專門的內存分配。它絕對不會出錯...... – 2011-02-04 09:45:28

1

在情況下,當傳入消息很長(〜的MB或GBS)可以使用一個const長度的緩衝器和輔助數據結構,其中,你將存儲MessageN片(N = 1,2,... )。每個recv()從一開始就填充緩衝區。然後你需要處理它的內容 - 搜索\n。如果你發現它 - 你可以提取新的消息(MessageN);如果不存在 - 將緩衝區的內容存儲在輔助數據結構(可能是向量或列表)中,並再次執行recv()。如果您發現\n並且列表不是空的 - 那麼它表示\n之前的字節實際上是MessageN的最後一部分 - 連接列表元素和此部分,然後清空列表。如果您發現\n且列表爲空,則表示從開始到\n的緩衝區中的所有字節均爲MessageN。然後,您需要在\n(直到下一個找到的\n或緩衝區的末尾)之後的列表字節中保存爲消息(N + 1)的第一部分。

0

如果您不需要讓整個消息開始處理它,也可以使用循環緩衝區(wiki,boost)。

發送第一個,當開始時不知道它的大小是好的,我可以建議你不要使用unsigned int,因爲偏轉客戶端可以讓你分配多少內存(並且有長度限制)。

2

長度分隔選項可能是您最好的選擇。它允許你在接收端分配你的緩衝區,並允許你發送包含你想要的任何字符的消息。它還可以讓你不必仔細檢查每個角色,看看你是否已經收到了消息的結尾。不幸的是,這很難實現。

我會爲您提供一些很好的代碼,可以正確執行此操作。

在接收端:

unsigned char lenbuf[4]; 

// This whole thing with the while loop occurs twice here, should probably 
// have its own function. 
{ 
    bytesRead = 0; 
    while (bytesRead < 4) { 
     //read as much as we can. note: byteInc may not == len-bytesRead. 
     int byteInc = recv(socket, &lenbuf[bytesRead], 4-bytesRead, 0); 
     if(byteInc != SOCKET_ERROR) 
     { 
      bytesRead += byteInc; 
     } 
     else 
     { 
      //should probably handle this error properly 
      break; 
     } 
    } 
} // end scope for bytesRead 

unsigned int len = ((lenbuf[0] & 0xffu) << 24) | ((lenbuf[1] & 0xffu) << 16) 
        | ((lenbuf[2] & 0xffu) << 8) | (lenbuf[3] & 0xffu); 

::std::vector<char> buffer; 
buffer.resize(len); 

{ 
    unsigned int bytesRead = 0; 
    while(bytesRead < len) 
    { 
     //read as much as we can. note: byteInc may not == len-bytesRead. 
     int byteInc = recv(socket, &buffer[bytesRead], len-bytesRead, 0); 
     if(byteInc != SOCKET_ERROR) 
     { 
      bytesRead += byteInc; 
     } 
     else 
     { 
      //should probably handle this error properly 
      break; 
     } 
    } 

    //buffer now contains the complete message. 
    some_processing_function(buffer); 
} 

在發送端:

const unsigned char lenbuf[4] = { 
     ((bytesToSend >> 24) & 0xffu), ((bytesToSend >> 16) & 0xffu), 
     ((bytesToSend >> 8) & 0xffu), (bytesToSend & 0xffu) 
    }; 

// This basic block is repeated twice and should be in a function 
{ 
    unsigned int bytesSent = 0; 
    while (bytesSend < 4) { 
     const int sentNow = send(socket, &lenbuf[bytesSent], 4-bytesSent, 0); 
     if (sentNow != SOCKET_ERROR) { 
      bytesSent += sentNow; 
     } else { 
      // Should handle this error somehow. 
      break; 
     } 
    } 
} 

{ 
    unsigned int bytesSent = 0; 
    while (bytesSent < bytesToSend) { 
     const unsigned int toSend = bytesToSend - bytesSent; 
     const int sentNow = send(socket, &byteBuf[bytesSent], toSend, 0); 
     if (sentNow != SOCKET_ERROR) { 
      bytesSent += sentNow; 
     } else { 
      // Should handle this error somehow. 
      break; 
     } 
    } 
} 

的主要問題在此發佈的其他代碼已經是它不處理事情非常好,如果你只接收部分長度,而不是整個事情。沒有什麼可以說信息不會分裂,以至於事情會在長度信息的中間被分割。

另一個問題是長度的發送方式不是CPU和編譯器不可知的。不同類型的CPU和不同的C++編譯器以不同的方式存儲它們的整數。如果發送方使用的編譯器/ CPU組合與接收方使用的編譯器/ CPU組合不同,則會導致問題。

因此,明確地將整數分解爲平臺中性方式的字符並將其重新放回到一起是最好的方法。

相關問題