tcp_session.hpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. /*
  2. * Copyright (c) 2017-2023 zhllxt
  3. *
  4. * author : zhllxt
  5. * email : 37792738@qq.com
  6. *
  7. * Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. */
  10. #ifndef __ASIO2_TCP_SESSION_HPP__
  11. #define __ASIO2_TCP_SESSION_HPP__
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. #pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <asio2/base/detail/push_options.hpp>
  16. #include <asio2/base/session.hpp>
  17. #include <asio2/tcp/impl/tcp_keepalive_cp.hpp>
  18. #include <asio2/tcp/impl/tcp_send_op.hpp>
  19. #include <asio2/tcp/impl/tcp_recv_op.hpp>
  20. namespace asio2::detail
  21. {
  22. struct template_args_tcp_session : public tcp_tag
  23. {
  24. static constexpr bool is_session = true;
  25. static constexpr bool is_client = false;
  26. static constexpr bool is_server = false;
  27. using socket_t = asio::ip::tcp::socket;
  28. using buffer_t = asio::streambuf;
  29. using send_data_t = std::string_view;
  30. using recv_data_t = std::string_view;
  31. };
  32. ASIO2_CLASS_FORWARD_DECLARE_BASE;
  33. ASIO2_CLASS_FORWARD_DECLARE_TCP_BASE;
  34. ASIO2_CLASS_FORWARD_DECLARE_TCP_SERVER;
  35. ASIO2_CLASS_FORWARD_DECLARE_TCP_SESSION;
  36. template<class derived_t, class args_t = template_args_tcp_session>
  37. class tcp_session_impl_t
  38. : public session_impl_t <derived_t, args_t>
  39. , public tcp_keepalive_cp <derived_t, args_t>
  40. , public tcp_send_op <derived_t, args_t>
  41. , public tcp_recv_op <derived_t, args_t>
  42. , public tcp_tag
  43. {
  44. ASIO2_CLASS_FRIEND_DECLARE_BASE;
  45. ASIO2_CLASS_FRIEND_DECLARE_TCP_BASE;
  46. ASIO2_CLASS_FRIEND_DECLARE_TCP_SERVER;
  47. ASIO2_CLASS_FRIEND_DECLARE_TCP_SESSION;
  48. public:
  49. using super = session_impl_t <derived_t, args_t>;
  50. using self = tcp_session_impl_t<derived_t, args_t>;
  51. using args_type = args_t;
  52. using key_type = std::size_t;
  53. using buffer_type = typename args_t::buffer_t;
  54. using send_data_t = typename args_t::send_data_t;
  55. using recv_data_t = typename args_t::recv_data_t;
  56. public:
  57. /**
  58. * @brief constructor
  59. */
  60. explicit tcp_session_impl_t(
  61. session_mgr_t<derived_t> & sessions,
  62. listener_t & listener,
  63. std::shared_ptr<io_t> rwio,
  64. std::size_t init_buf_size,
  65. std::size_t max_buf_size
  66. )
  67. : super(sessions, listener, rwio, init_buf_size, max_buf_size, rwio->context())
  68. , tcp_keepalive_cp<derived_t, args_t>()
  69. , tcp_send_op <derived_t, args_t>()
  70. , tcp_recv_op <derived_t, args_t>()
  71. , rallocator_()
  72. , wallocator_()
  73. {
  74. this->set_silence_timeout(std::chrono::milliseconds(tcp_silence_timeout));
  75. this->set_connect_timeout(std::chrono::milliseconds(tcp_connect_timeout));
  76. }
  77. /**
  78. * @brief destructor
  79. */
  80. ~tcp_session_impl_t()
  81. {
  82. }
  83. protected:
  84. /**
  85. * @brief start the session for prepare to recv/send msg
  86. */
  87. template<typename C>
  88. inline void start(std::shared_ptr<ecs_t<C>> ecs)
  89. {
  90. derived_t& derive = this->derived();
  91. error_code ec = get_last_error();
  92. #if defined(ASIO2_ENABLE_LOG)
  93. // Used to test whether the behavior of different compilers is consistent
  94. static_assert(tcp_send_op<derived_t, args_t>::template has_member_dgram<self>::value,
  95. "The behavior of different compilers is not consistent");
  96. #endif
  97. ASIO2_ASSERT(this->sessions_.io_->running_in_this_thread());
  98. ASIO2_ASSERT(this->io_->get_thread_id() != std::thread::id{});
  99. #if defined(_DEBUG) || defined(DEBUG)
  100. this->is_stop_silence_timer_called_ = false;
  101. this->is_stop_connect_timeout_timer_called_ = false;
  102. this->is_disconnect_called_ = false;
  103. #endif
  104. std::shared_ptr<derived_t> this_ptr = derive.selfptr();
  105. error_code ec_ignore{};
  106. auto ep = this->socket_->lowest_layer().remote_endpoint(ec_ignore);
  107. if (!ec_ignore)
  108. {
  109. this->remote_endpoint_ = std::move(ep);
  110. }
  111. state_t expected = state_t::stopped;
  112. if (!this->state_.compare_exchange_strong(expected, state_t::starting))
  113. {
  114. derive._do_disconnect(asio::error::already_started, std::move(this_ptr));
  115. return;
  116. }
  117. // must read/write ecs in the io_context thread.
  118. derive.ecs_ = ecs;
  119. // init function maybe change the last error.
  120. derive._do_init(this_ptr, ecs);
  121. // if the accept function has error, reset the last error to it.
  122. if (ec)
  123. {
  124. set_last_error(ec);
  125. }
  126. // now, the fire accept maybe has error, so the user should check it.
  127. derive._fire_accept(this_ptr);
  128. // if the accept has error, disconnect this session.
  129. if (ec)
  130. {
  131. derive._do_disconnect(ec, std::move(this_ptr));
  132. return;
  133. }
  134. // user maybe called the session stop in the accept callbak, so we need check it.
  135. expected = state_t::starting;
  136. if (!this->state_.compare_exchange_strong(expected, state_t::starting))
  137. {
  138. derive._do_disconnect(asio::error::operation_aborted, std::move(this_ptr));
  139. return;
  140. }
  141. // user maybe closed the socket in the accept callbak, so we need check it.
  142. if (!derive.socket().is_open())
  143. {
  144. derive._do_disconnect(asio::error::operation_aborted, std::move(this_ptr));
  145. return;
  146. }
  147. // First call the base class start function
  148. super::start();
  149. // if the ecs has remote data call mode,do some thing.
  150. derive._rdc_init(ecs);
  151. // use push event to avoid this problem in ssl session:
  152. // 1. _post_handshake not completed, this means the handshake callback hasn't been called.
  153. // 2. call session stop, then the ssl async shutdown will be called.
  154. // 3. then "ASIO2_ASSERT(derive.post_send_counter_.load() == 0);" will be failed.
  155. derive.push_event(
  156. [&derive, this_ptr = std::move(this_ptr), ecs = std::move(ecs)]
  157. (event_queue_guard<derived_t> g) mutable
  158. {
  159. derive.sessions_.dispatch(
  160. [&derive, this_ptr, ecs = std::move(ecs), g = std::move(g)]
  161. () mutable
  162. {
  163. derive._handle_connect(
  164. error_code{}, std::move(this_ptr), std::move(ecs), defer_event(std::move(g)));
  165. });
  166. });
  167. }
  168. public:
  169. /**
  170. * @brief stop session
  171. * You can call this function in the communication thread and anywhere to stop the session.
  172. * If this function is called in the communication thread, it will post a asynchronous
  173. * event into the event queue, then return immediately.
  174. * If this function is called not in the communication thread, it will blocking forever
  175. * util the session is stopped completed.
  176. * note : this function must be noblocking if it is called in the communication thread,
  177. * otherwise if it's blocking, maybe cause circle lock.
  178. * If the session stop is called in the server's bind connect callback, then the session
  179. * will can't be added into the session manager, and the session's bind disconnect event
  180. * can't be called also.
  181. */
  182. inline void stop()
  183. {
  184. derived_t& derive = this->derived();
  185. state_t expected = state_t::stopped;
  186. if (this->state_.compare_exchange_strong(expected, state_t::stopped))
  187. return;
  188. expected = state_t::stopping;
  189. if (this->state_.compare_exchange_strong(expected, state_t::stopping))
  190. return;
  191. // if user call session stop in the bind accept callback, we close the connection with RST.
  192. // after test, if close the connection with RST, no timewait will be generated.
  193. if (derive.sessions_.io_->running_in_this_thread())
  194. {
  195. if (this->state_ == state_t::starting)
  196. {
  197. // How to close the socket with RST instead of FIN/ACK/FIN/ACK ?
  198. // set the linger with 1,0
  199. derive.set_linger(true, 0);
  200. // close the socket directly.
  201. error_code ec_ignore;
  202. derive.socket().close(ec_ignore);
  203. }
  204. }
  205. // use promise to get the result of stop
  206. std::promise<state_t> promise;
  207. std::future<state_t> future = promise.get_future();
  208. // use derfer to ensure the promise's value must be seted.
  209. detail::defer_event pg
  210. {
  211. [this, p = std::move(promise)]() mutable
  212. {
  213. p.set_value(this->state_.load());
  214. }
  215. };
  216. derive.post_event([&derive, this_ptr = derive.selfptr(), pg = std::move(pg)]
  217. (event_queue_guard<derived_t> g) mutable
  218. {
  219. derive._do_disconnect(asio::error::operation_aborted, derive.selfptr(), defer_event
  220. {
  221. [&derive, this_ptr = std::move(this_ptr), pg = std::move(pg)]
  222. (event_queue_guard<derived_t> g) mutable
  223. {
  224. detail::ignore_unused(derive, pg, g);
  225. // the "pg" should destroyed before the "g", otherwise if the "g"
  226. // is destroyed before "pg", the next event maybe called, then the
  227. // state maybe change to not stopped.
  228. {
  229. [[maybe_unused]] detail::defer_event t{ std::move(pg) };
  230. }
  231. }, std::move(g)
  232. });
  233. });
  234. // use this to ensure the client is stopped completed when the stop is called not in the io_context thread
  235. while (!derive.running_in_this_thread() && !derive.sessions_.io_->running_in_this_thread())
  236. {
  237. std::future_status status = future.wait_for(std::chrono::milliseconds(100));
  238. if (status == std::future_status::ready)
  239. {
  240. ASIO2_ASSERT(future.get() == state_t::stopped);
  241. break;
  242. }
  243. else
  244. {
  245. if (derive.get_thread_id() == std::thread::id{})
  246. break;
  247. if (derive.sessions_.io_->get_thread_id() == std::thread::id{})
  248. break;
  249. if (derive.io_->context().stopped())
  250. break;
  251. }
  252. }
  253. }
  254. /**
  255. * @brief get this object hash key,used for session map
  256. */
  257. inline key_type hash_key() const noexcept
  258. {
  259. return reinterpret_cast<key_type>(this);
  260. }
  261. protected:
  262. template<class T, class R, class... Args>
  263. struct condition_has_member_init : std::false_type {};
  264. template<class T, class... Args>
  265. struct condition_has_member_init<T, decltype(std::declval<std::decay_t<T>>().
  266. init((std::declval<Args>())...)), Args...> : std::true_type {};
  267. template<typename C>
  268. inline void _do_init(std::shared_ptr<derived_t>& this_ptr, std::shared_ptr<ecs_t<C>>& ecs) noexcept
  269. {
  270. detail::ignore_unused(this_ptr, ecs);
  271. // reset the variable to default status
  272. this->derived().reset_connect_time();
  273. this->derived().update_alive_time();
  274. if constexpr (std::is_same_v<typename ecs_t<C>::condition_lowest_type, use_dgram_t>)
  275. {
  276. this->dgram_ = true;
  277. }
  278. else
  279. {
  280. this->dgram_ = false;
  281. }
  282. using condition_lowest_type = typename detail::remove_cvref_t<typename ecs_t<C>::condition_lowest_type>;
  283. if constexpr (std::is_class_v<condition_lowest_type>)
  284. {
  285. if constexpr (condition_has_member_init<condition_lowest_type, void, std::shared_ptr<derived_t>&>::value)
  286. {
  287. ecs->get_condition().lowest().init(this_ptr);
  288. }
  289. else
  290. {
  291. }
  292. }
  293. else
  294. {
  295. }
  296. // set keeplive options
  297. this->derived().set_keep_alive_options();
  298. }
  299. template<typename C, typename DeferEvent>
  300. inline void _do_start(std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs, DeferEvent chain)
  301. {
  302. derived_t& derive = this->derived();
  303. // Beacuse we ensured that the session::_do_disconnect must be called in the session's
  304. // io_context thread, so if the session::stop is called in the server's bind_connect
  305. // callback, the session's disconnect event maybe still be called, However, in this case,
  306. // we do not want the disconnect event to be called, so at here, we need use post_event
  307. // to ensure the join session is must be executed after the disconnect event, otherwise,
  308. // the join session maybe executed before the disconnect event(the bind_disconnect callback).
  309. // if the join session is executed before the disconnect event, the bind_disconnect will
  310. // be called.
  311. derive.post_event(
  312. [&derive, this_ptr = std::move(this_ptr), ecs = std::move(ecs), e = chain.move_event()]
  313. (event_queue_guard<derived_t> g) mutable
  314. {
  315. defer_event chain(std::move(e), std::move(g));
  316. if (!derive.is_started())
  317. {
  318. derive._do_disconnect(asio::error::operation_aborted, std::move(this_ptr), std::move(chain));
  319. return;
  320. }
  321. derive._join_session(std::move(this_ptr), std::move(ecs), std::move(chain));
  322. });
  323. }
  324. template<typename DeferEvent>
  325. inline void _handle_disconnect(const error_code& ec, std::shared_ptr<derived_t> this_ptr, DeferEvent chain)
  326. {
  327. ASIO2_ASSERT(this->derived().io_->running_in_this_thread());
  328. ASIO2_ASSERT(this->state_ == state_t::stopped);
  329. ASIO2_LOG_DEBUG("tcp_session::_handle_disconnect: {} {}", ec.value(), ec.message());
  330. set_last_error(ec);
  331. this->derived()._rdc_stop();
  332. // call shutdown again, beacuse the do shutdown maybe not called, eg: when
  333. // protocol error is checked in the mqtt or http, then the do disconnect
  334. // maybe called directly.
  335. // the socket maybe closed already somewhere else.
  336. if (this->socket().is_open())
  337. {
  338. error_code ec_linger{}, ec_ignore{};
  339. asio::socket_base::linger lnger{};
  340. this->socket().lowest_layer().get_option(lnger, ec_linger);
  341. // call socket's close function to notify the _handle_recv function response with
  342. // error > 0 ,then the socket can get notify to exit
  343. // Call shutdown() to indicate that you will not write any more data to the socket.
  344. if (!ec_linger && !(lnger.enabled() == true && lnger.timeout() == 0))
  345. {
  346. this->socket().shutdown(asio::socket_base::shutdown_both, ec_ignore);
  347. }
  348. // if the socket is basic_stream with rate limit, we should call the cancel,
  349. // otherwise the rate timer maybe can't canceled, and cause the io_context
  350. // can't stopped forever, even if the socket is closed already.
  351. this->socket().cancel(ec_ignore);
  352. // Call close,otherwise the _handle_recv will never return
  353. this->socket().close(ec_ignore);
  354. }
  355. super::_handle_disconnect(ec, std::move(this_ptr), std::move(chain));
  356. }
  357. template<typename DeferEvent>
  358. inline void _do_stop(const error_code& ec, std::shared_ptr<derived_t> this_ptr, DeferEvent chain)
  359. {
  360. this->derived()._post_stop(ec, std::move(this_ptr), std::move(chain));
  361. }
  362. template<typename DeferEvent>
  363. inline void _post_stop(const error_code& ec, std::shared_ptr<derived_t> this_ptr, DeferEvent chain)
  364. {
  365. // call the base class stop function
  366. super::stop();
  367. // call CRTP polymorphic stop
  368. this->derived()._handle_stop(ec, std::move(this_ptr), std::move(chain));
  369. }
  370. template<typename DeferEvent>
  371. inline void _handle_stop(const error_code& ec, std::shared_ptr<derived_t> this_ptr, DeferEvent chain)
  372. {
  373. detail::ignore_unused(ec, this_ptr, chain);
  374. ASIO2_ASSERT(this->state_ == state_t::stopped);
  375. }
  376. template<typename C, typename DeferEvent>
  377. inline void _join_session(
  378. std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs, DeferEvent chain)
  379. {
  380. this->sessions_.emplace(this_ptr,
  381. [this, this_ptr, ecs = std::move(ecs), chain = std::move(chain)](bool inserted) mutable
  382. {
  383. if (inserted)
  384. this->derived()._start_recv(std::move(this_ptr), std::move(ecs), std::move(chain));
  385. else
  386. this->derived()._do_disconnect(
  387. asio::error::address_in_use, std::move(this_ptr), std::move(chain));
  388. });
  389. }
  390. template<typename C, typename DeferEvent>
  391. inline void _start_recv(
  392. std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs, DeferEvent chain)
  393. {
  394. // to avlid the user call stop in another thread,then it may be socket.async_read_some
  395. // and socket.close be called at the same time
  396. asio::dispatch(this->io_->context(), make_allocator(this->wallocator_,
  397. [this, this_ptr = std::move(this_ptr), ecs = std::move(ecs), chain = std::move(chain)]
  398. () mutable
  399. {
  400. using condition_lowest_type = typename ecs_t<C>::condition_lowest_type;
  401. detail::ignore_unused(chain);
  402. if constexpr (!std::is_same_v<condition_lowest_type, asio2::detail::hook_buffer_t>)
  403. {
  404. this->derived().buffer().consume(this->derived().buffer().size());
  405. }
  406. else
  407. {
  408. std::ignore = true;
  409. }
  410. // start the timer of check silence timeout
  411. this->derived()._post_silence_timer(this->silence_timeout_, this_ptr);
  412. this->derived()._post_recv(std::move(this_ptr), std::move(ecs));
  413. }));
  414. }
  415. template<class Data, class Callback>
  416. inline bool _do_send(Data& data, Callback&& callback)
  417. {
  418. return this->derived()._tcp_send(data, std::forward<Callback>(callback));
  419. }
  420. template<class Data>
  421. inline send_data_t _rdc_convert_to_send_data(Data& data) noexcept
  422. {
  423. auto buffer = asio::buffer(data);
  424. return send_data_t{ reinterpret_cast<
  425. std::string_view::const_pointer>(buffer.data()),buffer.size() };
  426. }
  427. template<class Invoker>
  428. inline void _rdc_invoke_with_none(const error_code& ec, Invoker& invoker)
  429. {
  430. if (invoker)
  431. invoker(ec, send_data_t{}, recv_data_t{});
  432. }
  433. template<class Invoker>
  434. inline void _rdc_invoke_with_recv(const error_code& ec, Invoker& invoker, recv_data_t data)
  435. {
  436. if (invoker)
  437. invoker(ec, send_data_t{}, data);
  438. }
  439. template<class Invoker>
  440. inline void _rdc_invoke_with_send(const error_code& ec, Invoker& invoker, send_data_t data)
  441. {
  442. if (invoker)
  443. invoker(ec, data, recv_data_t{});
  444. }
  445. protected:
  446. template<typename C>
  447. inline void _post_recv(std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs)
  448. {
  449. this->derived()._tcp_post_recv(std::move(this_ptr), std::move(ecs));
  450. }
  451. template<typename C>
  452. inline void _handle_recv(
  453. const error_code& ec, std::size_t bytes_recvd,
  454. std::shared_ptr<derived_t> this_ptr, std::shared_ptr<ecs_t<C>> ecs)
  455. {
  456. this->derived()._tcp_handle_recv(ec, bytes_recvd, std::move(this_ptr), std::move(ecs));
  457. }
  458. template<typename C>
  459. inline void _fire_recv(
  460. std::shared_ptr<derived_t>& this_ptr, std::shared_ptr<ecs_t<C>>& ecs, std::string_view data)
  461. {
  462. data = detail::call_data_filter_before_recv(this->derived(), data);
  463. this->listener_.notify(event_type::recv, this_ptr, data);
  464. this->derived()._rdc_handle_recv(this_ptr, ecs, data);
  465. }
  466. inline void _fire_accept(std::shared_ptr<derived_t>& this_ptr)
  467. {
  468. // the _fire_accept must be executed in the thread 0.
  469. ASIO2_ASSERT(this->sessions_.io_->running_in_this_thread());
  470. this->listener_.notify(event_type::accept, this_ptr);
  471. }
  472. template<typename C>
  473. inline void _fire_connect(std::shared_ptr<derived_t>& this_ptr, std::shared_ptr<ecs_t<C>>& ecs)
  474. {
  475. // the _fire_connect must be executed in the thread 0.
  476. ASIO2_ASSERT(this->sessions_.io_->running_in_this_thread());
  477. #if defined(_DEBUG) || defined(DEBUG)
  478. ASIO2_ASSERT(this->is_disconnect_called_ == false);
  479. #endif
  480. this->derived()._rdc_start(this_ptr, ecs);
  481. this->listener_.notify(event_type::connect, this_ptr);
  482. }
  483. inline void _fire_disconnect(std::shared_ptr<derived_t>& this_ptr)
  484. {
  485. // the _fire_disconnect must be executed in the thread 0.
  486. ASIO2_ASSERT(this->sessions_.io_->running_in_this_thread());
  487. #if defined(_DEBUG) || defined(DEBUG)
  488. this->is_disconnect_called_ = true;
  489. #endif
  490. this->listener_.notify(event_type::disconnect, this_ptr);
  491. }
  492. protected:
  493. /**
  494. * @brief get the recv/read allocator object reference
  495. */
  496. inline auto & rallocator() noexcept { return this->rallocator_; }
  497. /**
  498. * @brief get the send/write allocator object reference
  499. */
  500. inline auto & wallocator() noexcept { return this->wallocator_; }
  501. protected:
  502. /// The memory to use for handler-based custom memory allocation. used fo recv/read.
  503. handler_memory<std::true_type , assizer<args_t>> rallocator_;
  504. /// The memory to use for handler-based custom memory allocation. used fo send/write.
  505. handler_memory<std::false_type, assizer<args_t>> wallocator_;
  506. /// Does it have the same datagram mechanism as udp?
  507. bool dgram_ = false;
  508. #if defined(_DEBUG) || defined(DEBUG)
  509. bool is_disconnect_called_ = false;
  510. #endif
  511. };
  512. }
  513. namespace asio2
  514. {
  515. using tcp_session_args = detail::template_args_tcp_session;
  516. template<class derived_t, class args_t>
  517. using tcp_session_impl_t = detail::tcp_session_impl_t<derived_t, args_t>;
  518. /**
  519. * @brief tcp session
  520. */
  521. template<class derived_t>
  522. class tcp_session_t : public detail::tcp_session_impl_t<derived_t, detail::template_args_tcp_session>
  523. {
  524. public:
  525. using detail::tcp_session_impl_t<derived_t, detail::template_args_tcp_session>::tcp_session_impl_t;
  526. };
  527. /**
  528. * @brief tcp session
  529. */
  530. class tcp_session : public tcp_session_t<tcp_session>
  531. {
  532. public:
  533. using tcp_session_t<tcp_session>::tcp_session_t;
  534. };
  535. }
  536. #if defined(ASIO2_INCLUDE_RATE_LIMIT)
  537. #include <asio2/tcp/tcp_stream.hpp>
  538. namespace asio2
  539. {
  540. struct tcp_rate_session_args : public tcp_session_args
  541. {
  542. using socket_t = asio2::tcp_stream<asio2::simple_rate_policy>;
  543. };
  544. template<class derived_t>
  545. class tcp_rate_session_t : public asio2::tcp_session_impl_t<derived_t, tcp_rate_session_args>
  546. {
  547. public:
  548. using asio2::tcp_session_impl_t<derived_t, tcp_rate_session_args>::tcp_session_impl_t;
  549. };
  550. class tcp_rate_session : public asio2::tcp_rate_session_t<tcp_rate_session>
  551. {
  552. public:
  553. using asio2::tcp_rate_session_t<tcp_rate_session>::tcp_rate_session_t;
  554. };
  555. }
  556. #endif
  557. #include <asio2/base/detail/pop_options.hpp>
  558. #endif // !__ASIO2_TCP_SESSION_HPP__