boost TCP 服务器在 Linux 下无法正确接受连接

问题描述 投票:0回答:1

我正在开发客户端/服务器 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));
        });
}

并不能解决问题!

c++ boost boost-asio tcpclient tcpserver
1个回答
0
投票

服务器线程是从这里修改的。评论很说明问题:

// 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 服务,使用

io_context
而不是已弃用的
io_service
以及其他内容,例如不使用不必要的原始指针,
boost::bind
(或完全绑定)和
boost::thread
,传递执行上下文而不是执行器等:Live On Coliru

#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

奖金

要使其完整,请实际制作

accept_loop
循环:

住在Coliru

#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
© www.soinside.com 2019 - 2024. All rights reserved.