You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Boost.Asio UDP服务器构造时do_use_service中断问题排查与修改咨询

问题描述

调试时程序在调用service_registry.ipp中的execution_context::service* service_registry::do_use_service()函数时抛出异常,最终中断在win_mutex.hpp的以下代码处:

void lock()
{
  ::EnterCriticalSection(&crit_section_);
}

需要解决:

  • 中断的原因是什么?
  • 如何修改UDPServer类?
  • 将io_context作为UDPServer类的成员,仅传入主机和端口即可使用对象,这种方式是否可行?

现有代码

头文件(UdpServerByUsingBoostAsio.h)

#ifndef UDPSERVER_H
#define UDPSERVER_H
#include <boost/asio.hpp>
#include <string>
namespace util {
    class UDPServer : public std::enable_shared_from_this<UDPServer> {
    public:
        enum { UDP_PORT = 18400 };
        UDPServer(const std::string& host, short port);
        ~UDPServer();
        void start_receive();
        //void setObserver(std::weak_ptr<WebSocket::Observer> observer) noexcept;
        void start();

    private:
        boost::asio::ip::udp::socket socket_;
        boost::asio::io_context io_context_;
        // 接受ip和端口
        boost::asio::ip::udp::endpoint sender_endpoint_;
        // 设置一个指针,参考util::WebSocket
        //std::weak_ptr<WebSocket::Observer> observer_;
        enum { max_length = 1024 };
        char data_[max_length];
        bool start_ = false;
    };
} // namespace util
#endif 

.cpp文件(UdpServerByUsingBoostAsio.cpp)

#include "UdpServerByUsingBoostAsio.h"

namespace util {
    UDPServer::UDPServer(const std::string& host, short port)
        :io_context_(),socket_(io_context_, boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string(host), port))
    {
        printf("debug:UDPServer Constructor");
        //start_receive();
        start_receive();
        io_context_.run();
    }
    UDPServer::~UDPServer() {
        socket_.close();
    }
    void UDPServer::start_receive() {
        printf("debug:UDPServer start_receive");
        socket_.async_receive_from(boost::asio::buffer(data_, max_length), sender_endpoint_,
            [this](boost::system::error_code error, std::size_t bytes_recvd) {
                printf("debug:UDPServer handle_receive");
                if (!error) {
                    printf(" Received message from %s,%d,%s", sender_endpoint_.address().to_string().c_str(), sender_endpoint_.port(), data_);
                    start_receive();
                }
                else {
                    printf("error:UDPServer handle_receive,ec_msg:%s", error.message().c_str());
                }});
    }
    void UDPServer::start() {
        start_ = true;
        //start_receive();
        //io_context_.run();
    }
} // namespace util

main.cpp

#include <iostream>
#include <boost/asio.hpp>
#include "UdpServerByUsingBoostAsio.h"
#include <memory>
#include <string>
#include <cstdio>
#include <thread>
using namespace std;
int main() {
    try {
        shared_ptr<std::thread> netAccess_ = std::make_shared<std::thread>([&] {
            std::shared_ptr<util::UDPServer> ptr1 = std::make_shared<util::UDPServer>("127.0.0.1", util::UDPServer::UDP_PORT);
            ptr1->start();
            });
    }
    catch (std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
    printf("%-> modelId(%s)", "caiji", "jiuzhe");
    return 0;
}

问题分析与解决方案

中断原因

  1. io_context运行时机错误:构造函数中直接调用io_context_.run(),导致UDPServer对象的构造过程阻塞,而主线程很快退出,创建服务器的线程被销毁,引发io_context内部服务注册表的临界区访问异常。
  2. 线程生命周期管理混乱:main中创建的线程是局部shared_ptr,主线程执行完后线程对象被销毁,服务器线程可能还在运行,引发资源访问冲突。
  3. 悬空引用风险:线程lambda使用[&]捕获外部变量,主线程退出后引用失效,可能间接导致io_context内部状态异常。

修改UDPServer类的方案

核心思路是封装io_context的生命周期,让服务器启动、停止逻辑可控,避免构造阶段阻塞。

修改后的头文件

#ifndef UDPSERVER_H
#define UDPSERVER_H
#include <boost/asio.hpp>
#include <string>
#include <thread>
#include <atomic>

namespace util {
    class UDPServer : public std::enable_shared_from_this<UDPServer> {
    public:
        enum { UDP_PORT = 18400 };
        UDPServer(const std::string& host, short port);
        ~UDPServer();
        void start();
        void stop();

    private:
        void start_receive();
        boost::asio::ip::udp::socket socket_;
        boost::asio::io_context io_context_;
        boost::asio::ip::udp::endpoint sender_endpoint_;
        enum { max_length = 1024 };
        char data_[max_length];
        std::atomic<bool> running_;
        std::thread io_thread_;
    };
} // namespace util
#endif 

修改后的.cpp文件

#include "UdpServerByUsingBoostAsio.h"

namespace util {
    UDPServer::UDPServer(const std::string& host, short port)
        : socket_(io_context_, boost::asio::ip::udp::endpoint(boost::asio::ip::address::from_string(host), port)),
          running_(false)
    {
        printf("debug:UDPServer Constructor\n");
    }

    UDPServer::~UDPServer() {
        stop();
    }

    void UDPServer::start() {
        if (running_.exchange(true)) return;
        start_receive();
        io_thread_ = std::thread([this] {
            printf("debug:io_context running\n");
            io_context_.run();
            printf("debug:io_context stopped\n");
        });
    }

    void UDPServer::stop() {
        if (!running_.exchange(false)) return;
        socket_.close();
        io_context_.stop();
        if (io_thread_.joinable()) {
            io_thread_.join();
        }
    }

    void UDPServer::start_receive() {
        printf("debug:UDPServer start_receive\n");
        socket_.async_receive_from(boost::asio::buffer(data_, max_length), sender_endpoint_,
            [self = shared_from_this()](boost::system::error_code error, std::size_t bytes_recvd) {
                printf("debug:UDPServer handle_receive\n");
                if (!error && self->running_) {
                    printf(" Received message from %s:%d, data:%.*s\n", 
                           self->sender_endpoint_.address().to_string().c_str(), 
                           self->sender_endpoint_.port(),
                           static_cast<int>(bytes_recvd), self->data_);
                    self->start_receive();
                }
                else if (error) {
                    printf("error:UDPServer handle_receive, ec_msg:%s\n", error.message().c_str());
                }
            });
    }
} // namespace util

修改后的main.cpp

#include <iostream>
#include <boost/asio.hpp>
#include "UdpServerByUsingBoostAsio.h"
#include <memory>
#include <string>
#include <cstdio>
#include <thread>
#include <chrono>

using namespace std;

int main() {
    try {
        std::shared_ptr<util::UDPServer> ptr1 = std::make_shared<util::UDPServer>("127.0.0.1", util::UDPServer::UDP_PORT);
        ptr1->start();

        // 保持主线程运行,避免程序提前退出
        printf("Server running, press Enter to exit...\n");
        getchar();

        ptr1->stop();
    }
    catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}

关于io_context作为类成员的可行性

完全可行,这种方式完美符合面向对象的封装原则,将网络相关资源(io_context、socket)都封装在UDPServer内部,用户只需传入主机和端口即可创建并使用服务器,无需关心底层网络循环的实现,使用体验更简洁安全。修改后的代码正是采用这种设计。


内容的提问来源于stack exchange,提问作者sleepy2con

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 14:57:54