gpt4 book ai didi

c++ - 当使用 Boost ASIO 时,有效载荷在 MTU 范围内拆分为两个 TCP 数据包

转载 作者:可可西里 更新时间:2023-11-01 02:30:34 28 4
gpt4 key购买 nike

我对 boost::asio::ip::tcp::iostream 有疑问。我正在尝试发送大约 20 个原始字节。问题是这个 20 字节的有效载荷被分成两个 TCP 数据包,先是 1 字节,然后是 19 字节。简单的问题,为什么会发生我不知道。我正在为遗留二进制协议(protocol)编写此代码,该协议(protocol)非常需要有效负载适合单个 TCP 数据包(呻吟)。

从我的程序中粘贴整个源代码会很长而且过于复杂,我已经在此处的 2 个函数中发布了功能问题(经过测试,它确实重现了该问题);

#include <iostream>

// BEGIN cygwin nastyness
// The following macros and conditions are to address a Boost compile
// issue on cygwin. https://svn.boost.org/trac/boost/ticket/4816
//
/// 1st issue
#include <boost/asio/detail/pipe_select_interrupter.hpp>

/// 2nd issue
#ifdef __CYGWIN__
#include <termios.h>
#ifdef cfgetospeed
#define __cfgetospeed__impl(tp) cfgetospeed(tp)
#undef cfgetospeed
inline speed_t cfgetospeed(const struct termios *tp)
{
return __cfgetospeed__impl(tp);
}
#undef __cfgetospeed__impl
#endif /// cfgetospeed is a macro

/// 3rd issue
#undef __CYGWIN__
#include <boost/asio/detail/buffer_sequence_adapter.hpp>
#define __CYGWIN__
#endif
// END cygwin nastyness.

#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <iostream>

typedef boost::asio::ip::tcp::iostream networkStream;

void writeTestingData(networkStream* out) {
*out << "Hello world." << std::flush;
// *out << (char) 0x1 << (char) 0x2 << (char) 0x3 << std::flush;
}

int main() {
networkStream out("192.168.1.1", "502");

assert(out.good());

writeTestingData(&out);
out.close();
}

更奇怪的是,如果我发送字符串“Hello world.”,它会进入一个数据包。如果我发送 0x1、0x2、0x3(原始字节值),我会在数据包 1 中获得 0x1,然后在下一个 TCP 数据包中获得其余数据。我正在使用 wireshark 查看数据包,开发机器和 192.168.1.1 之间只有一个开关。

最佳答案

别担心,您是唯一一个遇到此问题的人。肯定有解决办法。事实上,您的遗留协议(protocol)有两个问题,而且不止一个。

您的旧遗留协议(protocol)需要一个“应用程序消息”以适应“一个且只有一个 TCP 数据包”(因为它错误地将面向 TCP 流的协议(protocol)用作面向数据包的协议(protocol))。所以我们必须确保:

  1. 没有“应用程序消息”被分割成多个 TCP 数据包(您看到的问题)
  2. 没有一个 TCP 数据包包含一个以上的“应用程序消息”(您没有看到这一点,但它肯定会发生)

解决方案:

问题1

您必须立即将所有“消息”数据提供给您的套接字。这目前没有发生,因为正如其他人所概述的那样,当您使用连续的“<<”并且您的操作系统的底层 TCP/IP 堆栈不缓冲时,您使用的 boost 流 API 将数据放入单独调用的套接字中足够了(并且有理由,为了更好的性能)

多种解决方案:

  • 你传递一个字符缓冲区而不是单独的字符,这样你只需要调用一次 <<
  • 你忘记了 boost,打开一个操作系统套接字并在一次调用 send() 时将其提供(在 Windows 上,寻找“winsock2”API,或在 unix/cygwin 上寻找“sys/socket.h”)

问题2

您必须激活 TCP_NODELAY socket 上的选项。此选项专为此类遗留协议(protocol)案例而设计。它将确保操作系统 TCP/IP 堆栈“毫不延迟地”发送您的数据,并且不会将其与您稍后可能发送的其他应用程序消息一起缓冲。

  • 如果您坚持使用 Boost,请查找 TCP_NODELAY 选项,它在文档中
  • 如果您使用操作系统套接字,则必须在您的套接字上使用 setsockopt() 函数。

结论

如果你解决了这两个问题,你应该没问题!

Windows 或 Linux 上的操作系统套接字 API 使用起来有点棘手,但您将获得对其行为的完全控制。 Unix example

关于c++ - 当使用 Boost ASIO 时,有效载荷在 MTU 范围内拆分为两个 TCP 数据包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6846924/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com