【问题标题】:Boost Asio tcp::iostream construction raise an Access Violation Exception on every second useBoost Asio tcp::iostream 构造在每次使用时都会引发访问冲突异常
【发布时间】:2020-10-14 18:59:01
【问题描述】:

我正在尝试在boost::asio::ip::tcp::socket 之上使用boost::asio 提供的std::iostream 的实现。我的代码几乎逐行复制published in Boost Asio's documentation的示例:

#include <iostream>
#include <stdexcept>

#include <boost/asio.hpp>

int main()
{
    using boost::asio::ip::tcp;
    try
    {
        boost::asio::io_service io_service;
        tcp::endpoint endpoint(tcp::v4(), 8000);
        tcp::acceptor acceptor(io_service, endpoint);

        for (;;)
        {
            tcp::iostream stream;  // <-- The exception is triggered on this line, on the second loop iteration.
            boost::system::error_code error_code;
            acceptor.accept(*stream.rdbuf(), error_code);
            std::cout << stream.rdbuf() << std::flush;
        }
    }
    catch (std::exception& exception)
    {
        std::cerr << exception.what() << std::endl;
    }

    return 0;
}

唯一的区别是我对生成的tcp::iostream 的使用:我将收到的所有内容转发到标准输出。

当我使用 VisualStudio2019/toolset v142 和来自 NuGet boost-vc142 的 Boost 编译此代码时,我在函数中仅在 for 循环的第二次迭代中得到访问冲突异常

template <typename Service>
Service& service_registry::use_service(io_context& owner)
{
  execution_context::service::key key;
  init_key<Service>(key, 0);
  factory_type factory = &service_registry::create<Service, io_context>;
  return *static_cast<Service*>(do_use_service(key, factory, &owner));
} // <-- The debugger show the exception was raised on this line

asio/detail/impl/service_registry.hpp。所以第一次迭代一切都按计划进行,连接被接受,数据显示在标准输出上,一旦stream第二次在堆栈上实例化,异常就会弹出。

我对调试器报告的异常的这个位置的准确性没有很高的信心。由于某种原因,堆栈接缝被弄乱并且只显示一帧。

如果stream 的声明移出循环,则不会再引发异常,但我需要在循环结束时使用stream.close(),否则除了来自第一个客户端的连接。

基本上,只要我尝试实例化多个boost::asio::tcp::iostream(不一定同时),就会引发异常。

我在 linux 下尝试了完全相同的代码(Arch linux,最新版本的 g++,相同版本的 Boost),一切正常。

我可以通过不使用iostreams 来解决这个问题,但我的想法是将在 tcp 套接字上接收到的数据提供给只接受 std::iostream 实现的解析器,因此我仍然需要包装 asio std::iostream 的自制(平庸)实现中的 tcp 套接字。

如果我在某处遗漏了重要的#define 或其他任何东西,是否有人知道此设置有什么问题?

更新:

随后的调查表明,发生访问冲突的唯一情况是从 Visual Studio 中运行可执行文件时(通常从菜单调试 -> 开始调试)。

构建过程似乎没有效果(直接调用cl.exe,使用MSBuild,使用devenv.exe)。

此外,如果可执行文件是从命令提示符运行的,然后才附加调试器,则不会发生访问冲突。

此时,问题很可能与代码本身无关。

【问题讨论】:

  • 真实代码是tcp::socket;吗?那有什么作用?
  • 是的,tcp::socket; 是一个有效的语句,但它什么也不做(它是一种解析为boost::asio::basic_stream_socket&lt;boost::asio::ip::tcp&gt; 的类型)。为了清楚起见,我编辑了问题以将其删除。
  • 我从不怀疑这是一个有效的声明。我什至设法在 Windows 上进行了测试,并发布了我的发现。 [即使这样,我也可以确认虚假线路没有影响。]

标签: sockets visual-c++ boost iostream asio


【解决方案1】:

好的,在 windows 上测试这个非常痛苦。

当然,我首先在 Linux (clang/gcc) 和 Windows 上的 MingW 8.1 上进行了尝试。

然后我咬紧牙关,用 boost 包在命令行中获取 MSVC ¹。

我通过手动将 boost_{system,date_time,regex} 的 .lib/.dll 复制到工作目录中来作弊,因此命令行保持“灵活”:

C:\work>C:\Users\sghee\Downloads\nuget.exe install boost_system-vc142
C:\work>C:\Users\sghee\Downloads\nuget.exe install boost_date_time-vc142
C:\work>C:\Users\sghee\Downloads\nuget.exe install boost_regex-vc142

(一定要在那些期间喝点咖啡)

C:\work\> cl /EHsc test.cpp /I .\boost.1.72.0.0\lib\native\include /link

现在我可以运行 test.exe

C:\work\> test.exe

它听得很好,接受连接(顺序,而不是同时)。如果您在第一个客户端仍然连接时连接第二个客户端,它将排队并仅在第一个断开连接后被接受。没关系,因为这是您对同步 accept 和循环的期望。

我使用 Ncat.exe(来自 Nmap)进行连接:

 C:\Program Files (x86)\Nmap>.\ncat.exe localhost 8000

怪癖:与 MingW 行为相反,MSVC cl.exe 构建(逐行)的缓冲很好,即使 MingW 也使用 ws2_32.dll。 #琐事

我知道这没有“帮助”,但也许你可以比较一下笔记,看看你的系统有什么不同。

测试视频


¹(如果没有 VS,这是一项艰巨的工作,而且我 - 显然 - 空间不足,因为 50GiB 的 VM 还不够)

【讨论】:

  • 在我的 Win10 虚拟机中添加了测试的 gif
  • 好的,所以我们在这里进入了奇怪的领域。我可以以相同的结果复制您的步骤:没有访问冲突。但事实上,我得到访问冲突的唯一 方式是当我在附加调试器的情况下运行可执行文件 Visual Studio 中。如果我在外部运行它并且只有附加调试器,则没有访问冲突。所以这个问题很可能与 VS 有关。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-08
  • 1970-01-01
  • 2017-01-17
  • 1970-01-01
相关资源
最近更新 更多