首页 > 解决方案 > 如何创建监听两个不同端口的 boost::asio 服务器

问题描述

我正在使用 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_;

};

这是我的会话课

#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只包括构造函数的类的实现,因为那里不相关。

标签: c++boostboost-asio

解决方案


你需要两个接受者。

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

这是一个运行 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和执行者

您可以通过使用 executor 模型对其进行更多现代化改造:

住在科利鲁

#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

推荐阅读