C++ 接受程序拒绝仅为-O0生成打开套接字

C++ 接受程序拒绝仅为-O0生成打开套接字,c++,c++11,boost-asio,C++,C++11,Boost Asio,嗨,亲爱的stackoverflow社区 (编辑:结尾处的单文件版本) 我只在某些编译设置中遇到了问题(这表明存在某种UB,但我决定将此问题标记为boost asio,因为它可能涉及asio细节方面的知识)。请注意,我使用的是独立asio库的当前git版本 首先让我留下一些片段-完整的代码如下: (请注意,CMakeLists.txt非常粗糙,可能需要修复,以防您想要编译它) 我在examples/echo_server.cpp中有一个测试echo服务器(tcp,异步): [snip...] i

嗨,亲爱的stackoverflow社区

(编辑:结尾处的单文件版本)

我只在某些编译设置中遇到了问题(这表明存在某种UB,但我决定将此问题标记为boost asio,因为它可能涉及asio细节方面的知识)。请注意,我使用的是独立asio库的当前git版本

首先让我留下一些片段-完整的代码如下: (请注意,CMakeLists.txt非常粗糙,可能需要修复,以防您想要编译它)

我在examples/echo_server.cpp中有一个测试echo服务器(tcp,异步):

[snip...]
int main (int argc, char const* argv[]) {
    try {
        if (argc != 2) {
            std::cerr << "Usage: echo_server <port>\n";
            return 1;
        }

        asio::io_context io_context;
        magellan::server server;
        server.accept<echo_session>(io_context, 9003);
        io_context.run();
    } catch (std::exception& e) {
        std::cerr << e.what() << "\n";
    }
}
。。。以及include/server.ipp:

#include <iostream>
namespace magellan {

template <typename Session>
inline void
server::accept(asio::io_context& io_context, short port) {
    using asio::ip::tcp;
    asio::spawn(io_context, [&](asio::yield_context yield) {
        tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), port));

        for (;;) {
            asio::error_code ec;
            tcp::socket socket(io_context);
            acceptor.async_accept(socket, yield[ec]);
            if (!ec) {
                auto session = std::make_shared<Session>(std::move(socket));
                session->start();
            }
        }
    });
}

[snip comments...]

} // magellan
当使用-O0编译时,套接字永远不会打开。我还通过io_context::work实例检查了服务是否仍在运行

我最好的猜测(承认缺乏信心)是boostcoroutine对-O0做了一些不同的事情。还值得一提的是,如果我在server.ipp中使用注释掉的代码,它也不会打开套接字(无论编译设置如何):

