2012-12-27 39 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是允许的,但我已经使用它们,我不知道如何 –