tcp_send_op.hpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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_SEND_OP_HPP__
  11. #define __ASIO2_TCP_SEND_OP_HPP__
  12. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  13. #pragma once
  14. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  15. #include <memory>
  16. #include <future>
  17. #include <utility>
  18. #include <string_view>
  19. #include <asio2/base/error.hpp>
  20. #include <asio2/base/detail/buffer_wrap.hpp>
  21. namespace asio2::detail
  22. {
  23. template<class derived_t, class args_t>
  24. class tcp_send_op
  25. {
  26. protected:
  27. template<class, class = std::void_t<>>
  28. struct has_member_dgram : std::false_type {};
  29. template<class T>
  30. struct has_member_dgram<T, std::void_t<decltype(T::dgram_)>> : std::true_type {};
  31. //template<class T>
  32. //struct has_member_dgram<T, std::void_t<decltype(T::dgram_), std::enable_if_t<
  33. // std::is_same_v<decltype(T::dgram_), bool>>>> : std::true_type {};
  34. public:
  35. /**
  36. * @brief constructor
  37. */
  38. tcp_send_op() noexcept {}
  39. /**
  40. * @brief destructor
  41. */
  42. ~tcp_send_op() = default;
  43. protected:
  44. template<class Data, class Callback>
  45. inline bool _tcp_send(Data& data, Callback&& callback)
  46. {
  47. derived_t& derive = static_cast<derived_t&>(*this);
  48. if constexpr (has_member_dgram<derived_t>::value)
  49. {
  50. if (derive.dgram_)
  51. {
  52. return derive._tcp_send_dgram(asio::buffer(data), std::forward<Callback>(callback));
  53. }
  54. }
  55. else
  56. {
  57. std::ignore = true;
  58. }
  59. return derive._tcp_send_general(asio::buffer(data), std::forward<Callback>(callback));
  60. }
  61. template<class Buffer, class Callback>
  62. inline bool _tcp_send_dgram(Buffer&& buffer, Callback&& callback)
  63. {
  64. derived_t& derive = static_cast<derived_t&>(*this);
  65. int bytes = 0;
  66. std::unique_ptr<std::uint8_t[]> head;
  67. // why don't use std::string for "head"?
  68. // beacuse std::string has a SSO(Small String Optimization) mechanism
  69. // https://stackoverflow.com/questions/34788789/disable-stdstrings-sso
  70. // std::string str;
  71. // str.reserve(sizeof(str) + 1);
  72. // note : need ensure big endian and little endian
  73. if (buffer.size() < std::size_t(254))
  74. {
  75. bytes = 1;
  76. head = std::make_unique<std::uint8_t[]>(bytes);
  77. head[0] = static_cast<std::uint8_t>(buffer.size());
  78. }
  79. else if (buffer.size() <= (std::numeric_limits<std::uint16_t>::max)())
  80. {
  81. bytes = 3;
  82. head = std::make_unique<std::uint8_t[]>(bytes);
  83. head[0] = static_cast<std::uint8_t>(254);
  84. std::uint16_t size = static_cast<std::uint16_t>(buffer.size());
  85. std::memcpy(&head[1], reinterpret_cast<const void*>(&size), sizeof(std::uint16_t));
  86. // use little endian
  87. if (!is_little_endian())
  88. {
  89. swap_bytes<sizeof(std::uint16_t)>(&head[1]);
  90. }
  91. }
  92. else
  93. {
  94. ASIO2_ASSERT(buffer.size() > (std::numeric_limits<std::uint16_t>::max)());
  95. bytes = 9;
  96. head = std::make_unique<std::uint8_t[]>(bytes);
  97. head[0] = static_cast<std::uint8_t>(255);
  98. std::uint64_t size = buffer.size();
  99. std::memcpy(&head[1], reinterpret_cast<const void*>(&size), sizeof(std::uint64_t));
  100. // use little endian
  101. if (!is_little_endian())
  102. {
  103. swap_bytes<sizeof(std::uint64_t)>(&head[1]);
  104. }
  105. }
  106. std::array<asio::const_buffer, 2> buffers
  107. {
  108. asio::buffer(reinterpret_cast<const void*>(head.get()), bytes),
  109. std::forward<Buffer>(buffer)
  110. };
  111. #if defined(_DEBUG) || defined(DEBUG)
  112. ASIO2_ASSERT(derive.post_send_counter_.load() == 0);
  113. derive.post_send_counter_++;
  114. #endif
  115. asio::async_write(derive.stream(), buffers, make_allocator(derive.wallocator(),
  116. [&derive, bytes, head = std::move(head), callback = std::forward<Callback>(callback)]
  117. (const error_code& ec, std::size_t bytes_sent) mutable
  118. {
  119. #if defined(_DEBUG) || defined(DEBUG)
  120. derive.post_send_counter_--;
  121. #endif
  122. set_last_error(ec);
  123. if (ec)
  124. {
  125. callback(ec, bytes_sent);
  126. // must stop, otherwise re-sending will cause header confusion
  127. if (derive.state_ == state_t::started)
  128. {
  129. derive._do_disconnect(ec, derive.selfptr());
  130. }
  131. }
  132. else
  133. {
  134. callback(ec, bytes_sent - bytes);
  135. }
  136. }));
  137. return true;
  138. }
  139. template<class Buffer, class Callback>
  140. inline bool _tcp_send_general(Buffer&& buffer, Callback&& callback)
  141. {
  142. derived_t& derive = static_cast<derived_t&>(*this);
  143. #if defined(_DEBUG) || defined(DEBUG)
  144. ASIO2_ASSERT(derive.post_send_counter_.load() == 0);
  145. derive.post_send_counter_++;
  146. #endif
  147. asio::async_write(derive.stream(), buffer, make_allocator(derive.wallocator(),
  148. [&derive, callback = std::forward<Callback>(callback)]
  149. (const error_code& ec, std::size_t bytes_sent) mutable
  150. {
  151. #if defined(_DEBUG) || defined(DEBUG)
  152. derive.post_send_counter_--;
  153. #endif
  154. set_last_error(ec);
  155. callback(ec, bytes_sent);
  156. if (ec)
  157. {
  158. // must stop, otherwise re-sending will cause body confusion
  159. if (derive.state_ == state_t::started)
  160. {
  161. derive._do_disconnect(ec, derive.selfptr());
  162. }
  163. }
  164. }));
  165. return true;
  166. }
  167. protected:
  168. };
  169. }
  170. #endif // !__ASIO2_TCP_SEND_OP_HPP__