如何创建监听两个不同端口的 boost::asio 服务器

how to create boost::asio server that listen to two different ports

我正在使用 boost 1.75.0 并且正在尝试更新我的服务器以便他可以同时收听两个不同的端口 假设 IP 127.0.0.1 port 6500 and port 6600. 我需要保持 Server 两个插座吗? 这是我的服务器

#include <boost/asio/io_service.hpp>
#include <boost/asio.hpp>
#include <queue>
#include "Session.h"
using boost::asio::ip::tcp;

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();
  }

 private:
  void do_accept(){
    acceptor_.async_accept(socket_,
                           [this](boost::system::error_code ec) {
                             if (!ec) {
                               std::cout << "accept connection\n";

                               std::make_shared<Session>(std::move(socket_))->start();
                             }

                             do_accept();
                           });
  }


  tcp::acceptor acceptor_;
  tcp::socket socket_;

};

这是我的会话class

#include <boost/asio/io_service.hpp>
#include <boost/asio.hpp>
#include "message.h"
#include <fstream>
#include <boost/bind/bind.hpp>

namespace {
    using boost::asio::ip::tcp;
    auto constexpr log_active=true;
    using boost::system::error_code;
    using namespace std::chrono_literals;
    using namespace boost::posix_time;
};


class Session
    : public std::enable_shared_from_this<Session>
{
 public:
  Session(tcp::socket socket)
      : socket_(std::move(socket)) ,
  {
  }
  ~Session()= default;

  void start();
  void do_write_alive();


 private:
  void do_read_header();
  void do_read_body();
  void do_write();
  using Strand = boost::asio::strand<tcp::socket::executor_type>;
  using Timer  = boost::asio::steady_timer;

  tcp::socket socket_{strand_};
  Strand      strand_{make_strand(socket_.get_executor())};
  Timer       recv_deadline_{strand_};
  Timer       send_deadline_{strand_};
  enum { max_length = 1024 };
  char data_[max_length];
};

我没有包括 Session class 只有构造函数的实现,因为那里不相关。

你需要两个受体。

您可以不使用 socket_ 实例,同时删除重复项。

这是一个最小的重构,运行s 10 个监听器,存储在双端队列中(为了参考稳定性):

class Server {
  public:
    Server(boost::asio::io_service& svc, uint16_t base_port) {
        for (uint16_t port = base_port; port < base_port + 10; ++port) {
            auto& a = acceptors_.emplace_back(svc, tcp::endpoint{{}, port});
            a.listen();
            accept_loop(a);
        }
    }

    void stop() {
        for (auto& a: acceptors_)
            a.cancel();
    }

  private:
    static void accept_loop(tcp::acceptor& a) {
        a.async_accept(a.get_executor(), [&a](error_code ec, tcp::socket&& s) {
            if (!ec) {
                std::cout << "accepted " << s.remote_endpoint() << " on :" << a.local_endpoint().port() << std::endl;
                std::make_shared<Session>(std::move(s))->start();

                accept_loop(a);
            } else {
                std::cout << "exit accept_loop " << ec.message() << std::endl;
            }
        });
    }

    std::deque<tcp::acceptor> acceptors_;
};

使用io_context和执行器

您可以使用执行器模型对其进行更多现代化改造:

Live On Coliru

#include <boost/asio.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/bind/bind.hpp>
#include <fstream>
#include <deque>

namespace {
    using boost::asio::ip::tcp;
    //auto constexpr log_active = true;
    using boost::system::error_code;
    using namespace std::chrono_literals;
    //using namespace boost::posix_time;
} // namespace

class Session : public std::enable_shared_from_this<Session> {
  public:
    Session(tcp::socket socket) : socket_(std::move(socket)) {}
    ~Session() = default;

    void start() {}
    void do_write_alive() {}

  private:
    void do_read_header() {}
    void do_read_body() {}
    void do_write() {}

    using Strand = boost::asio::strand<tcp::socket::executor_type>;
    using Timer  = boost::asio::steady_timer;

    tcp::socket socket_{strand_};
    Strand      strand_{make_strand(socket_.get_executor())};
    Timer       recv_deadline_{strand_};
    Timer       send_deadline_{strand_};
    enum { max_length = 1024 };
    char data_[max_length];
};

#include <boost/asio.hpp>
#include <boost/asio/io_service.hpp>
#include <queue>
#include <iostream>
using boost::asio::ip::tcp;

class Server {
  public:
    template <typename Ex>
    Server(Ex executor, uint16_t base_port) {
        for (uint16_t port = base_port; port < base_port + 10; ++port) {
            auto& a = acceptors_.emplace_back(executor, tcp::endpoint{{}, port});
            a.listen();
            accept_loop(a);
        }
    }

    void stop() {
        for (auto& a: acceptors_)
            a.cancel();
    }

  private:
    static void accept_loop(tcp::acceptor& a) {
        a.async_accept(a.get_executor(), [&a](error_code ec, tcp::socket&& s) {
            if (!ec) {
                std::cout << "accepted " << s.remote_endpoint() << " on :" << a.local_endpoint().port() << std::endl;
                std::make_shared<Session>(std::move(s))->start();

                accept_loop(a);
            } else {
                std::cout << "exit accept_loop " << ec.message() << std::endl;
            }
        });
    }

    std::deque<tcp::acceptor> acceptors_;
};

int main() {
    boost::asio::io_context context;
    Server s(context.get_executor(), 7878);

    context.run_for(10s);

    s.stop();
    context.run();
}

另请注意,现在可以取消接受循环。使用示例客户端 运行 例如

(for p in {7878..7887}; do (sleep 1.$RANDOM; nc localhost $p <<<"HELLO")& done; wait)

打印例如

accepted 127.0.0.1:37958 on :7880
accepted 127.0.0.1:45100 on :7885
accepted 127.0.0.1:42414 on :7883
accepted 127.0.0.1:49988 on :7886
accepted 127.0.0.1:44898 on :7878
accepted 127.0.0.1:43536 on :7879
accepted 127.0.0.1:35350 on :7882
accepted 127.0.0.1:40158 on :7887
accepted 127.0.0.1:53022 on :7884
accepted 127.0.0.1:39020 on :7881
exit accept_loop Operation canceled
exit accept_loop Operation canceled
exit accept_loop Operation canceled
exit accept_loop Operation canceled
exit accept_loop Operation canceled
exit accept_loop Operation canceled
exit accept_loop Operation canceled
exit accept_loop Operation canceled
exit accept_loop Operation canceled
exit accept_loop Operation canceled