C++ Winsock结构发送/ recv

问题描述:

我试图通过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

首先,在网络上发送一个原始结构是高度非便携式:

  • 可以具有发送机和接收机
  • 之间字节序的差异可以哈瓦发送者和接收者

之间的对准的差异除了特殊情况下,您可以确定发件人和收件人共享相同的体系结构,操作系统和编译器,绝不会那样做

正常的方式是使用可移植的格式,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

您可以使用:#pragma pack(show)将校准打印为警告并确保其一致。

如果是有区别的使用#pragma pack(n)这样的:

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

您不必memcpy的结构缓冲。你可以直接使用你的头对象。