Boost :: Asio - 抛出get_io_service异常

时间:2015-09-04 20:14:09

标签: c++ boost boost-asio

有人可以向我解释为什么当我想调用get_io_service()时,我得到以下异常?

enter image description here

我看到启动时接受器已初始化,但是当客户端想要连接并且服务器想要打开新连接时,接受器有一些随机数。我不知道为什么会这样。

我的代码:

的main.cpp

#include "TServer.h"
#include "TDatabase.h"
#include "Includes.h"
#include "Structures.h"

int main()
{
try
{
    std::cout << "========================================" << std::endl
        << "= Game Server v1.0 by Gravity1        =" << std::endl
        << "========================================" << std::endl;

    boost::asio::io_service io_service;
    Database database;
    std::vector<std::vector<TServer>> Server;
    srand(time(0));

    boost::property_tree::ptree pt;
    boost::property_tree::ini_parser::read_ini("game_server_config.ini", pt);

    database.host = pt.get<std::string>("DATABASE.HOST");
    database.username = pt.get<std::string>("DATABASE.USER");
    database.password = pt.get<std::string>("DATABASE.PASS");
    database.schema = pt.get<std::string>("DATABASE.SCHEMA");

    std::shared_ptr<TDatabase> Database_ptr = std::make_shared<TDatabase>(database);

    Database_ptr->Connect();

    short server_count = pt.get<short>("GAME_SERVER.SERVER_COUNT");

    if (server_count > 0)
        Server.resize(server_count);

    for (int i = 0; i < server_count; i++)
    {
        short channel_count = pt.get<short>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL_COUNT");

        for (int j = 0; j < channel_count; j++)
        {
            Canal CanalTemp;
            CanalTemp.ip = pt.get<std::string>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_IP");
            CanalTemp.port = pt.get<short>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_PORT");
            boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(CanalTemp.ip), CanalTemp.port);
            Server[i].emplace_back(io_service, Database_ptr,endpoint);
        }
    }

    io_service.run();
}

catch (std::exception &e)
{
    std::cerr << e.what() << std::endl;
}

std::cin.get();
return 0;
}

TServer.cpp

TServer::TServer(boost::asio::io_service &io_service,std::shared_ptr<TDatabase> database, const boost::asio::ip::tcp::endpoint &endpoint) :
acceptor(io_service,endpoint)
{
Accept_Connection();
}

void TServer::Accept_Connection()
{
Connection = std::make_shared<TSession>(acceptor.get_io_service(),Database);
acceptor.async_accept(*(Connection->Socket()),(boost::bind(&TServer::Handle_Connection, this, Connection, boost::asio::placeholders::error)));
}

void TServer::Handle_Connection(std::shared_ptr<TSession> Connection, const boost::system::error_code &error)
{
if (!error)
{
    Connection->Start();
    Accept_Connection();
}
}

2 个答案:

答案 0 :(得分:0)

问题很简单。

您将TServer放置在向量的背面。当你这样做时,它会(可能)重新分配,使程序其他部分中的引用无效。见Iterator invalidation rules

在您的情况下,会立即执行此类引用,因为Accept_Connection()是从构造函数中调用的,而绑定是指向this指针。请记住,this指针指向向量中TServer元素的地址。

OOPS。当您的完成处理程序触发时,该元素/可能已被重新分配。所以指针只是悬空,你有Undefined Behaviour

