我正在编写一个服务器类,我希望能够支持任何流协议。
此页面提供了一些提示:https://beta.boost.org/doc/libs/1_83_0/doc/html/boost_asio/overview/networking/other_protocols.html
然而,受体部分却相当难以捉摸……
您是否有关于如何设置
acceptor
以与 boost::asio::generic::stream_protocol::endpoint
一起使用的示例(至少可以是 ip/TCP 或 unix 域)?
您需要有多个侦听器,例如
template <typename Socket> struct Listener {
using Proto = typename Socket::protocol_type;
using Acceptor = asio::basic_socket_acceptor<Proto>;
using Endpoint = Acceptor::endpoint_type;
Listener(asio::any_io_executor ex, Endpoint ep) : acc_(std::move(ex), std::move(ep)) { accept_loop(); }
private:
void accept_loop() {
acc_.async_accept([](error_code ec, Socket s) {
std::cout << "Accepted (" << ec.message() << ") from " << s.remote_endpoint() << std::endl;
if (!ec)
std::make_shared<Session<Socket>>(std::move(s))->Start();
});
}
Acceptor acc_;
};
正如您所看到的,会话类也是在套接字类型上模板化的,因此您只需要实现一次:
template <typename Socket> struct Session /*: BaseSession*/ {
Session(Socket s) : sock_(std::move(s)) {}
void Start() {
read_loop();
}
private:
void read_loop() {
async_read_until(sock_, asio::dynamic_buffer(message), "\n",
[this, self = shared_from_this()](error_code ec, size_t n) {
std::cout << "Received (" << ec.message() << "): " //
<< quoted(std::string_view(message).substr(0, n - 1)) //
<< std::endl;
if (!ec) {
message.erase(0, n);
read_loop();
}
});
}
std::string message;
Socket sock_;
};
如果您需要对所有会话实例进行通用操作,请考虑基类。
现在多服务器就这么简单:
struct MultiServer {
MultiServer(asio::any_io_executor ex, std::set<uint16_t> tcp_listen_ports, std::set<std::string> unix_domain_socket) {
for (auto p : tcp_listen_ports)
listeners_.push_back(std::make_shared<Listener<tcp::socket>>(ex, tcp::endpoint{{}, p}));
for (auto& s : unix_domain_socket)
listeners_.push_back(std::make_shared<Listener<uxd::socket>>(ex, s));
}
private:
std::vector<std::shared_ptr<void> > listeners_;
};
int main() {
asio::io_context ioc;
MultiServer multi(ioc.get_executor(), {8989, 7979}, {"socket1", "socket2"});
ioc.run_for(10s); // limited for Coliru
}
这将运行一个服务器,侦听 2 个 tcp 端口和 2 个 unix 域套接字,并在所有端口上生成相同的会话:
#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
#include <set>
namespace asio = boost::asio;
using namespace std::chrono_literals;
using boost::system::error_code;
using tcp = asio::ip::tcp;
using uxd = asio::local::stream_protocol;
struct BaseSession : std::enable_shared_from_this<BaseSession> {
// in case you need any "common" behavior
// virtual ~BaseSession() = default;
};
template <typename Socket> struct Session : BaseSession {
Session(Socket s) : sock_(std::move(s)) {}
void Start() {
read_loop();
}
private:
void read_loop() {
async_read_until(sock_, asio::dynamic_buffer(message), "\n",
[this, self = shared_from_this()](error_code ec, size_t n) {
std::cout << "Received (" << ec.message() << "): " //
<< quoted(std::string_view(message).substr(0, n - 1)) //
<< std::endl;
if (!ec) {
message.erase(0, n);
read_loop();
}
});
}
std::string message;
Socket sock_;
};
template <typename Socket> struct Listener {
using Proto = typename Socket::protocol_type;
using Acceptor = asio::basic_socket_acceptor<Proto>;
using Endpoint = Acceptor::endpoint_type;
Listener(asio::any_io_executor ex, Endpoint ep) : acc_(std::move(ex), std::move(ep)) { accept_loop(); }
private:
void accept_loop() {
acc_.async_accept([](error_code ec, Socket s) {
std::cout << "Accepted (" << ec.message() << ") from " << s.remote_endpoint() << std::endl;
if (!ec)
std::make_shared<Session<Socket>>(std::move(s))->Start();
});
}
Acceptor acc_;
};
struct MultiServer {
MultiServer(asio::any_io_executor ex, std::set<uint16_t> tcp_listen_ports, std::set<std::string> unix_domain_socket) {
for (auto p : tcp_listen_ports)
listeners_.push_back(std::make_shared<Listener<tcp::socket>>(ex, tcp::endpoint{{}, p}));
for (auto& s : unix_domain_socket)
listeners_.push_back(std::make_shared<Listener<uxd::socket>>(ex, s));
}
private:
std::vector<std::shared_ptr<void> > listeners_;
};
int main() {
asio::io_context ioc;
MultiServer multi(ioc.get_executor(), {8989, 7979}, {"socket1", "socket2"});
ioc.run_for(10s); // limited for Coliru
}