簡體   English   中英

當服務器以c結構發送數據時,我們可以在客戶端上將緩沖區轉換為C ++結構嗎?

[英]Can we typecast buffer into C++ structure on client when server is sending data as c structure?

我有服務器,用C編寫的客戶端進程名為NetworkServer.c和NetworkClient.c,這兩個正在使用linux套接字進行通信。 當客戶端發送如下請求以獲取以太網統計信息時,

// rxbuf - character array of 128K
// ETHERNET_DIAGNOSTIC_INFO - structure typedefed
recv(sockfd, rxbuf, sizeof(ETHERNET_DIAGNOSTIC_INFO), 0)

服務器將數據填入rxbuf(作為ETHERNET_DIAGNOSTIC_INFO,因為服務器也使用與此結構定義相同的頭文件副本)並發送數據。 客戶端收到后,將按如下方式進行類型轉換以獲取數據。

ETHERNET_DIAGNOSTIC_INFO *info = (ETHERNET_DIAGNOSTIC_INFO *) rxbuf;

結構在NetworkDiag.h中定義如下。

#ifdef __cplusplus
extern "C" {
#endif

typedef struct ETHERNET_DIAGNOSTIC_INFO
{
    uint32_t             cmdId; 
    unsigned long        RxCount[MAX_SAMPLES];
    unsigned long        TxCount[MAX_SAMPLES];
    time_t               TimeStamp[MAX_SAMPLES] ;
    char                 LanIpAddress[20];
    char                 LanMacAddress[20];
    char                 WanIpAddress[20];
    char                 LanDefaultGateway[20];
    char                 LanSubnetMask[20];
    char                 LanLease[5000];
}ETHERNET_DIAGNOSTIC_INFO;

這工作正常。

現在要求我需要創建一個應該作為客戶端工作的c ++文件(我刪除了客戶端C文件,服務器應該保留為c文件)。 我為結構定義定義了頭文件,如下所示。

struct ETHERNET_DIAGNOSTIC_INFO
{
    uint32_t             cmdId; 
    unsigned long        RxCount[MAX_SAMPLES];
    unsigned long        TxCount[MAX_SAMPLES];
    time_t               TimeStamp[MAX_SAMPLES] ;
    char                 LanIpAddress[20];
    char                 LanMacAddress[20];
    char                 WanIpAddress[20];
    char                 LanDefaultGateway[20];
    char                 LanSubnetMask[20];
    char                 LanLease[5000];
};

基本上我刪除了C ++ guard和typedef,並使用client.cpp文件中的以下代碼從服務器獲取結果。

if(recv(sockfd, rxbuf, sizeof(ETHERNET_DIAGNOSTIC_INFO), 0) > 0)
{
    ETHERNET_DIAGNOSTIC_INFO *info = reinterpret_cast<ETHERNET_DIAGNOSTIC_INFO *> (rxbuf);
}

我沒有得到正確的結果。 結構中的值放錯了位置(某些值是正確的,但很多值都放錯了位置)。 我也試過“C”型鑄造,但沒有用。

我懷疑當服務器以c結構的形式發送數據時,我們不能將緩沖區轉換為客戶端上的C ++結構。 這是正確的嗎? 任何人都可以告訴我如何解決這個問題?

這種方法存在多個問題:

  1. 服務器和客戶端計算機之間的字節順序可能不同

    然后你需要去實現數字和time_t

  2. 在服務器(c ++)和客戶端(C)上編譯的代碼之間的結構打包可能不同

    然后,您需要使用協議來發送數據,如二進制ASN,protobuf或許多其他。

  3. if(recv(sockfd, rxbuf, sizeof(ETHERNET_DIAGNOSTIC_INFO), 0) > 0)無法保證recv將讀取完全sizeof(ETHERNET_DIAGNOSTIC_INFO)個字節。

    你需要將它包裝成while循環( 代碼是樣本,可能是不可編譯的 ):

int left = sizeof(ETHERNET_DIAGNOSTIC_INFO);
char *ptr = rxbuf;
int rd;

while(left>0)
{
    rd=recv(sockfd, ptr, left, 0);
    if(rd==0)
    {
        if(left>0) return SOCKET_CLOSED_PREMATURELY;
        else return SOCKET_DONE;
    } else if(rd==-1 && errno==EAGAIN) {
        //do again
        continue; 
    } else if(rd==-1 && errno!=EAGAIN) {
       return SOCKET_ERROR;
    }
    left = left - rd;
    ptr=ptr+rd;
}

發送二進制數據的正確方法是使用protobuf或apache thrift,或ASN或自己發明一些東西。

您可以這樣做,但在嘗試時可能會遇到嚴重的重大問題:

  • 不同的編譯器和編譯器設置將以不同方式打包和對齊結構,以便針對特定處理器體系結構進行優化。 除非你使用pragma,否則絕對不能保證結構的成員將完全相鄰。
  • 不同的處理器將對整數和浮點值等事物使用不同的字節順序。 如果要在客戶端和服務器之間交換數據(反之亦然),那么您應該明確定義字節順序,然后使雙方都符合該定義,而不管本機順序如何。
  • 像unsigned long這樣的值將根據編譯器所針對的處理器體系結構具有不同的大小。 為了可靠地交換數據,您需要明確定義要傳輸的值的大小。

出於這些原因,我更喜歡編寫在交換時明確打包和解包消息的函數(或方法)。 通過這樣做,您將遭受更少看似神秘的錯誤。

我想到了許多可能的解釋:

  • C struct和C ++ struct之間的ETHERNET_DIAGNOSTIC_INFO不同打包。
  • (不太可能) rxbuf不同對齊(您不顯示此指針的來源)。 在C或C ++中無法保證讀取不在自然邊界上的intlong (例如,4字節對齊)會產生正確的結果。
  • 您的C和C ++編譯器正在針對不同的ABI進行編譯(例如分別為32位和64位)。 請注意,32位平台上的sizeof(time_t) == 4和許多64位平台上的8

所有這些問題都指向同一個方向:將struct映射到這樣的有線數據布局實際上是不可移植且有問題的。

如果你真的堅持這樣做,你需要做以下事情:

  • 使用#pragma pack指令(或更好:如果使用C ++ 11編譯器__attribute__ ((__packed__))) 即使這樣,你也可以獲得驚喜。
  • 確定您打算使用哪個字節順序,並使用htons()和朋友將所有多字節值進行字節交換。 慣例是通過TCP / IP將多字節數量設置為大端。
  • 確保調用recv()的緩沖區已對齊 - 可能是4字節邊界。

更健壯的方法是將輸入緩沖區作為字節流讀取,根據需要重建任何多字節字段。

是的,你可以因為緩沖區只是另一方發送的struct的字節表示。 處理完字節順序后,可以將緩沖區指針強制轉換為結構類型的指針。

在C ++中,您可以編寫例如ETHERNET_DIAGNOSTIC_INFO* NewPtr = reinterpret_cast<ETHERNET_DIAGNOSTIC_INFO*>(buffer); 除非您運行不能理解C ++ 11語法的舊C ++編譯器,否則這將執行您想要的操作。 但是,根據您的編譯器,錯誤可能來自填充數據。

如果你定義位字段並在兩邊打包結構,你會沒事的。 詢問您是否需要幫助,但谷歌是您的朋友。

我懷疑當服務器以c結構的形式發送數據時,我們不能將緩沖區轉換為客戶端上的C ++結構。 這是正確的嗎?

編輯:您可以將任何編程語言生成的任何二進制數據轉換為程序中可讀的代碼段。 畢竟,它都是關於位和字節的。 因此,您可以將任何程序中的任何數據轉換為任何其他程序中的任何數據。 你能快速打印兩側的尺寸(ETHERNET_DIAGNOSTIC_INFO),看它們是否匹配?

暫無
暫無

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

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