2015-02-23 95 views
0

我試圖通過winsock UDP套接字從客戶端發送一個結構到服務器,但我沒有收到已發送的相同字節,或者至少不是正確地重組結構。C++ Winsock結構發送/ recv

struct Header 
{ 
    uint16_t sender; 
    uint16_t number; 
    uint16_t packetSize; 
    uint16_t type; 
}; 

發送的結構:

Header header; 
header.sender = 1; 
header.number = 2; 
header.packetSize = 3; 
header.type = 4; 

char buffer[sizeof(Header)]; 
memcpy(buffer, &header, sizeof(Header)); 

int bytesSent = sendto(sendSocket_, data, sizeof(Header), 0, (const sockaddr*)&sendSocketAddr_, sizeof(sendSocketAddr_)); 
    if (bytesSent > 0) 
    { 
    cout << bytesSent << endl; 
    } 

接收結構:

char *data = new char[sizeof(Header)]; 
    int bytesRecv = recv(listenSocket_, data, sizeof(Header), 0); 
    if (bytesRecv > 0) 
    { 
    cout << bytesRecv << endl; 
    Header header; 
    memcpy(&header, data, sizeof(data)); 
    cout << header.sender << ": " << header.number << ": " << header.packetSize << ": " << header.type << endl; 
} 

字節相同量的被接收爲被髮送,然而一旦複製回上唯一的服務器側的新結構前兩個值是正確的,其餘的不是。

在這種情況下,結構包含1,2,52428,52428時收到。

在發送/接收過程中是否有錯誤,或者是在另一端創建結構?

+1

客戶端和服務器是否使用相同的選項使用相同的編譯器編譯?如果不是,則結構中字段的對齊可能不同。 – Eelke 2015-02-23 07:49:45

+0

是的,客戶端和服務器都使用相同的編譯器使用相同的選項進行編譯。 – bic 2015-02-23 07:53:38

+0

我不確定爲緩衝區數組分配了正確的大小。你應該在memcpy中使用'sizeof(header)'(變量的大小,而不是類型)。在發送之前打印緩衝區的內容以確保您發送的內容是正確的。 – 2015-02-23 08:07:08

回答

2

首先,在網絡上發送一個原始結構是高度非便攜式:

  • 可以具有發送機和接收機
  • 之間字節序的差異可以哈瓦發送者和接收者

之間的對準的差異除了特殊情況下,您可以確定發件人和收件人共享相同的體系結構,操作系統和編譯器,絕不會那樣做

正常的方式是使用可移植的格式,ASCII或者二進制,但你必須定義一個非模糊的表示。例如你用4 uint16_t

包裝:

char buffer[8]; 
buffer[0] = header.sender >> 8; /* or (header.sender >> 8) & 0xFF if longer ...*/ 
buffer[1] = header.sender & 0xFF; 
buffer[2] = header.number >> 8; 
... 

開箱:

header.sender = (buffer[0] << 8) | buffer[1]; 
header.number = (buffer[2] << 8) | buffer[3]; 
... 

但是,如果你有不同的體系結構的問題,你不會得到前兩值。您目前的問題是要簡單得多:在接收代碼您有:

char *data = new char[sizeof(Header)]; 
.... 
    memcpy(&header, data, sizeof(data)); 

而且sizeof(data)sizeof(char *),即在32位機器4個字節,而sizeof(Header)爲4×2 = 8個字節。

您應該改爲:

memcpy(&header, data, sizeof(Header)); 

順便說一句,你能避免memcpy部分。

發件人部分:

int bytesSent = sendto(sendSocket_, &header, sizeof(Header), 0, (const sockaddr*)&sendSocketAddr_, sizeof(sendSocketAddr_)); 

有效的,因爲第二個參數sendtoconst void *和轉化爲void *是自動的。

接收機:

int bytesRecv = recv(listenSocket_, &header, sizeof(Header), 0); 

因爲第二個參數recv實際上是一個void *

+0

您也可以有填充差異或數據類型大小差異。所有這些都可以根據硬件,操作系統,編譯器,編譯器版本,編譯器標誌,編譯器選項,周圍的#pragmas,...更不用說源代碼本身。使用這種技術的唯一安全方法是確保兩個對等體都是用相同的目標文件構建的。 – EJP 2015-02-23 09:06:23

0

您可以使用:#pragma pack(show)將校準打印爲警告並確保其一致。

如果是有區別的使用#pragma pack(n)這樣的:

#pragma pack(push) 
#pragma pack(1) 
struct Header{ 
    ... 
}; 
#pragma pack(pop) 

您不必memcpy的結構緩衝。你可以直接使用你的頭對象。