OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
tcp_socket.hpp
1#pragma once
2#include <ossia/network/context.hpp>
3#include <ossia/network/sockets/configuration.hpp>
4
5#include <boost/asio/io_context.hpp>
6#include <boost/asio/ip/tcp.hpp>
7#include <boost/asio/ip/udp.hpp>
8#include <boost/asio/local/datagram_protocol.hpp>
9#include <boost/asio/placeholders.hpp>
10#include <boost/asio/strand.hpp>
11#include <boost/asio/write.hpp>
12
13#include <nano_signal_slot.hpp>
14
15namespace ossia::net
16{
17class tcp_listener
18{
19public:
20 using proto = boost::asio::ip::tcp;
21 using socket = typename proto::socket;
22
23 tcp_listener() = delete;
24 tcp_listener(const tcp_listener&) = delete;
25 tcp_listener& operator=(const tcp_listener&) = delete;
26 tcp_listener(tcp_listener&&) = default;
27 tcp_listener& operator=(tcp_listener&&) = default;
28 explicit tcp_listener(proto::socket sock)
29 : m_socket{std::move(sock)}
30 {
31 }
32
33 void close()
34 {
35 // FIXME async?
36 try
37 {
38 m_socket.shutdown(boost::asio::ip::udp::socket::shutdown_both);
39 }
40 catch(...)
41 {
42 }
43 m_socket.close();
44 }
45
46 void write(const boost::asio::const_buffer& buf)
47 {
48 boost::system::error_code ec;
49 boost::asio::write(m_socket, buf, ec);
50 }
51
52 void on_close() { }
53
54 void on_fail() { }
55
56 proto::socket m_socket;
57};
58
59class tcp_server
60{
61public:
62 using proto = boost::asio::ip::tcp;
63 using socket = typename proto::socket;
64 using listener = tcp_listener;
65
66 tcp_server(const inbound_socket_configuration& conf, boost::asio::io_context& ctx)
67 : m_context{ctx}
68 , m_acceptor{
69 boost::asio::make_strand(ctx),
70 proto::endpoint{boost::asio::ip::make_address(conf.bind), conf.port}}
71 {
72 }
73
74 tcp_server(
75 const inbound_socket_configuration& conf, ossia::net::network_context_ptr ctx)
76 : tcp_server{conf, ctx->context}
77 {
78 }
79
80 boost::asio::io_context& m_context;
81 proto::acceptor m_acceptor;
82};
83
84class tcp_client
85{
86public:
87 using proto = boost::asio::ip::tcp;
88 using socket = typename proto::socket;
89
90 tcp_client(const outbound_socket_configuration& conf, boost::asio::io_context& ctx)
91 : m_context{ctx}
92 , m_endpoint{boost::asio::ip::make_address(conf.host), conf.port}
93 , m_socket{boost::asio::make_strand(ctx)}
94 {
95 }
96
97 void connect()
98 {
99 boost::system::error_code ec;
100 m_socket.set_option(boost::asio::ip::tcp::no_delay{true}, ec);
101 m_socket.set_option(boost::asio::socket_base::reuse_address{true}, ec);
102
103 m_socket.async_connect(
104 m_endpoint, [this](const boost::system::error_code& ec, auto&&...) {
105 if(m_socket.is_open() && !ec)
106 {
107 m_connected = true;
108 on_open();
109 }
110 else
111 {
112 m_connected = false;
113 puts(ec.message().c_str());
114 on_fail();
115 }
116 });
117 }
118
119 bool connected() const { return m_connected; }
120
121 void close()
122 {
123 boost::asio::post(m_context, [this] {
124 try
125 {
126 m_socket.shutdown(boost::asio::ip::udp::socket::shutdown_both);
127 }
128 catch(...)
129 {
130 }
131 m_socket.close();
132 on_close();
133 });
134 }
135
136 void write(const char* data, std::size_t sz)
137 {
138 boost::system::error_code ec;
139 boost::asio::write(m_socket, boost::asio::const_buffer(data, sz), ec);
140 }
141
142 Nano::Signal<void()> on_open;
143 Nano::Signal<void()> on_close;
144 Nano::Signal<void()> on_fail;
145
146 boost::asio::io_context& m_context;
147 proto::endpoint m_endpoint;
148 proto::socket m_socket;
149 bool m_connected{false};
150};
151}