C++ 如何使用Boost ASIO创建到服务器的多个连接

C++ 如何使用Boost ASIO创建到服务器的多个连接,c++,boost-asio,C++,Boost Asio,我使用Boost Asio作为TCP网络解决方案 这是我的服务器代码: Server.h: #ifndef VIBRANIUM_CORE_SERVER_H #define VIBRANIUM_CORE_SERVER_H #include <cstdlib> #include <iostream> #include <memory> #include <utility> #include <boost/asio.hpp> #include

我使用Boost Asio作为TCP网络解决方案

这是我的服务器代码:

Server.h

#ifndef VIBRANIUM_CORE_SERVER_H
#define VIBRANIUM_CORE_SERVER_H
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
#include <deque>
#include "Logger.h"
#include "Client.h"

using boost::asio::ip::tcp;
namespace Vibranium {
    class Server {
    public:
        Server(boost::asio::io_service &io_service, short port)
                : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
                  socket_(io_service) {
            do_accept();
            Logger::Log("Server Started! Listening on Port("+std::to_string(port)+")", Logger::Success, true);
        }
        static std::deque<std::shared_ptr<Client>> Clients;

    private:
        void do_accept();
        int incrementor;
        tcp::acceptor acceptor_;
        tcp::socket socket_;
    };

}


#endif //VIBRANIUM_CORE_SERVER_H
#ifndef VIBRANIUM_CORE_CLIENT_H
#define VIBRANIUM_CORE_CLIENT_H

#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>

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

namespace Vibranium{
    class Client: public std::enable_shared_from_this<Client>
    {
    public:
        Client(tcp::socket socket)
        : socket_(std::move(socket))
        {
        }
        void start();
        int connectionId;
    private:
        void do_read();
        void do_write(std::size_t length);
        tcp::socket socket_;
        enum { max_length = 1024 };
        char data_[max_length];
    };
}
#endif //VIBRANIUM_CORE_CLIENT_H
Client.h

#ifndef VIBRANIUM_CORE_SERVER_H
#define VIBRANIUM_CORE_SERVER_H
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
#include <deque>
#include "Logger.h"
#include "Client.h"

using boost::asio::ip::tcp;
namespace Vibranium {
    class Server {
    public:
        Server(boost::asio::io_service &io_service, short port)
                : acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
                  socket_(io_service) {
            do_accept();
            Logger::Log("Server Started! Listening on Port("+std::to_string(port)+")", Logger::Success, true);
        }
        static std::deque<std::shared_ptr<Client>> Clients;

    private:
        void do_accept();
        int incrementor;
        tcp::acceptor acceptor_;
        tcp::socket socket_;
    };

}


#endif //VIBRANIUM_CORE_SERVER_H
#ifndef VIBRANIUM_CORE_CLIENT_H
#define VIBRANIUM_CORE_CLIENT_H

#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>

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

namespace Vibranium{
    class Client: public std::enable_shared_from_this<Client>
    {
    public:
        Client(tcp::socket socket)
        : socket_(std::move(socket))
        {
        }
        void start();
        int connectionId;
    private:
        void do_read();
        void do_write(std::size_t length);
        tcp::socket socket_;
        enum { max_length = 1024 };
        char data_[max_length];
    };
}
#endif //VIBRANIUM_CORE_CLIENT_H
以下是我如何启动服务器:

#include "Config.h"
#include "Database/MySQLConnection.h"
#include "Implementation/LoginDatabase.h"
#include "Banner.h"
#include "Server/Server.h"
#include <boost/asio.hpp>

using boost::asio::ip::tcp;
using namespace std;
using namespace Vibranium;

int main() {
    //Don't mind Logger::FatalError it's just for coloring!
    Banner::Show(Logger::Error,"AuthServer");
    Config config("AuthServer");
    std::string defaultPort = "8080";
    MySQLConnectionInfo mySqlConnectionInfo(config, "LoginDatabaseInfo");
    LoginDatabaseConnection loginDatabaseConnection(mySqlConnectionInfo);
    loginDatabaseConnection.LoadDatabase();

    try
    {
        boost::asio::io_service io_service;
        Server s(io_service, std::stoi(config.GetConfigValue("AuthServerPort", defaultPort)));
        io_service.run();
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}
Server Started! Listening on Port(8085)
New Connection (ID: 1)
I am testing here!!!
Disconnected ID: 1
New Connection (ID: 2)
I am testing here!!!
Disconnected ID: 2
New Connection (ID: 3)
I am testing here!!!
Disconnected ID: 3
我正在初始化从同一台PC到服务器的连接,但是从另一个C++目标可执行文件初始化。因此,我已经启动并运行了服务器可执行文件,另一个可执行文件正在尝试创建多个连接并同时保持它们


我如何使我的连接保持不变,而不是在创建连接后直接断开?我的错误在哪里?如何修复它?

当它超出范围时,
tcp::socket
将关闭它的连接()。如果要保持连接打开,必须保持对象处于活动状态

您可以通过将
io_上下文
移出客户机示例中的循环并保留一些用于保存套接字的列表来实现这一点:

  boost::asio::io_context io_context;
  std::vector<tcp::socket> sockets{};

  for (int i = 0; i < connectionsNumber; ++i) {

    try {
      sockets.emplace_back(io_context);
      tcp::socket& s{sockets.back()};

      // initialize socket as before ...

另一个注意事项:您没有显示
do\u write
的内容,但我假设您以某种方式实现此函数,再次调用
do\u read
,以便它能够处理最终的EOF或连接重置。

谢谢!不知何故,我错过了“编写”,但我现在添加了它。它实现了
do_read
check out;)当
io\u context
也超出范围时,连接是否关闭?这不是您想要的意义。如果io_上下文在向其注册套接字之前销毁,那么套接字析构函数将触发释放内存后使用错误,即未定义行为。您的程序可能运行正常,也可能运行不正常,但您无法保证会发生什么(这是非常糟糕的)。您可以通过在上面的示例中的套接字向量之后声明io_上下文(这样io_上下文析构函数将在套接字析构函数之前运行),然后在启用地址清理的情况下进行编译(
-fsanize=address
,使用clang或gcc)。然后,它应该报告免后使用。因此,io_上下文应该始终比注册到它的套接字更有效。另外,从性能角度来看,当您执行异步I/O时,共享同一io_上下文的套接字越多,您将获得更多的好处。当您有多个io_上下文对象,每个对象控制一个套接字时,在它们的工作线程之间切换会产生性能开销。而对于单个io_上下文,多个套接字可以由一个线程控制。您仍然可以为io_上下文分配一个线程组来平衡多个线程的负载,但这将比将每个套接字固定到单独的线程(当套接字上没有可用数据时,该线程将被强制处于空闲状态)更有效地平衡负载。因此,从性能的角度来看,您应该尽可能多地共享io_上下文。
  boost::asio::io_context io_context;
  std::vector<tcp::socket> sockets{};

  for (int i = 0; i < connectionsNumber; ++i) {

    try {
      sockets.emplace_back(io_context);
      tcp::socket& s{sockets.back()};

      // initialize socket as before ...
New Connection (ID: 2)
Hello, world!
New Connection (ID: 3)
Hello, world!
New Connection (ID: 4)
Hello, world!
Disconnected ID: 2
Disconnected ID: 3
Disconnected ID: 4