Chops Net IP
Loading...
Searching...
No Matches
tcp_acceptor.hpp
Go to the documentation of this file.
1
18#ifndef TCP_ACCEPTOR_HPP_INCLUDED
19#define TCP_ACCEPTOR_HPP_INCLUDED
20
21#include "asio/ip/tcp.hpp"
22#include "asio/io_context.hpp"
23#include "asio/post.hpp"
24
25#include <system_error>
26#include <memory>// std::shared_ptr, std::weak_ptr
27#include <vector>
28#include <utility> // std::move, std::forward
29#include <cstddef> // for std::size_t
30#include <functional> // std::bind
31#include <string>
32#include <string_view>
33#include <future>
34#include <chrono>
35
39
41
42namespace chops {
43namespace net {
44namespace detail {
45
46class tcp_acceptor : public std::enable_shared_from_this<tcp_acceptor> {
47public:
48 using endpoint_type = asio::ip::tcp::endpoint;
49
50private:
51 net_entity_common<tcp_io> m_entity_common;
52 asio::io_context& m_ioc;
53 asio::ip::tcp::acceptor m_acceptor;
54 std::vector<tcp_io_shared_ptr> m_io_handlers;
55 endpoint_type m_acceptor_endp;
56 std::string m_local_port_or_service;
57 std::string m_listen_intf;
58 bool m_reuse_addr;
59 bool m_shutting_down;
60
61public:
62 tcp_acceptor(asio::io_context& ioc, const endpoint_type& endp,
63 bool reuse_addr) :
64 m_entity_common(), m_ioc(ioc), m_acceptor(ioc), m_io_handlers(), m_acceptor_endp(endp),
65 m_local_port_or_service(), m_listen_intf(),
66 m_reuse_addr(reuse_addr), m_shutting_down(false) { }
67
68 tcp_acceptor(asio::io_context& ioc,
69 std::string_view local_port_or_service, std::string_view listen_intf,
70 bool reuse_addr) :
71 m_entity_common(), m_ioc(ioc), m_acceptor(ioc), m_io_handlers(), m_acceptor_endp(),
72 m_local_port_or_service(local_port_or_service), m_listen_intf(listen_intf),
73 m_reuse_addr(reuse_addr), m_shutting_down(false) { }
74
75private:
76 // no copy or assignment semantics for this class
77 tcp_acceptor(const tcp_acceptor&) = delete;
78 tcp_acceptor(tcp_acceptor&&) = delete;
79 tcp_acceptor& operator=(const tcp_acceptor&) = delete;
80 tcp_acceptor& operator=(tcp_acceptor&&) = delete;
81
82public:
83
84 bool is_started() const noexcept { return m_entity_common.is_started(); }
85
86 template <typename F>
87 void visit_socket(F&& f) {
88 f(m_acceptor);
89 }
90
91 template <typename F>
92 std::size_t visit_io_output(F&& func) {
93 auto self = shared_from_this();
94 std::promise<std::size_t> prom;
95 auto fut = prom.get_future();
96 // send to executor for concurrency protection
97 asio::post(m_ioc, [this, self, &func, p = std::move(prom)] () mutable {
98 std::size_t sum = 0u;
99 if (m_shutting_down) {
100 p.set_value(sum);
101 return;
102 }
103 for (auto& ioh : m_io_handlers) {
104 if (ioh->is_io_started()) {
105 func(basic_io_output<tcp_io>(ioh));
106 sum += 1u;
107 }
108 }
109 p.set_value(sum);
110 }
111 );
112 return fut.get();
113 }
114
115 template <typename F1, typename F2>
116 std::error_code start(F1&& io_state_chg, F2&& err_func) {
117 auto self = shared_from_this();
118 return m_entity_common.start(std::forward<F1>(io_state_chg), std::forward<F2>(err_func),
119 m_acceptor.get_executor(),
120 [this, self] () { return do_start(); } );
121 }
122
123 std::error_code stop() {
124 auto self = shared_from_this();
125 return m_entity_common.stop(m_acceptor.get_executor(),
126 [this, self] () {
127 close(std::make_error_code(net_ip_errc::tcp_acceptor_stopped));
128 return std::error_code();
129 }
130 );
131 }
132
133private:
134
135 std::error_code do_start() {
136 if (!m_local_port_or_service.empty()) {
137 endpoints_resolver<asio::ip::tcp> resolver(m_ioc);
138 auto ret = resolver.make_endpoints(true, m_listen_intf, m_local_port_or_service);
139 if (!ret) {
140 close(ret.error());
141 return ret.error();
142 }
143 m_acceptor_endp = ret->cbegin()->endpoint();
144 m_local_port_or_service.clear();
145 m_local_port_or_service.shrink_to_fit();
146 m_listen_intf.clear();
147 m_listen_intf.shrink_to_fit();
148 }
149 std::error_code ec;
150 m_acceptor.open(m_acceptor_endp.protocol(), ec);
151 if (ec) {
152 close(ec);
153 return ec;
154 }
155 if (m_reuse_addr) {
156 m_acceptor.set_option(asio::socket_base::reuse_address(true), ec);
157 if (ec) {
158 close(ec);
159 return ec;
160 }
161 }
162 m_acceptor.bind(m_acceptor_endp, ec);
163 if (ec) {
164 close(ec);
165 return ec;
166 }
167 m_acceptor.listen(asio::socket_base::max_listen_connections, ec);
168 if (ec) {
169 close(ec);
170 return ec;
171 }
172
173 start_accept();
174 return { };
175 }
176
177 void close(const std::error_code& err) {
178 m_entity_common.call_error_cb(tcp_io_shared_ptr(), err);
179 if (m_shutting_down) {
180 return; // already shutting down, bypass closing again
181 }
182 m_shutting_down = true;
183 m_entity_common.set_stopped(); // in case of internal call to close
184 // the following copy is important, since the notify_me modifies the
185 // m_io_handlers container
186 auto iohs = m_io_handlers;
187 for (auto& i : iohs) {
188 i->stop_io();
189 }
190 // m_io_handlers.clear(); // the stop_io on each tcp_io handler should clear the container
191 std::error_code ec;
192 m_acceptor.close(ec);
193 if (ec) {
194 m_entity_common.call_error_cb(tcp_io_shared_ptr(), ec);
195 }
196 m_entity_common.call_error_cb(tcp_io_shared_ptr(),
197 std::make_error_code(net_ip_errc::tcp_acceptor_closed));
198 }
199
200private:
201
202 void start_accept() {
203 using namespace std::placeholders;
204
205 if (m_shutting_down) {
206 return;
207 }
208 auto self = shared_from_this();
209 m_acceptor.async_accept( [this, self]
210 (const std::error_code& err, asio::ip::tcp::socket sock) {
211 if (err || m_shutting_down ) {
212 return;
213 }
214 tcp_io_shared_ptr iop = std::make_shared<tcp_io>(std::move(sock),
215 tcp_io::entity_notifier_cb(std::bind(&tcp_acceptor::notify_me, shared_from_this(), _1, _2)));
216 m_io_handlers.push_back(iop);
217 // make sure app doesn't do any strangeness during callback
218 // even if another accept completes, post order should invoke callback before next
219 // accept handler is invoked
220 asio::post(m_ioc, [this, self, iop] () {
221 m_entity_common.call_io_state_chg_cb(iop, m_io_handlers.size(), true);
222 }
223 );
224 start_accept();
225 }
226 );
227 }
228
229 // this code invoked via a posted function object, allowing the TCP IO handler
230 // to completely shut down
231 void notify_me(std::error_code err, tcp_io_shared_ptr iop) {
232 std::erase_if (m_io_handlers, [iop] (auto sp) { return iop == sp; } );
233 m_entity_common.call_error_cb(iop, err);
234 m_entity_common.call_io_state_chg_cb(iop, m_io_handlers.size(), false);
235 }
236
237};
238
239using tcp_acceptor_shared_ptr = std::shared_ptr<tcp_acceptor>;
240using tcp_acceptor_weak_ptr = std::weak_ptr<tcp_acceptor>;
241
242} // end detail namespace
243} // end net namespace
244} // end chops namespace
245
246
247#endif
248
basic_io_output class template, providing send and get_output_queue_stats methods.
The basic_io_output class template provides methods for sending data to an associated network IO hand...
Definition basic_io_output.hpp:57
Definition net_entity_common.hpp:49
Definition tcp_acceptor.hpp:46
Convenience class for resolving names to endpoints suitable for use within the Chops Net IP library (...
Definition endpoints_resolver.hpp:53
void make_endpoints(bool local, std::string_view host_or_intf_name, std::string_view service_or_port, F &&func)
Create a sequence of endpoints and return them in a function object callback.
Definition endpoints_resolver.hpp:96
Class to convert network host names and ports into Asio endpoint objects.
Common code, factored out, for TCP acceptor, TCP connector, and UDP net entity handlers.
Definition tcp_io_test.cpp:56
Internal handler class for TCP stream input and output.