Boost::asio - 如何中断阻塞的 TCP 服务器线程?

2024-01-06

我正在开发一个多线程应用程序,其中一个线程充当从客户端接收命令的 TCP 服务器。该线程使用 Boost 套接字和接受器来等待客户端连接,接收来自客户端的命令,将命令传递给应用程序的其余部分,然后再次等待。这是代码:

void ServerThreadFunc()
{
    using boost::asio::ip::tcp;
    boost::asio::io_service io_service;
    tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), port_no));

    for (;;)
    {
        //  listen for command connection
        tcp::socket socket(io_service);
        acceptor.accept(socket);

        //  connected; receive command
        boost::array<char,256> msg_buf;
        socket.receive(boost::asio::buffer(msg_buf));

        //  do something with received bytes here
    }
}

该线程大部分时间都被阻塞在调用上acceptor.accept()。目前,线程仅在应用程序退出时终止。不幸的是,这会在 main() 返回后导致崩溃 - 我相信是因为在单例被销毁后线程尝试访问应用程序的日志记录单例。 (诚​​实的先生,当我到达这里时,情况就是这样。)

当应用程序退出时,如何彻底关闭该线程?我读过,可以通过从另一个线程关闭套接字来中断原始套接字上的阻塞accept()调用,但这似乎不适用于Boost套接字。我尝试使用以下方法将服务器逻辑转换为异步 I/OBoost 异步 tcp echo 服务器示例 http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp,但这似乎只是交换了一个阻塞调用acceptor::accept()阻止调用io_service::run(),所以我遇到了同样的问题:一个我无法打断的阻塞呼叫。有任何想法吗?


简而言之,有两种选择:

  • 将代码更改为异步(acceptor::async_accept() and async_read),通过以下方式在事件循环中运行io_service::run(),并通过取消io_service::stop().
  • 强制阻塞调用以使用较低级别的机制(例如信号)进行中断。

我会推荐第一个选项,因为它更便携且更易于维护。需要理解的重要概念是io_service::run() http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service/run/overload1.html只要有待处理的工作,就会阻塞。什么时候io_service::stop() http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio/reference/io_service/stop.html被调用时,它将尝试导致所有线程阻塞io_service::run()尽快返回;它不会中断同步操作,例如acceptor::accept() and socket::receive(),即使在事件循环内调用同步操作。重要的是要注意io_service::stop()是非阻塞调用,因此与阻塞的线程同步io_service::run()必须使用其他机制,例如thread::join() http://www.boost.org/doc/libs/1_49_0/doc/html/thread/thread_management.html#thread.thread_management.thread.join.

下面是一个运行 10 秒并监听端口 8080 的示例:

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <iostream>

void StartAccept( boost::asio::ip::tcp::acceptor& );

void ServerThreadFunc( boost::asio::io_service& io_service )
{
  using boost::asio::ip::tcp;
  tcp::acceptor acceptor( io_service, tcp::endpoint( tcp::v4(), 8080 ) );

  // Add a job to start accepting connections.
  StartAccept( acceptor );

  // Process event loop.
  io_service.run();

  std::cout << "Server thread exiting." << std::endl;
}

void HandleAccept( const boost::system::error_code& error,
                   boost::shared_ptr< boost::asio::ip::tcp::socket > socket,
                   boost::asio::ip::tcp::acceptor& acceptor )
{
  // If there was an error, then do not add any more jobs to the service.
  if ( error )
  {
    std::cout << "Error accepting connection: " << error.message() 
              << std::endl;
    return;
  }

  // Otherwise, the socket is good to use.
  std::cout << "Doing things with socket..." << std::endl;

  // Perform async operations on the socket.

  // Done using the socket, so start accepting another connection.  This
  // will add a job to the service, preventing io_service::run() from
  // returning.
  std::cout << "Done using socket, ready for another connection." 
            << std::endl;
  StartAccept( acceptor );
};

void StartAccept( boost::asio::ip::tcp::acceptor& acceptor )
{
  using boost::asio::ip::tcp;
  boost::shared_ptr< tcp::socket > socket(
                                new tcp::socket( acceptor.get_io_service() ) );

  // Add an accept call to the service.  This will prevent io_service::run()
  // from returning.
  std::cout << "Waiting on connection" << std::endl;
  acceptor.async_accept( *socket,
    boost::bind( HandleAccept,
      boost::asio::placeholders::error,
      socket,
      boost::ref( acceptor ) ) );
}

int main()
{
  using boost::asio::ip::tcp;

  // Create io service.
  boost::asio::io_service io_service;

  // Create server thread that will start accepting connections.
  boost::thread server_thread( ServerThreadFunc, boost::ref( io_service ) );

  // Sleep for 10 seconds, then shutdown the server.
  std::cout << "Stopping service in 10 seconds..." << std::endl;
  boost::this_thread::sleep( boost::posix_time::seconds( 10 ) );
  std::cout << "Stopping service now!" << std::endl;

  // Stopping the io_service is a non-blocking call.  The threads that are
  // blocked on io_service::run() will try to return as soon as possible, but
  // they may still be in the middle of a handler.  Thus, perform a join on 
  // the server thread to guarantee a block occurs.
  io_service.stop();

  std::cout << "Waiting on server thread..." << std::endl;
  server_thread.join();
  std::cout << "Done waiting on server thread." << std::endl;

  return 0;
}

运行时,我打开了两个连接。这是输出:


