在 C++ 中通过套接字传输数据

Cam*_*lsa 3 c c++ sockets serialization tcp

我目前正在做一个使用网络的项目。我必须发送一个结构

    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];
    };
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用 TCP 将 struct Packet 从一个套接字发送到另一个套接字。我试过像这样发送我的结构

      send(socket, &my_struct, sizeof(my_struct), 0);
Run Code Online (Sandbox Code Playgroud)

但它不起作用所以我试图将我的结构序列化为 char*

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));
    }
Run Code Online (Sandbox Code Playgroud)

即使当我在服务器端读取它时客户端 symply 发送 struct Header 数据已损坏 为什么数据已损坏?

客户端发送循环

    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());
}
Run Code Online (Sandbox Code Playgroud)

服务器接收循环:(通过 select() 调用函数)

  buff = new char[bav];
  socket->read(buff, bav);
  hexdump("buff", buff, bav);
Run Code Online (Sandbox Code Playgroud)

套接字->读取():

    int                     TcpSocket::read(char *buff, int len)
    {
      int                   ret;

      ret = recv(this->_socket, buff, len, 0);
      return (ret);
    }
Run Code Online (Sandbox Code Playgroud)

当我运行这些程序时:

    ./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
Run Code Online (Sandbox Code Playgroud)

Die*_*Epp 5

所以,这是错误的,它肯定会导致错误。

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);
}
Run Code Online (Sandbox Code Playgroud)

recv()不能忽略from 的返回值。

来自man 2 recv

返回值
     这些调用返回接收到的字节数,如果有错误,则返回 -1
     发生了。

     对于 TCP 套接字,返回值 0 表示对等方已关闭其一半
     连接的一侧。

那么,您收到了多少字节?无法判断,如果您丢弃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 不同,相邻的数据块可以任意连接或拆分。