提问人:jpo38 提问时间:11/7/2023 最后编辑:jpo38 更新时间:11/7/2023 访问量:57
boost TCP 服务器在 Linux 下无法正确接受连接
boost TCP server does not correctly accept connections under Linux
问:
我正在开发客户端/服务器 TCP 应用程序。它在 Windows 下运行良好,但我在 Linux 下遇到了问题:当与客户端建立新连接时,服务器不会收到通知(很少,但大多数情况下不会)。
我能够在一个非常简单的程序中隔离问题。该程序创建并运行一个服务器(有自己的)接受新连接,然后从客户端建立连接(也使用自己的连接,就像它在单独的应用程序中一样)。boost::asio::io_service
boost::asio::io_service
代码如下:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <assert.h>
using boost::asio::ip::tcp;
std::shared_ptr<tcp::socket> m_nextConnection;
std::atomic_bool m_continueServerThread = true;
void server_thread_func(boost::asio::io_service* service)
{
// http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.effect_of_exceptions_thrown_from_handlers
while (m_continueServerThread) {
try {
service->run();
// don't break, keep looping, else we may exit before a connection is actually established
//break; // exited normally
}
catch (std::exception const& e) {
std::cerr << "[eventloop] error: " << e.what();
}
catch (...) {
std::cerr << "[eventloop] unexpected error";
}
}
}
void on_accept_connection(std::error_code ec)
{
if (!ec)
{
std::cout << "SERVER ACCEPTED CONNECTION" << std::endl;
}
else
{
std::cout << "Reader connection error " << ec << " (" << ec.message() << ")" << std::endl;
}
}
void do_accept_connection(boost::asio::ip::tcp::acceptor& acceptor, boost::asio::io_service& service)
{
m_nextConnection = std::shared_ptr<tcp::socket>(new boost::asio::ip::tcp::socket(service));
acceptor.async_accept(
*m_nextConnection, boost::bind(on_accept_connection, boost::asio::placeholders::error));
}
int main( int argc, char* argv[] )
{
auto endpoint = boost::asio::ip::tcp::endpoint{ boost::asio::ip::address::from_string("127.0.0.1"), 1900 };
try
{
// start server:
boost::asio::io_service IOServiceServer;
boost::thread serverThread( boost::bind(server_thread_func,&IOServiceServer) );
boost::this_thread::sleep(boost::posix_time::milliseconds(500));
boost::asio::ip::tcp::acceptor m_acceptor{ IOServiceServer, endpoint };
// dunno if this is needed or not here
//m_acceptor.set_option(tcp::acceptor::reuse_address(true));
do_accept_connection(m_acceptor, IOServiceServer);
boost::this_thread::sleep(boost::posix_time::milliseconds(500));
// start client:
boost::asio::io_service IOServiceClient;
tcp::socket socket(IOServiceClient);
std::cout << "Connecting socket..." << std::endl;
socket.connect(endpoint);
std::cout << "Connected socket" << std::endl;
boost::this_thread::sleep(boost::posix_time::milliseconds(500));
// stop/close client:
IOServiceClient.stop();
socket.close();
// stop server:
IOServiceServer.stop();
m_continueServerThread = false;
serverThread.join();
return 0;
}
catch (std::exception const& e) {
std::cerr << "error: " << e.what();
}
catch (...) {
std::cerr << "unexpected error";
}
return 1;
}
在 Windows 下运行,如下所示:
Connecting socket...
Connected socket
SERVER ACCEPTED CONNECTION
在 Linux 下运行,这仅显示:
Connecting socket...
Connected socket
如您所见,未被调用,因此服务器不会收到已建立新连接的通知。on_accept_connection
我做错了什么吗?
编辑:也尝试过没有m_nextConnection:
void on_accept_connection(std::error_code ec, tcp::socket socket)
{
if (!ec)
{
std::cout << "SERVER ACCEPTED CONNECTION" << std::endl;
}
else
{
std::cout << "Reader connection error " << ec << " (" << ec.message() << ")" << std::endl;
}
}
void do_accept_connection(boost::asio::ip::tcp::acceptor& acceptor, boost::asio::io_service& service)
{
acceptor.async_accept(service, [&](const std::error_code& ec, tcp::socket newSocket) {
on_accept_connection(ec, std::move(newSocket));
});
}
不能解决问题!
答:
服务器线程已从此处修改。评论很能说明问题:
// don't break, keep looping, else we may exit before a connection is actually established
事实并非如此。在建立连接之前看到它退出的原因是 IO 服务耗尽了工作。由于你让服务耗尽了工作,所以循环是没有意义的,除非你真的使用了 restart():
对 、 或 的后续调用将立即返回,除非之前调用了
run()
run_one()
poll()
poll_one()
restart()
因此,真正的解决方法是保持循环的预期,但在开始之前放置工作,例如更改
std::thread serverThread(server_thread_func, std::ref(ioc));
do_accept_connection(m_acceptor, ioc);
自
do_accept_connection(m_acceptor, ioc);
std::thread serverThread(server_thread_func, std::ref(ioc));
其他问题
全局连接变量是一个问题,尤其是当您接受 1 个以上的连接时。通过不使用全局来修复它,例如:
void on_accept_connection(std::error_code ec, std::shared_ptr<tcp::socket> s) {
if (!ec)
std::cout << "SERVER ACCEPTED CONNECTION from " << s->remote_endpoint() << std::endl;
else
std::cout << "Reader connection error " << ec << " (" << ec.message() << ")" << std::endl;
}
void do_accept_connection(tcp::acceptor& acceptor, asio::io_context& service) {
auto s = std::make_shared<tcp::socket>(service);
acceptor.async_accept(*s, bind(on_accept_connection, _1, s));
}
请注意我们如何将共享指针绑定到完成处理程序中,以使其保持活动状态。
这已经有效了:https://coliru.stacked-crooked.com/a/df135c6936c5e37e
Connecting socket...
Connected socket
SERVER ACCEPTED CONNECTION from 127.0.0.1:45958
[eventloop] exit
Closing socket
修复一切
但是,您应该采用移动套接字的重载,使用内置线程池,不使用第二个 io 服务,使用而不是已弃用的和其他东西,例如不使用不必要的原始指针(或完全绑定)和传递执行上下文而不是执行器等: Live On Coliruio_context
io_service
boost::bind
boost::thread
#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using namespace std::chrono_literals;
using boost::asio::ip::tcp;
void on_accept_connection(std::error_code ec, tcp::socket s) {
if (!ec)
std::cout << "Accepted from " << s.remote_endpoint() << std::endl;
else
std::cout << std::endl;
}
void accept_loop(tcp::acceptor& acc) {
acc.async_accept(make_strand(acc.get_executor()), on_accept_connection);
}
int main() {
tcp::endpoint ep{{}, 1900};
using std::this_thread::sleep_for;
asio::thread_pool ioc(1);
tcp::acceptor listener{ioc, ep};
accept_loop(listener);
sleep_for(500ms);
{
// start client:
tcp::socket socket(ioc);
std::cout << "Connecting socket..." << std::endl;
socket.connect(ep);
std::cout << "Connected socket" << std::endl;
sleep_for(500ms);
std::cout << "Closing socket" << std::endl;
}
ioc.join();
}
印刷
Connecting socket...
Connected socket
Accepted from 127.0.0.1:47014
Closing socket
奖金
为了使它完整,实际上make loop:accept_loop
#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using namespace std::chrono_literals;
using boost::system::error_code;
using boost::asio::ip::tcp;
struct session : std::enable_shared_from_this<session> {
session(tcp::socket s) : s_(std::move(s)) {}
void run() {
std::cout << "Session from " << s_.remote_endpoint() << std::endl;
async_write(s_,
asio::buffer(message_), //
consign(asio::detached, shared_from_this()));
}
private:
tcp::socket s_;
std::string message_ = "Hello from server\n";
};
struct listener {
listener(tcp::endpoint ep) : acc_(ioc_, ep) { accept_loop(); }
private:
asio::thread_pool ioc_{1};
tcp::acceptor acc_;
void accept_loop() {
acc_.async_accept(make_strand(acc_.get_executor()), [this](error_code ec, tcp::socket s) {
if (!ec) {
std::make_shared<session>(std::move(s))->run();
accept_loop();
} else
std::cout << std::endl;
});
}
};
int main() {
tcp::endpoint ep{{}, 1900};
listener server(ep);
std::this_thread::sleep_for(50ms);
for (auto i = 0; i < 10; ++i) {
tcp::iostream is(ep);
std::cout << "Connected: " << is.rdbuf() << std::endl;
}
}
印刷,例如
Connected: Session from 127.0.0.1:48586
Hello from server
Connected: Session from 127.0.0.1:48588
Hello from server
Connected: Session from 127.0.0.1:48590
Hello from server
Connected: Session from 127.0.0.1:48592
Hello from server
Connected: Session from 127.0.0.1:48594
Hello from server
Connected: Session from 127.0.0.1:48596
Hello from server
Connected: Session from 127.0.0.1:48598
Hello from server
Connected: Session from 127.0.0.1:48600
Hello from server
Connected: Session from 127.0.0.1:48602
Hello from server
Connected: Session from 127.0.0.1:48604
Hello from server
评论
restart()
run()
server_thread_func
评论
acceptor.async_accept
on_accept_connection
async_accept
shared_ptr
*m_nextConnection
async_accept
do_accept_connection
m_nextConnection
async_accept
on_accept_connection