Stopping service in 10 seconds...
Waiting on connection
Doing things with socket...
Done using socket, ready for another connection.
Waiting on connection
Doing things with socket...
Done using socket, ready for another connection.
Waiting on connection
Stopping service now!
Waiting on server thread...
Server thread exiting.
Done waiting on server thread.  
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Boost::asio - 如何中断阻塞的 TCP 服务器线程? 的相关文章

  • Poco c++Net:Http 从响应中获取标头

    我使用 POCO C Net 库进行 http 我想尝试制定持久缓存策略 首先 我认为我需要从缓存标头中获取过期时间 并与缓存值进行交叉检查 如果我错了 请告诉我 那么我如何从中提取缓存头httpResponse 我已经看到你可以用 Jav
  • 有没有办法在 xcode 上使用 c++0x ?我想使用 gcc 4.4 或更高版本

    我想使用 gcc 4 4 或更高版本进行 iphone 开发 有人知道怎么做吗 不 你不知道 相信我 你不会 Apple 仍保留 gcc 4 2 1 因为 4 2 2 及更高版本使用 GPLv3 这意味着他们必须放弃对其平台的控制 对于 i
  • 从代码中,如何创建对存储在附加属性中的对象的属性的绑定?

    我们有一个继承的附加属性来存储一个对象 在可视化树的更下方 我们希望从代码绑定到该对象的属性 通常我们像这样构建绑定的路径部分 var someBinding new Binding Path new PropertyPath Attach
  • 在 C# 中调用 C++ 库 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我有很多用 C 编写的库 我想从 C 调用这些库 但是 我遇到了很多问题 我想知道是否有书籍或指南告诉我如何做到这一点 Dll导入 htt
  • std::call_once 可重入且线程安全吗?

    std call once http en cppreference com w cpp thread call once是线程安全的 但它也是可重入的吗 我使用 VS2012 调试和发布 进行的测试表明 调用std call once从单
  • 将表(行)与 OpenXML SDK 2.5 保持在一起

    我想在 Word 文档中生成多个表 每行 2 行 但我想将这两行保留在一起 如果可能的话 new KeepNext 第一行不起作用 new KeepNext 第一行的最后一段不起作用 new CantSplit 放在桌子上不起作用 在所有情
  • 使用 LINQ 更新 IEnumerable 对象的简单方法

    假设我有一个这样的业务对象 class Employee public string name public int id public string desgination public int grade List
  • C# 编译器不会优化不必要的强制转换

    前几天 在写答案的时候这个问题 https stackoverflow com questions 2208315 why is any slower than contains在这里 关于溢出 我对 C 编译器感到有点惊讶 它没有按照我的
  • MFC:如何设置CEdit框的焦点?

    我正在开发我的第一个简单的 MFC 项目 但我正在努力解决一个问题 想要设置所有的焦点CEdit其中一个对话框中的框 我的想法是 当打开对话框时 焦点位于第一个编辑框上 然后使用 选项卡 在它们之间交换 我看到了方法SetFocus 但我无
  • 如何在三个 IEnumerable 上使用 Zip [重复]

    这个问题在这里已经有答案了 可能的重复 使用 Linq 从 3 个集合创建项目 https stackoverflow com questions 5284315 create items from 3 collections using
  • 在 asp.net MVC 中使用活动目录进行身份验证

    我想使用活动目录对我的 asp net mvc 项目中的用户进行身份验证 在网上冲浪了几个小时后 我没有找到任何对我有用的东西 我已经看到了所有结果 但什么也没有 我尝试按照许多帖子的建议编辑我的 web config 如果有人可以帮助我提
  • 使用具有抗锯齿功能的 C# 更改抗锯齿图像的背景颜色

    我有一个图像需要更改背景颜色 例如 将下面示例图像的背景更改为蓝色 然而 图像是抗锯齿的 所以我不能简单地用不同的颜色替换背景颜色 我尝试过的一种方法是创建第二个图像 仅作为背景 并更改其颜色并将两个图像合并为一个图像 但是这不起作用 因为
  • 引用/指针失效到底是什么?

    我找不到任何定义指针 引用无效在标准中 我问这个问题是因为我刚刚发现 C 11 禁止字符串的写时复制 COW 据我了解 如果应用了 COW 那么p仍然是一个有效的指针并且r以下命令后的有效参考 std string s abc std st
  • Linux mremap 不释放旧映射?

    我需要一种方法将页面从一个虚拟地址范围复制到另一个虚拟地址范围 而无需实际复制数据 范围很大 延迟很重要 mremap 可以做到这一点 但问题是它也会删除旧的映射 由于我需要在多线程环境中执行此操作 因此我需要旧映射能够同时使用 因此稍后当
  • 选择查询不适用于使用Parameters.AddWithValue 的参数

    C 中的以下查询不起作用 但我看不出问题所在 string Getquery select from user tbl where emp id emp id and birthdate birthdate cmdR Parameters
  • CUDA 8 编译错误 -std=gnu++11

    我正在尝试转换一些代码以使用 CUDA 并且我认为我遇到了兼容性问题 我们使用CMake 这些是我使用的 gcc 和 CUDA 版本 gcc version gcc Ubuntu 5 4 0 6ubuntu1 16 04 5 5 4 0 2
  • 通过 Tab 键浏览 XML 文档字段

    In VB NET you can move through the fields in the XML member documentation with the Tab key 这在 C 中不起作用 还有其他方法吗 除了用鼠标将光标放在
  • 为什么以下 C 程序会出现总线错误?

    我认为这是第一个失败的 strtok 调用 好久没写C了 有点不知所措 非常感谢 include
  • 如何使用placement new重新初始化该字段?

    我的课程包含字段 private OrderUpdate curOrderUpdate 我一遍又一遍地使用它 经常需要重新初始化 for int i 0 i lt entries size i auto entry entries i ne
  • 结构化绑定的用例有哪些?

    C 17 标准引入了新的结构化绑定 http en cppreference com w cpp language structured binding功能 最初是proposed http www open std org jtc1 sc

随机推荐