您可以通过不同方式解决问题:

  1. 用容器替换向量,以确保插入时参考的稳定性。例如,您只需使用list<>代替:

    std::list<std::list<TServer> > servers;
    if (server_count > 0)
        servers.resize(server_count);
    
    auto current_server = servers.begin();
    
    for (int i = 0; i < server_count; i++, ++current_server) {
        short channel_count = pt.get<short>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL_COUNT");
    
        for (int j = 0; j < channel_count; j++) {
            Canal CanalTemp;
            CanalTemp.ip   = pt.get<std::string>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_IP");
            CanalTemp.port = pt.get<short>("GAME_SERVER.SERVER_"       + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_PORT");
            tcp::endpoint endpoint(boost::asio::ip::address::from_string(CanalTemp.ip), CanalTemp.port);
    
            current_server->emplace_back(io_service, Database_ptr, endpoint);
        }
    }
    
  2. 或者,您可以推迟初始绑定,直到 所有通道都添加到所有服务器之后:

    TServer(boost::asio::io_service &io_service, std::shared_ptr<TDatabase> database, const boost::asio::ip::tcp::endpoint &endpoint)
            : acceptor(io_service, endpoint), database(database)
    {
        //Accept_Connection();
    }
    

    io_service::run()

    之前明确地这样做
    for(auto& server: servers)
        for(auto& channel: server)
            channel.Accept_Connection();
    
    io_service.run();
    
      

    注意:事实上,在惯用的Asio代码中,直接从构造函数中运行异步操作通常不可能。看,例如在TSession类型;它无法将完成处理程序绑定到成员函数,因为shared_from_this() 不允许来自构造函数("Note that prior to calling shared_from_this on an object t, there must be a shared_ptr that owns t.")。

  3. 两者都有效。我选择第一个:

    <强> Live On Coliru

    #include <boost/asio.hpp>
    #include <boost/make_shared.hpp>
    #include <boost/enable_shared_from_this.hpp>
    #include <boost/bind.hpp>
    #include <iostream>
    // for iterator and reference stability (see:
    // https://stackoverflow.com/questions/6438086/iterator-invalidation-rules)
    #include <list> 
    
    using tcp = boost::asio::ip::tcp;
    
    struct Canal {
        std::string ip;
        int port;
    };
    
    struct Database {
        std::string host, username, password, schema;
    };
    
    struct TDatabase {
        TDatabase(Database config) : details(config) {}
    
        void Connect() {
            std::cout 
                << "Connecting to fake database " << details.host << "/" << details.schema
                << " with user " << details.username << " and password '" << std::string(details.password.size(), '*') << "'\n";
        }
      private:
        Database details;
    };
    
    struct TSession : std::enable_shared_from_this<TSession> {
        TSession(boost::asio::io_service& svc, std::shared_ptr<TDatabase> db) :
            _svc(svc), _socket(_svc), _db(db) {}
    
        tcp::socket& Socket() { return _socket; }
    
        void Start() {
            boost::asio::async_read(_socket, _sb, 
                    boost::bind(&TSession::HandleReceived, shared_from_this(),
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred));
        } 
    
        void HandleReceived(boost::system::error_code ec, size_t bytes_transferred) {
            if (!ec || boost::asio::error::eof == ec) {
                std::cout << "Received from " << _socket.remote_endpoint() << ": '" << &_sb << "'\n";
            } else
            {
                std::cout << "Error reading from peer: " << ec.message() << "\n";
            }
        }
    
      private:
        boost::asio::io_service& _svc;
        tcp::socket _socket;
        std::shared_ptr<TDatabase> _db;
        boost::asio::streambuf _sb;
    };
    
    struct TServer {
        tcp::acceptor acceptor;
        std::shared_ptr<TDatabase> database;
    
        TServer(boost::asio::io_service &io_service, std::shared_ptr<TDatabase> database, const boost::asio::ip::tcp::endpoint &endpoint)
                : acceptor(io_service, endpoint), database(database)
        {
            Accept_Connection();
        }
    
        void Accept_Connection() {
            auto Connection = std::make_shared<TSession>(acceptor.get_io_service(), database);
            acceptor.async_accept(Connection->Socket(),
                    boost::bind(&TServer::Handle_Connection, this, Connection, boost::asio::placeholders::error));
        }
    
        void Handle_Connection(std::shared_ptr<TSession> Connection, const boost::system::error_code &error) {
            if (!error) {
                Connection->Start();
                Accept_Connection();
            } else
                std::cout << "Error: " << error.message() << "\n";
        }
    };
    
    //#include "TServer.h"
    //#include "TDatabase.h"
    //#include "Includes.h"
    //#include "Structures.h"
    #include <boost/property_tree/ptree.hpp>
    #include <boost/property_tree/ini_parser.hpp>
    
    int main() {
        try {
            std::cout << "========================================" << std::endl
                      << "= Game Server v1.0 by Gravity1        =" << std::endl
                      << "========================================" << std::endl;
    
            boost::asio::io_service io_service;
            Database database;
            std::list<std::list<TServer> > servers;
            srand(time(0));
    
            boost::property_tree::ptree pt;
            boost::property_tree::read_ini("game_server_config.ini", pt);
    
            database.host     = pt.get<std::string>("DATABASE.HOST");
            database.username = pt.get<std::string>("DATABASE.USER");
            database.password = pt.get<std::string>("DATABASE.PASS");
            database.schema   = pt.get<std::string>("DATABASE.SCHEMA");
    
            std::shared_ptr<TDatabase> Database_ptr = std::make_shared<TDatabase>(database);
            Database_ptr->Connect();
    
            short server_count = pt.get<short>("GAME_SERVER.SERVER_COUNT");
    
            if (server_count > 0)
                servers.resize(server_count);
    
            auto current_server = servers.begin();
    
            for (int i = 0; i < server_count; i++, ++current_server) {
                short channel_count = pt.get<short>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL_COUNT");
    
                for (int j = 0; j < channel_count; j++) {
                    Canal CanalTemp;
                    CanalTemp.ip   = pt.get<std::string>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_IP");
                    CanalTemp.port = pt.get<short>("GAME_SERVER.SERVER_"       + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_PORT");
                    tcp::endpoint endpoint(boost::asio::ip::address::from_string(CanalTemp.ip), CanalTemp.port);
    
                    current_server->emplace_back(io_service, Database_ptr, endpoint);
                }
            }
    
            io_service.run();
        } 
        catch (std::exception &e) {
            std::cerr << e.what() << std::endl;
        }
    
        std::cin.get();
    }
    

    我使用了

    的配置
    [DATABASE]
    HOST=localhost
    USER=root
    PASS=youbet
    SCHEMA=my_game
    
    [GAME_SERVER]
    SERVER_COUNT=1
    SERVER_1_CHANNEL_COUNT=2
    SERVER_1_CHANNEL1_IP=127.0.0.1
    SERVER_1_CHANNEL1_PORT=6767
    SERVER_1_CHANNEL2_IP=127.0.0.2
    SERVER_1_CHANNEL2_PORT=6868
    

    当在两个频道(端口6767和6868)上运行客户端时,打印出一个&#34;无尽的&#34;重复:

    ========================================
    = Game Server v1.0 by Gravity1        =
    ========================================
    Connecting to fake database localhost/my_game with user root and password '******'
    Received from 127.0.0.1:54942: 'hello channel
    '
    Received from 127.0.0.1:37217: 'hello OTHER channel
    '
    Received from 127.0.0.1:54945: 'hello channel
    '
    Received from 127.0.0.1:37220: 'hello OTHER channel
    '
    Received from 127.0.0.1:54947: 'hello channel
    '
    Received from 127.0.0.1:37222: 'hello OTHER channel
    '
    Received from 127.0.0.1:54949: 'hello channel
    '
    Received from 127.0.0.1:37224: 'hello OTHER channel
    '
    Received from 127.0.0.1:54951: 'hello channel
    '
    Received from 127.0.0.1:37226: 'hello OTHER channel
    '
    Received from 127.0.0.1:54953: 'hello channel
    '
    Received from 127.0.0.1:37228: 'hello OTHER channel
    '
    Received from 127.0.0.1:54955: 'hello channel
    '
    Received from 127.0.0.1:37230: 'hello OTHER channel
    '
    Received from 127.0.0.1:54957: 'hello channel
    '
    Received from 127.0.0.1:37232: 'hello OTHER channel
    '
    

答案 1 :(得分:0)

完全不相关,但您的配置格式对于层次格式(如JSON或XML)确实 begs

为了好玩,我重构了该示例以使用XML:

<?xml version="1.0"?>
<CONFIG>
  <DATABASE>
    <HOST>localhost</HOST>
    <USER>root</USER>
    <PASS>youbet</PASS>
    <SCHEMA>my_game</SCHEMA>
  </DATABASE>
  <GAME_SERVER>
    <SERVER>
      <CHANNEL>
        <IP>127.0.0.1</IP>
        <PORT>6767</PORT>
      </CHANNEL>
      <CHANNEL>
        <IP>127.0.0.2</IP>
        <PORT>6868</PORT>
      </CHANNEL>
    </SERVER>
  </GAME_SERVER>
</CONFIG>

您可以使用以下代码段阅读:

boost::property_tree::ptree pt;
boost::property_tree::read_xml("game_server_config.xml", pt);

if (auto dbconfig = pt.get_child_optional("CONFIG.DATABASE")) {
    database.host     = dbconfig->get<std::string>("HOST");
    database.username = dbconfig->get<std::string>("USER");
    database.password = dbconfig->get<std::string>("PASS");
    database.schema   = dbconfig->get<std::string>("SCHEMA");
}

对于服务器/频道:

for (auto& serverconfig: pt.get_child("CONFIG.GAME_SERVER")) {
    if ("SERVER" != serverconfig.first)
        continue;

    servers.emplace_back();
    auto& current_server = servers.back();

    for (auto& channelconfig: serverconfig.second) {
        if ("CHANNEL" != channelconfig.first)
            continue;

        Canal CanalTemp;
        CanalTemp.ip   = channelconfig.second.get<std::string>("IP");
        CanalTemp.port = channelconfig.second.get<short>("PORT");
        tcp::endpoint endpoint(boost::asio::ip::address::from_string(CanalTemp.ip), CanalTemp.port);

        current_server.emplace_back(io_service, Database_ptr, endpoint);
    }
}

同时查看 Live On Coliru :)

#include <boost/asio.hpp>
#include <boost/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind.hpp>
#include <iostream>
// for iterator and reference stability (see:
// http://stackoverflow.com/questions/6438086/iterator-invalidation-rules)
#include <list> 

using tcp = boost::asio::ip::tcp;

struct Canal {
    std::string ip;
    int port;
};

struct Database {
    std::string host, username, password, schema;
};

struct TDatabase {
    TDatabase(Database config) : details(config) {}

    void Connect() {
        std::cout 
            << "Connecting to fake database " << details.host << "/" << details.schema
            << " with user " << details.username << " and password '" << std::string(details.password.size(), '*') << "'\n";
    }
  private:
    Database details;
};

struct TSession : std::enable_shared_from_this<TSession> {
    TSession(boost::asio::io_service& svc, std::shared_ptr<TDatabase> db) :
        _svc(svc), _socket(_svc), _db(db) {}

    tcp::socket& Socket() { return _socket; }

    void Start() {
        boost::asio::async_read(_socket, _sb, 
                boost::bind(&TSession::HandleReceived, shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));
    } 

    void HandleReceived(boost::system::error_code ec, size_t bytes_transferred) {
        if (!ec || boost::asio::error::eof == ec) {
            std::cout << "Received from " << _socket.remote_endpoint() << ": '" << &_sb << "'\n";
        } else
        {
            std::cout << "Error reading from peer: " << ec.message() << "\n";
        }
    }

  private:
    boost::asio::io_service& _svc;
    tcp::socket _socket;
    std::shared_ptr<TDatabase> _db;
    boost::asio::streambuf _sb;
};

struct TServer {
    tcp::acceptor acceptor;
    std::shared_ptr<TDatabase> database;

    TServer(boost::asio::io_service &io_service, std::shared_ptr<TDatabase> database, const boost::asio::ip::tcp::endpoint &endpoint)
            : acceptor(io_service, endpoint), database(database)
    {
        Accept_Connection();
    }

    void Accept_Connection() {
        auto Connection = std::make_shared<TSession>(acceptor.get_io_service(), database);
        acceptor.async_accept(Connection->Socket(),
                boost::bind(&TServer::Handle_Connection, this, Connection, boost::asio::placeholders::error));
    }

    void Handle_Connection(std::shared_ptr<TSession> Connection, const boost::system::error_code &error) {
        if (!error) {
            Connection->Start();
            Accept_Connection();
        } else
            std::cout << "Error: " << error.message() << "\n";
    }
};

//#include "TServer.h"
//#include "TDatabase.h"
//#include "Includes.h"
//#include "Structures.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>

int main() {
    try {
        std::cout << "========================================" << std::endl
                  << "= Game Server v1.0 by Gravity1        =" << std::endl
                  << "========================================" << std::endl;

        boost::asio::io_service io_service;
        Database database;
        std::list<std::list<TServer> > servers;
        srand(time(0));

        boost::property_tree::ptree pt;
        boost::property_tree::read_xml("game_server_config.xml", pt);

        if (auto dbconfig = pt.get_child_optional("CONFIG.DATABASE")) {
            database.host     = dbconfig->get<std::string>("HOST");
            database.username = dbconfig->get<std::string>("USER");
            database.password = dbconfig->get<std::string>("PASS");
            database.schema   = dbconfig->get<std::string>("SCHEMA");
        }

        std::shared_ptr<TDatabase> Database_ptr = std::make_shared<TDatabase>(database);
        Database_ptr->Connect();

        for (auto& serverconfig: pt.get_child("CONFIG.GAME_SERVER")) {
            if ("SERVER" != serverconfig.first)
                continue;

            servers.emplace_back();
            auto& current_server = servers.back();

            for (auto& channelconfig: serverconfig.second) {
                if ("CHANNEL" != channelconfig.first)
                    continue;

                Canal CanalTemp;
                CanalTemp.ip   = channelconfig.second.get<std::string>("IP");
                CanalTemp.port = channelconfig.second.get<short>("PORT");
                tcp::endpoint endpoint(boost::asio::ip::address::from_string(CanalTemp.ip), CanalTemp.port);

                current_server.emplace_back(io_service, Database_ptr, endpoint);
            }
        }

        io_service.run();
    } 
    catch (std::exception &e) {
        std::cerr << e.what() << std::endl;
    }

    std::cin.get();
}

打印

========================================
= Game Server v1.0 by Gravity1        =
========================================
Connecting to fake database localhost/my_game with user root and password '******'
Received from 127.0.0.1:55712: 'hello channel
'
Received from 127.0.0.1:37987: 'hello OTHER channel
'
Received from 127.0.0.1:55714: 'hello channel
'
Received from 127.0.0.1:37989: 'hello OTHER channel
'
Received from 127.0.0.1:55716: 'hello channel
'