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