c++中通过套接字实现数据

Data through Sockets in C++

本文关键字:实现 数据 套接字 c++      更新时间:2023-10-16

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

    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从一个套接字发送结构包到另一个套接字。我试过像这样发送struct

      send(socket, &my_struct, sizeof(my_struct), 0);

,但它不工作,所以我试图序列化我的结构成一个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));
    }

甚至当客户端只是发送一个结构头数据是损坏的,当我读取它的服务器端为什么数据损坏了?

客户端发送循环

    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());
}

server recv loop: (function call by select())

  buff = new char[bav];
  socket->read(buff, bav);
  hexdump("buff", buff, bav);

插座->阅读():

    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

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

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:

<>之前返回值这些调用返回接收到的字节数,如果出现错误则返回-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不同,相邻的数据块可以任意地连接或分割。

  1. 每次只需要掩码低8位:

    buffer[3] = (arg >> 24) & 0xff;
    buffer[2] = (arg >> 16) & 0xff;
    buffer[1] = (arg >> 8) & 0xff;
    buffer[0] = (arg) & 0xff;
    
  2. 反序列化

  3. 时也这样做

如果我是你,我就不会重新发明轮子。有很多文档完备且经过测试的库/协议可以满足你的需求。一个我突然想到的小清单:

  • 提高序列化
  • Google Protocol Buffers
  • JSON XML>CORBA