Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/145.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ Boost ASIO-TCP数据包的标头和正文无效_C++_Sockets_Network Programming_Boost Asio - Fatal编程技术网

C++ Boost ASIO-TCP数据包的标头和正文无效

C++ Boost ASIO-TCP数据包的标头和正文无效,c++,sockets,network-programming,boost-asio,C++,Sockets,Network Programming,Boost Asio,我正在使用Boost ASIO作为我的项目的TCP网络通信解决方案 这是我的密码: Client.h: namespace Vibranium{ class Client: public std::enable_shared_from_this<Client> { public: Client(tcp::socket socket) : socket(std::move(socket)) { }

我正在使用Boost ASIO作为我的项目的TCP网络通信解决方案

这是我的密码:

Client.h

namespace Vibranium{
    class Client: public std::enable_shared_from_this<Client>
    {
    public:
        Client(tcp::socket socket)
        : socket(std::move(socket))
        {
        }
        void start();
        int connectionId;
        tcp::socket socket;
        void Send(ServerOpcode serverOpcode, const std::string& message);

    private:
        void read_header();
        void read_body();
        Packet _packet;
    };
}
#endif //VIBRANIUM_CORE_CLIENT_H
为了避免这个问题被代码淹没,这里是
Packet.h
:和
Packet.cpp
:的内容:

我所能看到的是,不知何故,头和体都被传输了,但它们没有正确解析。为什么我在服务器上收到如此未格式化的输出?我的错误在哪里?如何纠正?

分析 让我们一步一步地通过代码来了解发生了什么。在您的客户机中,您正在初始化大小为
8+1024
的缓冲区,您可以将一些数据填入其中:

char data_[header_length + body_length];
...
sprintf(data_, "%s %s", header_, body_);
您可以看到写入
数据的数据小于整个缓冲区的大小。由于您没有对
数据进行零初始化
,因此它的剩余部分将填充随机数据(之前堆栈上该位置发生的任何情况)。字符串
“%s%s”
是一个以零结尾的字符串,这意味着在内存中它后面将紧跟一个结束的零字节。因此,当
sprintf
将标题和正文插入此格式化字符串时,您将再次获得一个以零结尾的字符串,该字符串将写入
数据。因此,
数据
缓冲区将包含:

[--- header ---] <space> [--- body---] <zero-byte> [--- random data ---]
因此,客户机中的打印不会显示缓冲区末尾的随机数据

然后将整个缓冲区发送到服务器:

New Connection (ID: 1)
Header: 
1
 I am 
Body: 
testing here!!!
hP��U��TK���U�U�� K�h
�*�� K�) �J�Uh
 0�y���x������������RK�Px���� K��RK�`x��\TUK��TK��;�#K��TJ�d�"K��XUK�v\TUK� �TK�{�|@X#K� �TJ�`FK��XUK��\TUK� �TK��G�|X^#K� �TJ�4AK��XUK�:\TUK��TK�z���M$K��TJ���#K��XUK��
u��u�z��x�TK��u��<}(K�@u��Pu���aUK��TJ��TK������TJ��TK�x�TK���U�{��������UK��M$K��TK����@K��{����UK�
Invalid header sent!
size_t request_length = sizeof(data_)/sizeof(*data_);
boost::asio::write(s, boost::asio::buffer(data_, request_length));
在服务器中,您读取的缓冲区大小等于标头的大小:

 boost::asio::async_read(socket,boost::asio::buffer(_packet.data_, _packet.header_length), ...);
因此,这将处理您在客户机中写入的缓冲区的第一个
头\u长度
字节。如上所述,缓冲区的第一个
头\u长度
字节可以包含分隔空间和正文的某些部分,具体取决于您在客户端中写入缓冲区的头数据的长度。因此,当您使用此行在服务器中打印标题时,您将看到部分正文数据:

std::cout.write(_packet.data_, _packet.header_length);
然后通过读取
body\u length
多个字节继续读取正文。这将是您在客户端中写入的剩余字节。因此,它将包含主体的剩余部分(减去已作为头的一部分处理的部分)、零字节,然后是随机数据

使用此行打印缓冲区,该行不会在零字节处停止:

std::cout.write(_packet.data_, _packet.body_length);
因此,您将看到身体的截断部分,然后在日志中看到一些随机数据

如何解决这个问题? 首先,当您创建缓冲区时,您应该通过零初始化来确保它不包含随机数据。否则,在通过网络发送缓冲区时,您有可能会泄漏敏感数据(加密密钥、密码)。考虑下面的示例如何初始化零:

  std::cout << "Printing test" << std::endl;

  // Not zero-initialized, will print garbage.
  char test[10];
  std::cout.write(test, sizeof(test));
  std::cout << std::endl;

  std::cout << "Printing test2" << std::endl;

  // Zero-initialized, will print nothing (because \0 is not printable)
  char test2[10]{};
  std::cout.write(test2, sizeof(test2));
  std::cout << std::endl;
您可以使用以下方法检查缓冲区内容:

  // Debug: Print buffer content, requires #include <iomanip>
  for (char c : buffer) {
    std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(c) << ' ';
  }
  std::cout << std::endl;

您可以看到没有随机数据出现,因为缓冲区已初始化为零。页眉数据的大小仅为1字节,但页眉数据的结尾和正文数据之间存在填充字节,从
页眉长度开始。
字节。

精彩的解释!非常感谢你,先生!在我提出这个问题之后,我碰巧想出了一个解决办法。我已将
header\uu
从char更改为
std::string header\uu比写入流之前我做
头=std::to_字符串(opc)
标题。调整大小(标题长度)然后在阅读侧我再次调整大小。在处理TCP数据包时,使用
char
数组而不是
string
和resize是否有任何显著的好处?两点:从编码风格的角度来看,我更喜欢
std::array
变量,因为它(即,您希望处理二进制blob而不是文本)。从性能角度来看,
std::array
变体的性能会稍好一些。我做了一些简单的测量,两个变量重复了10亿次,用
-O3
编译,得到了
std::to_string
+复制到
std::array
变量与
std::to_string
+调整大小的16s。您可能关心也可能不关心这样的差异。我不确定如何将标题和长度的
std::array
s组合在一起,以及如何在另一侧读取它们。你能举一个例子,说明我如何用接收部分的字符数组读取数据吗?还有动态体大小。一个信息是身体大小不同,另一个则不同。我正计划创建一个序列
1.Header 2.BodySize 3.Body
。在这种情况下,主体尺寸将设置在第二个平面上,因此,当读取部分看到标题时,它将在标题之后准备主体尺寸,然后知道预期的主体有多大,将准确读取那么多。然而,这将迫使我调整
缓冲区{}
。我怎样才能做到这一点?这是一个好主意吗?使用你的方法,我进入了另一个问题:你能检查一下吗?
std::cout.write(_packet.data_, _packet.header_length);
std::cout.write(_packet.data_, _packet.body_length);
  std::cout << "Printing test" << std::endl;

  // Not zero-initialized, will print garbage.
  char test[10];
  std::cout.write(test, sizeof(test));
  std::cout << std::endl;

  std::cout << "Printing test2" << std::endl;

  // Zero-initialized, will print nothing (because \0 is not printable)
  char test2[10]{};
  std::cout.write(test2, sizeof(test2));
  std::cout << std::endl;
  int op_code = 1;
  std::string header = std::to_string(op_code);
  constexpr size_t header_length = 8;

  std::string body{"abc"};
  constexpr size_t body_length = 32;

  // Zero-initialize buffer, requires #include <array>
  std::array<char, header_length + body_length> buffer{};

  // TODO Check header.size() <= header_length
  // TODO Check body.size() <= body_length

  std::copy(header.begin(), header.end(), buffer.begin());
  std::copy(body.begin(), body.end(), buffer.begin() + header_length);
  // Debug: Print buffer content, requires #include <iomanip>
  for (char c : buffer) {
    std::cout << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(c) << ' ';
  }
  std::cout << std::endl;
31 00 00 00 00 00 00 00 61 62 63 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00