/* * Copyright (c) 2017-2023 zhllxt * * author : zhllxt * email : 37792738@qq.com * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef __ASIO2_TCP_SEND_OP_HPP__ #define __ASIO2_TCP_SEND_OP_HPP__ #if defined(_MSC_VER) && (_MSC_VER >= 1200) #pragma once #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) #include #include #include #include #include #include namespace asio2::detail { template class tcp_send_op { protected: template> struct has_member_dgram : std::false_type {}; template struct has_member_dgram> : std::true_type {}; //template //struct has_member_dgram>>> : std::true_type {}; public: /** * @brief constructor */ tcp_send_op() noexcept {} /** * @brief destructor */ ~tcp_send_op() = default; protected: template inline bool _tcp_send(Data& data, Callback&& callback) { derived_t& derive = static_cast(*this); if constexpr (has_member_dgram::value) { if (derive.dgram_) { return derive._tcp_send_dgram(asio::buffer(data), std::forward(callback)); } } else { std::ignore = true; } return derive._tcp_send_general(asio::buffer(data), std::forward(callback)); } template inline bool _tcp_send_dgram(Buffer&& buffer, Callback&& callback) { derived_t& derive = static_cast(*this); int bytes = 0; std::unique_ptr head; // why don't use std::string for "head"? // beacuse std::string has a SSO(Small String Optimization) mechanism // https://stackoverflow.com/questions/34788789/disable-stdstrings-sso // std::string str; // str.reserve(sizeof(str) + 1); // note : need ensure big endian and little endian if (buffer.size() < std::size_t(254)) { bytes = 1; head = std::make_unique(bytes); head[0] = static_cast(buffer.size()); } else if (buffer.size() <= (std::numeric_limits::max)()) { bytes = 3; head = std::make_unique(bytes); head[0] = static_cast(254); std::uint16_t size = static_cast(buffer.size()); std::memcpy(&head[1], reinterpret_cast(&size), sizeof(std::uint16_t)); // use little endian if (!is_little_endian()) { swap_bytes(&head[1]); } } else { ASIO2_ASSERT(buffer.size() > (std::numeric_limits::max)()); bytes = 9; head = std::make_unique(bytes); head[0] = static_cast(255); std::uint64_t size = buffer.size(); std::memcpy(&head[1], reinterpret_cast(&size), sizeof(std::uint64_t)); // use little endian if (!is_little_endian()) { swap_bytes(&head[1]); } } std::array buffers { asio::buffer(reinterpret_cast(head.get()), bytes), std::forward(buffer) }; #if defined(_DEBUG) || defined(DEBUG) ASIO2_ASSERT(derive.post_send_counter_.load() == 0); derive.post_send_counter_++; #endif asio::async_write(derive.stream(), buffers, make_allocator(derive.wallocator(), [&derive, bytes, head = std::move(head), callback = std::forward(callback)] (const error_code& ec, std::size_t bytes_sent) mutable { #if defined(_DEBUG) || defined(DEBUG) derive.post_send_counter_--; #endif set_last_error(ec); if (ec) { callback(ec, bytes_sent); // must stop, otherwise re-sending will cause header confusion if (derive.state_ == state_t::started) { derive._do_disconnect(ec, derive.selfptr()); } } else { callback(ec, bytes_sent - bytes); } })); return true; } template inline bool _tcp_send_general(Buffer&& buffer, Callback&& callback) { derived_t& derive = static_cast(*this); #if defined(_DEBUG) || defined(DEBUG) ASIO2_ASSERT(derive.post_send_counter_.load() == 0); derive.post_send_counter_++; #endif asio::async_write(derive.stream(), buffer, make_allocator(derive.wallocator(), [&derive, callback = std::forward(callback)] (const error_code& ec, std::size_t bytes_sent) mutable { #if defined(_DEBUG) || defined(DEBUG) derive.post_send_counter_--; #endif set_last_error(ec); callback(ec, bytes_sent); if (ec) { // must stop, otherwise re-sending will cause body confusion if (derive.state_ == state_t::started) { derive._do_disconnect(ec, derive.selfptr()); } } })); return true; } protected: }; } #endif // !__ASIO2_TCP_SEND_OP_HPP__