OSSIA
Open Scenario System for Interactive Application
udp_socket.hpp
1 #pragma once
3 #include <ossia/network/sockets/configuration.hpp>
4 
5 #include <boost/asio/io_context.hpp>
6 #include <boost/asio/ip/udp.hpp>
7 #include <boost/asio/local/datagram_protocol.hpp>
8 #include <boost/asio/placeholders.hpp>
9 #include <boost/asio/strand.hpp>
10 #include <boost/asio/write.hpp>
11 
12 #include <nano_signal_slot.hpp>
13 
14 namespace ossia::net
15 {
16 
17 class udp_receive_socket
18 {
19  using proto = boost::asio::ip::udp;
20 
21 public:
22  udp_receive_socket(boost::asio::io_context& ctx)
23  : m_context{ctx}
24  , m_socket{boost::asio::make_strand(ctx)}
25  {
26  }
27 
28  udp_receive_socket(const socket_configuration& conf, boost::asio::io_context& ctx)
29  : m_context{ctx}
30  , m_endpoint{boost::asio::ip::make_address(conf.host), conf.port}
31  , m_socket{boost::asio::make_strand(ctx)}
32  {
33  }
34 
35  ~udp_receive_socket() = default;
36 
37  void assign(int sock) { m_socket.assign(boost::asio::ip::udp::v4(), sock); }
38  void open()
39  {
40  m_socket.open(boost::asio::ip::udp::v4());
41  m_socket.bind(m_endpoint);
42  }
43 
44  void close()
45  {
46  if(m_socket.is_open())
47  {
48  m_context.post([this] {
49  m_socket.close();
50  on_close();
51  });
52  }
53  }
54 
55  template <typename F>
56  void receive(F f)
57  {
58  m_socket.async_receive_from(
59  boost::asio::mutable_buffer(&m_data[0], std::size(m_data)), m_endpoint,
60  [this, f](auto ec, std::size_t sz) {
61  if(ec == boost::asio::error::operation_aborted)
62  return;
63 
64  if(!ec && sz > 0)
65  {
66  try
67  {
68  f(m_data, sz);
69  }
70  catch(const std::exception& e)
71  {
72  ossia::logger().error("[udp_socket::receive]: {}", e.what());
73  }
74  catch(...)
75  {
76  ossia::logger().error("[udp_socket::receive]: unknown error");
77  }
78  }
79 
80  this->receive(f);
81  });
82  }
83 
84  Nano::Signal<void()> on_close;
85 
86  boost::asio::io_context& m_context;
87  proto::endpoint m_endpoint;
88  proto::socket m_socket;
89  alignas(16) char m_data[65535];
90 };
91 
92 class udp_send_socket
93 {
94  using proto = boost::asio::ip::udp;
95 
96 public:
97  udp_send_socket(const socket_configuration& conf, boost::asio::io_context& ctx)
98  : m_context{ctx}
99  , m_endpoint{conf.broadcast ? boost::asio::ip::address_v4::broadcast() : boost::asio::ip::make_address(conf.host), conf.port}
100  , m_socket{boost::asio::make_strand(ctx)}
101  {
102  }
103 
104  udp_send_socket(
105  const boost::asio::ip::address_v4& host, const uint16_t port,
106  boost::asio::io_context& ctx)
107  : m_context{ctx}
108  , m_endpoint{host, port}
109  , m_socket{boost::asio::make_strand(ctx)}
110  {
111  }
112 
113  void connect()
114  {
115  m_socket.open(boost::asio::ip::udp::v4());
116 
117  m_socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
118 
119  if(m_endpoint.address() == boost::asio::ip::address_v4::broadcast())
120  m_socket.set_option(boost::asio::socket_base::broadcast(true));
121  }
122 
123  void close()
124  {
125  if(m_socket.is_open())
126  {
127  m_context.post([this] {
128  m_socket.close();
129  on_close();
130  });
131  }
132  }
133 
134  void write(const char* data, std::size_t sz)
135  {
136  boost::system::error_code ec;
137  m_socket.send_to(boost::asio::const_buffer(data, sz), m_endpoint, 0, ec);
138  }
139 
140  Nano::Signal<void()> on_close;
141 
142  boost::asio::io_context& m_context;
143  proto::endpoint m_endpoint;
144  proto::socket m_socket;
145 };
146 
147 }
spdlog::logger & logger() noexcept
Where the errors will be logged. Default is stderr.
Definition: context.cpp:104