模板
内联空隙
服务器::接受(asio::io_上下文和io_上下文,短端口){
使用asio::ip::tcp;
接受(io_上下文,端口,[](tcp::套接字s){
返回std::make_shared(std::move(s));
});
}
模板
无效的
服务器::接受(asio::io_上下文和io_上下文、短端口、函数和工厂){
使用asio::ip::tcp;
asio::spawn(io_上下文,[&](asio::yield_上下文yield){
tcp::acceptor acceptor(io_上下文,tcp::endpoint(tcp::v4(),端口));
对于(;;){
asio::错误代码ec;
tcp::套接字(io_上下文);
acceptor.async_accept(socket,yield[ec]);
如果(!ec){
自动会话=工厂(标准::移动(套接字));
会话->开始();
}否则{

std::cout存在一种争用条件,该争用条件可能导致访问悬挂引用,调用未定义的行为。lambda捕获列表通过引用捕获自动变量,
端口
io_服务
。但是,
端口
的生存期可能在用于构造
接受器之前结束ode>。在这种情况下,未定义的行为很可能导致程序绑定到随机端口,但它可能以其他方式失败

void服务器::接受(asio::io_上下文和io_上下文,短端口)
{
使用asio::ip::tcp;
asio::spawn(io_上下文,[&](asio::yield_上下文yield)
{
tcp::acceptor acceptor(io_上下文,
端点(tcp::v4(),端口));
//生命也许已经结束了
...
}
}
要解决此问题,请在lambda捕获中按值捕获端口。更改:

[&](boost::asio::yield\u context yield){…}
致:

[端口和io_服务](boost::asio::yield\u上下文yield){…}

以下示例以原始代码为基础,可以(有时)更改竞争条件:

#包括
#包括
#包括
类服务器
{
公众:
无效接受(boost::asio::io_服务和io_服务,短端口)
{
使用boost::asio::ip::tcp;

std::cout存在一种争用条件,该争用条件可能导致访问悬挂引用,调用未定义的行为。lambda捕获列表通过引用捕获自动变量,
端口
io_服务
。但是,
端口
的生存期可能在用于构造
接受器之前结束ode>。在这种情况下,未定义的行为很可能导致程序绑定到随机端口,但它可能以其他方式失败

void服务器::接受(asio::io_上下文和io_上下文,短端口)
{
使用asio::ip::tcp;
asio::spawn(io_上下文,[&](asio::yield_上下文yield)
{
tcp::acceptor acceptor(io_上下文,
端点(tcp::v4(),端口));
//生命也许已经结束了
...
}
}
要解决此问题,请在lambda捕获中按值捕获端口。更改:

[&](boost::asio::yield\u context yield){…}
致:

[端口和io_服务](boost::asio::yield\u上下文yield){…}

以下示例以原始代码为基础,可以(有时)更改竞争条件:

#包括
#包括
#包括
类服务器
{
公众:
无效接受(boost::asio::io_服务和io_服务,短端口)
{
使用boost::asio::ip::tcp;

std::cout存在一种争用条件,该争用条件可能导致访问悬挂引用,调用未定义的行为。lambda捕获列表通过引用捕获自动变量,
端口
io_服务
。但是,
端口
的生存期可能在用于构造
接受器之前结束ode>。在这种情况下,未定义的行为很可能导致程序绑定到随机端口,但它可能以其他方式失败

void服务器::接受(asio::io_上下文和io_上下文,短端口)
{
使用asio::ip::tcp;
asio::spawn(io_上下文,[&](asio::yield_上下文yield)
{
tcp::acceptor acceptor(io_上下文,
端点(tcp::v4(),端口));
//生命也许已经结束了
...
}
}
要解决此问题,请在lambda捕获中按值捕获端口。更改:

[&](boost::asio::yield\u context yield){…}
致:

[端口和io_服务](boost::asio::yield\u上下文yield){…}

以下示例以原始代码为基础,可以(有时)更改竞争条件:

#包括
#包括
#包括
类服务器
{
公众:
无效接受(boost::asio::io_服务和io_服务,短端口)
{
使用boost::asio::ip::tcp;

std::cout存在一个争用条件,该争用条件可能导致访问悬空引用,从而调用未定义的行为。lambda捕获列表正在捕获自动
#include <iostream>
namespace magellan {

template <typename Session>
inline void
server::accept(asio::io_context& io_context, short port) {
    using asio::ip::tcp;
    asio::spawn(io_context, [&](asio::yield_context yield) {
        tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), port));

        for (;;) {
            asio::error_code ec;
            tcp::socket socket(io_context);
            acceptor.async_accept(socket, yield[ec]);
            if (!ec) {
                auto session = std::make_shared<Session>(std::move(socket));
                session->start();
            }
        }
    });
}

[snip comments...]

} // magellan
> ss -a | grep 9003
296:tcp    LISTEN     0      128     *:9003                  *:*  
template <typename Session>
inline void
server::accept(asio::io_context& io_context, short port) {
    using asio::ip::tcp;
    accept<Session>(io_context, port, [](tcp::socket s) {
        return std::make_shared<Session>(std::move(s));
    });
}

template <typename Session, typename Func>
void
server::accept(asio::io_context& io_context, short port, Func&& factory) {
    using asio::ip::tcp;
    asio::spawn(io_context, [&](asio::yield_context yield) {
        tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), port));

        for (;;) {
            asio::error_code ec;
            tcp::socket socket(io_context);
            acceptor.async_accept(socket, yield[ec]);
            if (!ec) {
                auto session = factory(std::move(socket));
                session->start();
            } else {
                std::cout << "failed accept" << "\n";
            }
        }
    });
}
#include <iostream>
#include <asio.hpp>
#include <asio/spawn.hpp>
using asio::ip::tcp;

namespace magellan {

class session : public std::enable_shared_from_this<session> {
public:
    session(asio::ip::tcp::socket socket) : socket_(std::move(socket)), strand_(socket_.get_io_context()) {
    }

    template <typename Func>
    void
    async_do(Func&& f) {
        auto self(shared_from_this());
        asio::spawn(strand_, [this, self, f](asio::yield_context yield) {
            try {
                f(std::ref(socket_), std::ref(yield));
            } catch (std::exception& e) {
                socket_.close();
            }
        });
    }

    void start() {
        async_do([this] (tcp::socket& s, asio::yield_context& yc) {
            perform(s, yc);
        });
    }

protected:
    virtual void perform(asio::ip::tcp::socket& s, asio::yield_context&) {
        s.close();
    }

protected:
    asio::ip::tcp::socket socket_;
    asio::io_context::strand strand_;
};

class server {
    public:
        typedef std::shared_ptr<server>       ptr;
        typedef std::weak_ptr<server>         wptr;
        typedef std::shared_ptr<const server> const_ptr;
        typedef std::weak_ptr<const server>   const_wptr;

    public:
        server() {}

        virtual ~server() {}

        template <typename Session>
        void
        accept(asio::io_context& io_context, short port) {
            using asio::ip::tcp;
            asio::spawn(io_context, [&](asio::yield_context yield) {
                tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), port));

                for (;;) {
                    asio::error_code ec;
                    tcp::socket socket(io_context);
                    acceptor.async_accept(socket, yield[ec]);
                    if (!ec) {
                        auto session = std::make_shared<Session>(std::move(socket));
                        session->start();
                    }
                }
            });
        }
};

} // magellan

class echo_session : public magellan::session {
    public:
        typedef std::shared_ptr<echo_session>       ptr;
        typedef std::weak_ptr<echo_session>         wptr;
        typedef std::shared_ptr<const echo_session> const_ptr;
        typedef std::weak_ptr<const echo_session>   const_wptr;

    public:
        echo_session(tcp::socket socket)
            : magellan::session(std::move(socket)) {}

        virtual ~echo_session() {}

    protected:
        void perform(asio::ip::tcp::socket& s, asio::yield_context& yc) {
            char data[128];
            for (;;) {
                std::size_t n = s.async_read_some(asio::buffer(data), yc);
                asio::async_write(s, asio::buffer(data, n), yc);
            }
        }
};


int main (int argc, char const* argv[]) {
    try {
        if (argc != 2) {
            std::cerr << "Usage: echo_server <port>\n";
            return 1;
        }

        asio::io_context io_context;
        magellan::server server;
        server.accept<echo_session>(io_context, 9003);
        io_context.run();
    } catch (std::exception& e) {
        std::cerr << e.what() << "\n";
    }
}