38template <
typename TSEND,
typename TREC>
class connection :
public std::enable_shared_from_this<connection<TSEND, TREC>> {
40#if defined(BOOST_ASIO_HAS_LOCAL_SOCKETS) && defined(USE_UDS)
41#warning "This build uses stream sockets and will not work across the network"
42 typedef boost::asio::local::stream_protocol::socket socket_t;
43 typedef boost::asio::local::stream_protocol::endpoint endpoint_t;
44 typedef boost::asio::local::stream_protocol::acceptor acceptor_t;
46 typedef boost::asio::ip::udp::socket socket_t;
47 typedef boost::asio::ip::udp::endpoint endpoint_t;
49 typedef boost::asio::ip::tcp::socket socket_t;
50 typedef boost::asio::ip::tcp::endpoint endpoint_t;
51 typedef boost::asio::ip::tcp::acceptor acceptor_t;
53 typedef std::shared_ptr<connection<TSEND, TREC>> ptr;
55 struct async_listener :
public std::enable_shared_from_this<async_listener> {
56 virtual void send_completed(
const boost::system::error_code& error) = 0;
57 virtual void receive_completed(
const boost::system::error_code& error,
const TREC*
const result) = 0;
60 connection(boost::asio::io_context& io_context)
61 : socket_(io_context) {}
63 socket_t& socket() {
return socket_; }
64 void add_listener(std::shared_ptr<async_listener> l) { listener = l; }
66 void async_write(TSEND& t) { async_write(&t); }
68 void async_write(TSEND* t) {
69 if(!prepare_outbound_data(t)) {
70 boost::system::error_code err(boost::asio::error::invalid_argument);
72 listener->send_completed(err);
76 std::vector<boost::asio::const_buffer> buffers;
77 buffers.push_back(boost::asio::buffer(outbound_header_));
78 buffers.push_back(boost::asio::buffer(outbound_data_));
79 auto self = this->shared_from_this();
80 boost::asio::async_write(socket_, buffers, [self](
const boost::system::error_code& error, std::size_t bytes_transferred) {
81 self->handle_async_write(error, bytes_transferred);
86 void handle_async_write(
const boost::system::error_code& err,
size_t ) {
88 listener->send_completed(err);
93 auto self = this->shared_from_this();
94 boost::asio::async_read(socket_, boost::asio::buffer(inbound_header_),
95 [self](
const boost::system::error_code& error, std::size_t) { self->async_read_header(error); });
99 void async_read_header(
const boost::system::error_code& e) {
102 listener->receive_completed(e, NULL);
104 std::string header(inbound_header_, header_length);
105 std::istringstream is(header);
106 std::size_t inbound_data_size = 0;
107 is >> std::hex >> inbound_data_size;
108 if(inbound_data_size == 0) {
109 boost::system::error_code error(boost::asio::error::invalid_argument);
111 listener->receive_completed(error, NULL);
114 inbound_data_.resize(inbound_data_size);
115 auto self = this->shared_from_this();
116 boost::asio::async_read(socket_, boost::asio::buffer(inbound_data_),
117 [self](
const boost::system::error_code& error, std::size_t) { self->async_read_data(error); });
121 void async_read_data(
const boost::system::error_code& e) {
124 listener->receive_completed(e, NULL);
126 if(!TREC::verify(inbound_data_.data(), inbound_data_.size())) {
127 boost::system::error_code err(boost::asio::error::invalid_argument);
129 listener->receive_completed(err, NULL);
132#ifdef TRACE_COMMUNICATION
133 std::string raw_data(
reinterpret_cast<const char*
>(inbound_data_.data()), inbound_data_.size());
134 CPPLOG(TRACE,
"tcp4tlm") <<
"inbound async data (" << inbound_data_.size() <<
"):[" << raw_data <<
"]" << std::endl;
136 TREC* t =
new TREC(std::move(inbound_data_));
138 listener->receive_completed(e, t);
144 void write_data(std::shared_ptr<TSEND>& t) { write_data(t.get()); }
146 void write_data(TSEND& t) { write_data(&t); }
148 void write_data(TSEND* t) {
149 boost::system::error_code ec;
150 this->write_data(t, ec);
151 boost::asio::detail::throw_error(ec);
154 void write_data(TSEND* t, boost::system::error_code& ec) {
155 if(!prepare_outbound_data(t)) {
156 ec.assign(boost::asio::error::invalid_argument, boost::asio::error::get_system_category());
159#ifdef GENERATE_STATISTICS
160 clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &get_t_stamp());
162 std::vector<boost::asio::const_buffer> buffers;
163 buffers.push_back(boost::asio::buffer(outbound_header_));
164 buffers.push_back(boost::asio::buffer(outbound_data_));
165 boost::asio::write(socket_, buffers);
168 bool read_data_available() {
169 boost::asio::socket_base::bytes_readable command(
true);
170 socket_.io_control(command);
171 std::size_t bytes_readable = command.get();
172 return bytes_readable > 0;
175 void read_data(std::shared_ptr<TREC>& msg) {
181 void read_data(TREC*& t) {
182 boost::system::error_code ec;
183 this->read_data(t, ec);
184 boost::asio::detail::throw_error(ec);
187 void read_data(TREC*& t, boost::system::error_code& ec) {
188 boost::asio::read(socket_, boost::asio::buffer(inbound_header_, header_length), boost::asio::transfer_exactly(header_length));
189 std::string header(inbound_header_, header_length);
190 std::istringstream is(header);
191 std::size_t inbound_data_size = 0;
192 is >> std::hex >> inbound_data_size;
193 if(inbound_data_size == 0) {
194 ec.assign(boost::asio::error::invalid_argument, boost::asio::error::get_system_category());
198 inbound_data_.resize(inbound_data_size);
199 boost::asio::read(socket_, boost::asio::buffer(inbound_data_, inbound_data_size), boost::asio::transfer_exactly(inbound_data_size));
200 if(!TREC::verify(inbound_data_.data(), inbound_data_.size())) {
201 ec.assign(boost::asio::error::invalid_argument, boost::asio::error::get_system_category());
204#ifdef TRACE_COMMUNICATION
205 std::string raw_data(
reinterpret_cast<const char*
>(inbound_data_.data()), inbound_data_.size());
206 CPPLOG(TRACE,
"tcp4tlm") <<
"inbound sync data (" << inbound_data_.size() <<
"):[" << raw_data <<
"]" << std::endl;
208 t =
new TREC(std::move(inbound_data_));
211#ifdef GENERATE_STATISTICS
212 static timespec& get_t_stamp() {
213 static timespec tstamp;
219 bool prepare_outbound_data(TSEND* t) {
220 if(t ==
nullptr || !(*t) || t->size() == 0) {
224 outbound_data_.assign(
reinterpret_cast<const char*
>(t->data()),
reinterpret_cast<const char*
>(t->data()) + t->size());
225#ifdef TRACE_COMMUNICATION
226 CPPLOG(TRACE,
"tcp4tlm") <<
"outbound data (" << outbound_data_.size() <<
"):[" << outbound_data_ <<
"]" << std::endl;
228 std::ostringstream header_stream;
229 header_stream << std::setw(header_length) << std::hex << std::setfill(
'0') << outbound_data_.size();
230 if(!header_stream || header_stream.str().size() != header_length) {
233 outbound_header_ = header_stream.str();
238 enum { header_length = 8 };
239 std::string outbound_header_;
240 std::string outbound_data_;
241 char inbound_header_[header_length];
242 std::vector<uint8_t> inbound_data_;
243 std::shared_ptr<async_listener> listener;