Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
tcp_socket.cpp
Go to the documentation of this file.
1#include <fc/network/tcp_socket.hpp>
2#include <fc/network/ip.hpp>
3#include <fc/network/tcp_socket_io_hooks.hpp>
4#include <fc/fwd_impl.hpp>
5#include <fc/asio.hpp>
6#include <fc/log/logger.hpp>
7#include <fc/io/stdio.hpp>
9
10#if defined _WIN32 || defined WIN32 || defined OS_WIN64 || defined _WIN64 || defined WIN64 || defined WINNT
11# include <mstcpip.h>
12#endif
13
14namespace fc {
15 using boost::fibers::future;
16
17 namespace detail
18 {
19 bool have_so_reuseport = true;
20 }
21
22 class tcp_socket::impl : public tcp_socket_io_hooks {
23 public:
24 impl() :
25 _sock(fc::asio::default_io_service()),
26 _io_hooks(this)
27 {}
29 {
30 if( _sock.is_open() )
31 try
32 {
33 _sock.close();
34 }
35 catch( ... )
36 {}
37 if( _read_in_progress.valid() )
38 try
39 {
41 }
42 catch ( ... )
43 {
44 }
45 if( _write_in_progress.valid() )
46 try
47 {
49 }
50 catch ( ... )
51 {
52 }
53 }
54 virtual size_t readsome(boost::asio::ip::tcp::socket& socket, char* buffer, size_t length) override;
55 virtual size_t readsome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr<char>& buffer, size_t length, size_t offset) override;
56 virtual size_t writesome(boost::asio::ip::tcp::socket& socket, const char* buffer, size_t length) override;
57 virtual size_t writesome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr<const char>& buffer, size_t length, size_t offset) override;
58
59 future<size_t> _write_in_progress;
60 future<size_t> _read_in_progress;
61 boost::asio::ip::tcp::socket _sock;
62 tcp_socket_io_hooks* _io_hooks;
63 };
64
65 size_t tcp_socket::impl::readsome(boost::asio::ip::tcp::socket& socket, char* buffer, size_t length)
66 {
67 return (_read_in_progress = fc::asio::read_some(socket, buffer, length)).get();
68 }
69 size_t tcp_socket::impl::readsome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr<char>& buffer, size_t length, size_t offset)
70 {
71 return (_read_in_progress = fc::asio::read_some(socket, buffer, length, offset)).get();
72 }
73 size_t tcp_socket::impl::writesome(boost::asio::ip::tcp::socket& socket, const char* buffer, size_t length)
74 {
75 return (_write_in_progress = fc::asio::write_some(socket, buffer, length)).get();
76 }
77 size_t tcp_socket::impl::writesome(boost::asio::ip::tcp::socket& socket, const std::shared_ptr<const char>& buffer, size_t length, size_t offset)
78 {
79 return (_write_in_progress = fc::asio::write_some(socket, buffer, length, offset)).get();
80 }
81
82
83 void tcp_socket::open()
84 {
85 my->_sock.open(boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 0).protocol());
86 }
87
88 bool tcp_socket::is_open()const {
89 return my->_sock.is_open();
90 }
91
92 tcp_socket::tcp_socket(){};
93
94 tcp_socket::~tcp_socket(){};
95
96 void tcp_socket::flush() {}
97 void tcp_socket::close() {
98 try {
99 if( is_open() )
100 {
101 my->_sock.close();
102 }
103 } FC_RETHROW_EXCEPTIONS( warn, "error closing tcp socket" );
104 }
105
106 bool tcp_socket::eof()const {
107 return !my->_sock.is_open();
108 }
109
110 size_t tcp_socket::writesome(const char* buf, size_t len)
111 {
112 return my->_io_hooks->writesome(my->_sock, buf, len);
113 }
114
115 size_t tcp_socket::writesome(const std::shared_ptr<const char>& buf, size_t len, size_t offset)
116 {
117 return my->_io_hooks->writesome(my->_sock, buf, len, offset);
118 }
119
120 fc::ip::endpoint tcp_socket::remote_endpoint()const
121 {
122 try
123 {
124 auto rep = my->_sock.remote_endpoint();
125 return fc::ip::endpoint(rep.address().to_v4().to_ulong(), rep.port() );
126 }
127 FC_RETHROW_EXCEPTIONS( warn, "error getting socket's remote endpoint" );
128 }
129
130
131 fc::ip::endpoint tcp_socket::local_endpoint() const
132 {
133 try
134 {
135 auto boost_local_endpoint = my->_sock.local_endpoint();
136 return fc::ip::endpoint(boost_local_endpoint.address().to_v4().to_ulong(), boost_local_endpoint.port() );
137 }
138 FC_RETHROW_EXCEPTIONS( warn, "error getting socket's local endpoint" );
139 }
140
141 size_t tcp_socket::readsome( char* buf, size_t len )
142 {
143 return my->_io_hooks->readsome(my->_sock, buf, len);
144 }
145
146 size_t tcp_socket::readsome( const std::shared_ptr<char>& buf, size_t len, size_t offset ) {
147 return my->_io_hooks->readsome(my->_sock, buf, len, offset);
148 }
149
150 void tcp_socket::connect_to( const fc::ip::endpoint& remote_endpoint ) {
151 fc::asio::tcp::connect(my->_sock, fc::asio::tcp::endpoint( boost::asio::ip::address_v4(remote_endpoint.get_address()), remote_endpoint.port() ) );
152 }
153
154 void tcp_socket::bind(const fc::ip::endpoint& local_endpoint)
155 {
156 try
157 {
158 my->_sock.bind(boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4(local_endpoint.get_address()),
159 local_endpoint.port()));
160 }
161 catch (const std::exception& except)
162 {
163 elog("Exception binding outgoing connection to desired local endpoint ${endpoint}: ${what}", ("endpoint", local_endpoint)("what", except.what()));
164 FC_THROW("error binding to ${endpoint}: ${what}", ("endpoint", local_endpoint)("what", except.what()));
165 }
166 }
167
168 void tcp_socket::enable_keep_alives(const fc::microseconds& interval)
169 {
170 if (interval.count())
171 {
172 boost::asio::socket_base::keep_alive option(true);
173 my->_sock.set_option(option);
174#if defined _WIN32 || defined WIN32 || defined OS_WIN64 || defined _WIN64 || defined WIN64 || defined WINNT
175 struct tcp_keepalive keepalive_settings;
176 keepalive_settings.onoff = 1;
177 keepalive_settings.keepalivetime = (ULONG)(interval.count() / fc::milliseconds(1).count());
178 keepalive_settings.keepaliveinterval = (ULONG)(interval.count() / fc::milliseconds(1).count());
179
180 DWORD dwBytesRet = 0;
181 if (WSAIoctl(my->_sock.native(), SIO_KEEPALIVE_VALS, &keepalive_settings, sizeof(keepalive_settings),
182 NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR)
183 wlog("Error setting TCP keepalive values");
184#elif !defined(__clang__) || (__clang_major__ >= 6)
185 // This should work for modern Linuxes and for OSX >= Mountain Lion
186 int timeout_sec = interval.count() / fc::seconds(1).count();
187 if (setsockopt(my->_sock.native(), IPPROTO_TCP,
188 #if defined( __APPLE__ )
189 TCP_KEEPALIVE,
190 #else
191 TCP_KEEPIDLE,
192 #endif
193 (char*)&timeout_sec, sizeof(timeout_sec)) < 0)
194 wlog("Error setting TCP keepalive idle time");
195# if !defined(__APPLE__) || defined(TCP_KEEPINTVL) // TCP_KEEPINTVL not defined before 10.9
196 if (setsockopt(my->_sock.native(), IPPROTO_TCP, TCP_KEEPINTVL,
197 (char*)&timeout_sec, sizeof(timeout_sec)) < 0)
198 wlog("Error setting TCP keepalive interval");
199# endif // !__APPLE__ || TCP_KEEPINTVL
200#endif // !WIN32
201 }
202 else
203 {
204 boost::asio::socket_base::keep_alive option(false);
205 my->_sock.set_option(option);
206 }
207 }
208
209 void tcp_socket::set_io_hooks(tcp_socket_io_hooks* new_hooks)
210 {
211 my->_io_hooks = new_hooks ? new_hooks : &*my;
212 }
213
214 void tcp_socket::set_reuse_address(bool enable /* = true */)
215 {
216 FC_ASSERT(my->_sock.is_open());
217 boost::asio::socket_base::reuse_address option(enable);
218 my->_sock.set_option(option);
219#if defined(__APPLE__) || defined(__linux__)
220# ifndef SO_REUSEPORT
221# define SO_REUSEPORT 15
222# endif
223 // OSX needs SO_REUSEPORT in addition to SO_REUSEADDR.
224 // This probably needs to be set for any BSD
226 {
227 int reuseport_value = 1;
228 if (setsockopt(my->_sock.native(), SOL_SOCKET, SO_REUSEPORT,
229 (char*)&reuseport_value, sizeof(reuseport_value)) < 0)
230 {
231 if (errno == ENOPROTOOPT)
233 else
234 wlog("Error setting SO_REUSEPORT");
235 }
236 }
237#endif // __APPLE__
238 }
239
240
242 public:
244 :_accept( fc::asio::default_io_service() )
245 {
246 _accept.open(boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 0).protocol());
247 }
248
250 try {
251 _accept.close();
252 }
253 catch ( boost::system::system_error& )
254 {
255 wlog( "unexpected exception ${e}", ("e", fc::except_str()) );
256 }
257 }
258
259 boost::asio::ip::tcp::acceptor _accept;
260 };
261 void tcp_server::close() {
262 if( my && my->_accept.is_open() )
263 my->_accept.close();
264 delete my;
265 my = nullptr;
266 }
267 tcp_server::tcp_server()
268 :my(nullptr) {
269 }
270 tcp_server::~tcp_server() {
271 delete my;
272 }
273
274
275 void tcp_server::accept( tcp_socket& s )
276 {
277 try
278 {
279 FC_ASSERT( my != nullptr );
280 fc::asio::tcp::accept( my->_accept, s.my->_sock );
281 } FC_RETHROW_EXCEPTIONS( warn, "Unable to accept connection on socket." );
282 }
283 void tcp_server::set_reuse_address(bool enable /* = true */)
284 {
285 if( !my )
286 my = new impl;
287 boost::asio::ip::tcp::acceptor::reuse_address option(enable);
288 my->_accept.set_option(option);
289#if defined(__APPLE__) || (defined(__linux__) && defined(SO_REUSEPORT))
290 // OSX needs SO_REUSEPORT in addition to SO_REUSEADDR.
291 // This probably needs to be set for any BSD
293 {
294 int reuseport_value = 1;
295 if (setsockopt(my->_accept.native(), SOL_SOCKET, SO_REUSEPORT,
296 (char*)&reuseport_value, sizeof(reuseport_value)) < 0)
297 {
298 if (errno == ENOPROTOOPT)
300 else
301 wlog("Error setting SO_REUSEPORT");
302 }
303 }
304#endif // __APPLE__
305 }
306 void tcp_server::listen( uint16_t port )
307 {
308 if( !my )
309 my = new impl;
310 try
311 {
312 my->_accept.bind(boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4(), port));
313 my->_accept.listen(256);
314 }
315 FC_RETHROW_EXCEPTIONS(warn, "error listening on socket");
316 }
317 void tcp_server::listen( const fc::ip::endpoint& ep )
318 {
319 if( !my )
320 my = new impl;
321 try
322 {
323 my->_accept.bind(boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::from_string((string)ep.get_address()), ep.port()));
324 my->_accept.listen();
325 }
326 FC_RETHROW_EXCEPTIONS(warn, "error listening on socket");
327 }
328
329 fc::ip::endpoint tcp_server::get_local_endpoint() const
330 {
331 FC_ASSERT( my != nullptr );
332 return fc::ip::endpoint(my->_accept.local_endpoint().address().to_v4().to_ulong(),
333 my->_accept.local_endpoint().port() );
334 }
335
336 uint16_t tcp_server::get_port()const
337 {
338 FC_ASSERT( my != nullptr );
339 return my->_accept.local_endpoint().port();
340 }
341
342
343
344} // namespace fc
const address & get_address() const
Definition ip.cpp:72
uint16_t port() const
Definition ip.cpp:71
constexpr int64_t count() const
Definition time.hpp:26
boost::asio::ip::tcp::acceptor _accept
virtual size_t readsome(boost::asio::ip::tcp::socket &socket, char *buffer, size_t length) override
future< size_t > _write_in_progress
future< size_t > _read_in_progress
boost::asio::ip::tcp::socket _sock
virtual size_t writesome(boost::asio::ip::tcp::socket &socket, const char *buffer, size_t length) override
tcp_socket_io_hooks * _io_hooks
Defines exception's used by fc.
#define FC_THROW( ...)
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
#define FC_RETHROW_EXCEPTIONS(LOG_LEVEL, FORMAT,...)
Catchs all exception's, std::exceptions, and ... and rethrows them after appending the provided log m...
#define wlog(FORMAT,...)
Definition logger.hpp:124
#define elog(FORMAT,...)
Definition logger.hpp:130
bool have_so_reuseport
namespace sysio::chain
Definition authority.cpp:3
std::string except_str()
constexpr microseconds milliseconds(int64_t s)
Definition time.hpp:33
constexpr microseconds seconds(int64_t s)
Definition time.hpp:32
unsigned short uint16_t
Definition stdint.h:125
void rep()
yh_option option
Definition yubihsm.h:685
char * s
size_t len
uint8_t buf[2048]