unilink  0.4.3
A simple C++ library for unified async communication
udp.cc
Go to the documentation of this file.
1 /*
2  * Copyright 2025 Jinwoo Sung
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
18 
19 #if defined(__GNUC__) || defined(__clang__)
20 #pragma GCC diagnostic ignored "-Wsign-conversion"
21 #pragma GCC diagnostic ignored "-Wconversion"
22 #endif
23 
24 #include <boost/asio/executor_work_guard.hpp>
25 #include <boost/asio/io_context.hpp>
26 #include <iostream>
27 #include <stdexcept>
28 #include <thread>
29 
30 #include "unilink/base/common.hpp"
33 
34 namespace unilink {
35 namespace wrapper {
36 
37 struct Udp::Impl {
39  std::shared_ptr<interface::Channel> channel;
40  std::shared_ptr<boost::asio::io_context> external_ioc;
41  bool use_external_context{false};
43  std::thread external_thread;
44 
45  // Start status notification
46  std::promise<bool> start_promise_;
48 
49  // Event handlers (Context based)
54 
55  bool auto_manage{false};
56  bool started{false};
57 
58  explicit Impl(const config::UdpConfig& config) : cfg(config) {}
59  Impl(const config::UdpConfig& config, std::shared_ptr<boost::asio::io_context> ioc)
60  : cfg(config), external_ioc(std::move(ioc)), use_external_context(external_ioc != nullptr) {}
61  explicit Impl(std::shared_ptr<interface::Channel> ch) : channel(std::move(ch)) {}
62 
63  ~Impl() {
64  try {
65  stop();
66  } catch (...) {
67  }
68  }
69 
70  std::future<bool> start() {
71  if (started) {
72  std::promise<bool> p;
73  p.set_value(true);
74  return p.get_future();
75  }
76 
77  start_promise_ = std::promise<bool>();
79 
80  if (!channel) {
83  }
84 
85  channel->start();
87  external_thread = std::thread([ioc = external_ioc]() {
88  boost::asio::executor_work_guard<boost::asio::io_context::executor_type> guard(ioc->get_executor());
89  ioc->run();
90  });
91  }
92 
93  started = true;
94  return start_promise_.get_future();
95  }
96 
97  void stop() {
98  if (!started) return;
99 
100  if (channel) {
101  channel->on_bytes(nullptr);
102  channel->on_state(nullptr);
103  channel->stop();
104  }
105 
107  if (external_ioc) external_ioc->stop();
108  external_thread.join();
109  }
110 
111  started = false;
112 
114  try {
115  start_promise_.set_value(false);
116  } catch (...) {
117  }
119  }
120  }
121 
123  if (!channel) return;
124 
125  channel->on_bytes([this](memory::ConstByteSpan data) {
126  if (data_handler) {
127  std::string str_data = common::safe_convert::uint8_to_string(data.data(), data.size());
128  data_handler(MessageContext(0, str_data));
129  }
130  });
131 
132  channel->on_state([this](base::LinkState state) {
133  switch (state) {
136  start_promise_.set_value(true);
138  }
140  break;
143  break;
146  start_promise_.set_value(false);
148  }
149  if (error_handler) error_handler(ErrorContext(ErrorCode::IoError, "Connection error"));
150  break;
151  default:
152  break;
153  }
154  });
155  }
156 };
157 
158 Udp::Udp(const config::UdpConfig& cfg) : impl_(std::make_unique<Impl>(cfg)) {}
159 Udp::Udp(const config::UdpConfig& cfg, std::shared_ptr<boost::asio::io_context> ioc)
160  : impl_(std::make_unique<Impl>(cfg, ioc)) {}
161 Udp::Udp(std::shared_ptr<interface::Channel> ch) : impl_(std::make_unique<Impl>(ch)) {
162  impl_->setup_internal_handlers();
163 }
164 Udp::~Udp() = default;
165 
166 Udp::Udp(Udp&&) noexcept = default;
167 Udp& Udp::operator=(Udp&&) noexcept = default;
168 
169 std::future<bool> Udp::start() { return impl_->start(); }
170 void Udp::stop() { impl_->stop(); }
171 void Udp::send(std::string_view data) {
172  if (is_connected() && get_impl()->channel) {
173  auto binary_view = common::safe_convert::string_to_bytes(data);
174  get_impl()->channel->async_write_copy(memory::ConstByteSpan(binary_view.first, binary_view.second));
175  }
176 }
177 void Udp::send_line(std::string_view line) { send(std::string(line) + "\n"); }
178 bool Udp::is_connected() const { return get_impl()->channel && get_impl()->channel->is_connected(); }
179 
181  impl_->data_handler = std::move(h);
182  return *this;
183 }
185  impl_->connect_handler = std::move(h);
186  return *this;
187 }
189  impl_->disconnect_handler = std::move(h);
190  return *this;
191 }
193  impl_->error_handler = std::move(h);
194  return *this;
195 }
196 
198  impl_->auto_manage = m;
199  if (impl_->auto_manage && !impl_->started) start();
200  return *this;
201 }
202 
203 void Udp::set_manage_external_context(bool manage) { impl_->manage_external_context = manage; }
204 
205 } // namespace wrapper
206 } // namespace unilink