join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
stream_socket.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_CORE_STREAM_SOCKET_HPP
26#define JOIN_CORE_STREAM_SOCKET_HPP
27
28// libjoin.
29#include <join/raw_socket.hpp>
30
31// C++.
32#include <chrono>
33
34// C.
35#include <sys/socket.h>
36
37namespace join
38{
42 template <class Protocol>
43 class BasicStreamSocket final : public BasicRawSocket<Protocol>
44 {
46 template <class P, class E, size_t N>
48
49 public:
50 using Ptr = std::unique_ptr<BasicStreamSocket<Protocol>>;
54 using Endpoint = typename Protocol::Endpoint;
56
62 {
63 }
64
69 explicit BasicStreamSocket (Mode mode) noexcept
71 {
72 }
73
80 explicit BasicStreamSocket (int fd, const Endpoint& remote, Mode mode = Mode::NonBlocking)
81 : BasicRawSocket<Protocol> (mode)
82 {
83 this->_handle = fd;
84 this->_state = State::Connected;
85 this->_protocol = remote.protocol ();
86 _remote = remote;
87
88 if (this->protocol () == IPPROTO_TCP)
89 {
90 setOption (Option::NoDelay, 1);
91 }
92 }
93
98 BasicStreamSocket (const BasicStreamSocket& other) = delete;
99
106
112 : BasicRawSocket<Protocol> (std::move (other))
113 , _remote (std::move (other._remote))
114 {
115 }
116
123 {
124 BasicRawSocket<Protocol>::operator= (std::move (other));
125
126 _remote = std::move (other._remote);
127
128 return *this;
129 }
130
134 ~BasicStreamSocket () = default;
135
141 int connect (const Endpoint& endpoint) noexcept
142 {
143 if ((this->_state != State::Closed) && (this->_state != State::Disconnected))
144 {
145 lastError = make_error_code (Errc::InUse);
146 return -1;
147 }
148
149 if ((this->_state == State::Closed) && (this->open (endpoint.protocol ()) == -1))
150 {
151 return -1;
152 }
153
154 int result = ::connect (this->_handle, endpoint.addr (), endpoint.length ());
155
156 this->_state = State::Connecting;
157 _remote = endpoint;
158
159 if (result == -1)
160 {
161 lastError = std::error_code (errno, std::generic_category ());
162 if (lastError != std::errc::operation_in_progress)
163 {
164 close ();
165 }
166 return -1;
167 }
168
169 this->_state = State::Connected;
170
171 return 0;
172 }
173
179 {
180 return waitConnected (TimePoint::max ());
181 }
182
188 bool waitConnected (std::chrono::nanoseconds timeout)
189 {
190 return waitConnected (std::chrono::steady_clock::now () + timeout);
191 }
192
198 bool waitConnected (TimePoint deadline)
199 {
200 if (this->_state != State::Connected)
201 {
202 if (this->_state != State::Connecting)
203 {
205 return false;
206 }
207
208 if (this->waitUntil (false, true, deadline) == -1)
209 {
210 return false;
211 }
212
213 return connected ();
214 }
215
216 return true;
217 }
218
223 int disconnect () noexcept
224 {
225 if (this->_state == State::Connected)
226 {
227 ::shutdown (this->_handle, SHUT_WR);
228 this->_state = State::Disconnecting;
229 }
230
231 if (this->_state == State::Disconnecting)
232 {
233 char buffer[4096];
234 // closing before reading can make the client
235 // not see all of our output.
236 // we have to do a "lingering close"
237 for (;;)
238 {
239 int result = this->read (buffer, sizeof (buffer));
240 if (result <= 0)
241 {
242 if ((result == -1) && (lastError == Errc::TemporaryError))
243 {
244 return -1;
245 }
246
247 break;
248 }
249 }
250
251 ::shutdown (this->_handle, SHUT_RD);
252 this->_state = State::Disconnected;
253 }
254
255 return 0;
256 }
257
263 {
264 return waitDisconnected (TimePoint::max ());
265 }
266
272 bool waitDisconnected (std::chrono::nanoseconds timeout)
273 {
274 return waitDisconnected (std::chrono::steady_clock::now () + timeout);
275 }
276
283 {
284 if (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (this->_mode == Mode::Blocking)))
285 {
287 return false;
288 }
289
290 if ((this->_state != State::Disconnected) && (this->_state != State::Closed))
291 {
292 if (this->_state != State::Disconnecting)
293 {
295 return false;
296 }
297
298 while (lastError == Errc::TemporaryError)
299 {
300 if (this->waitUntil (true, false, deadline) == -1)
301 {
302 return false;
303 }
304
305 if (disconnect () == 0)
306 {
307 return true;
308 }
309 }
310
311 return false;
312 }
313
314 return true;
315 }
316
320 void close () noexcept override
321 {
323 _remote = {};
324 }
325
332 ssize_t read (char* data, size_t maxSize) noexcept
333 {
334 ssize_t size = BasicRawSocket<Protocol>::read (data, maxSize);
335 if (size == 0)
336 {
338 return -1;
339 }
340
341 return size;
342 }
343
350 int readExactly (char* data, size_t size) noexcept
351 {
352 return readExactly (data, size, TimePoint::max ());
353 }
354
362 int readExactly (char* data, size_t size, std::chrono::nanoseconds timeout) noexcept
363 {
364 return readExactly (data, size, std::chrono::steady_clock::now () + timeout);
365 }
366
374 int readExactly (char* data, size_t size, TimePoint deadline) noexcept
375 {
376 if (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (this->_mode == Mode::Blocking)))
377 {
379 return -1;
380 }
381
382 size_t numRead = 0;
383
384 while (numRead < size)
385 {
386 ssize_t result = this->read (data + numRead, size - numRead);
387 if (result == -1)
388 {
389 if (lastError == Errc::TemporaryError)
390 {
391 if (this->waitUntil (true, false, deadline) == 0)
392 {
393 continue;
394 }
395 }
396
397 return -1;
398 }
399
400 numRead += result;
401 }
402
403 return 0;
404 }
405
412 int writeExactly (const char* data, size_t size) noexcept
413 {
414 return writeExactly (data, size, TimePoint::max ());
415 }
416
424 int writeExactly (const char* data, size_t size, std::chrono::nanoseconds timeout) noexcept
425 {
426 return writeExactly (data, size, std::chrono::steady_clock::now () + timeout);
427 }
428
436 int writeExactly (const char* data, size_t size, TimePoint deadline) noexcept
437 {
438 if (JOIN_UNLIKELY ((deadline != TimePoint::max ()) && (this->_mode == Mode::Blocking)))
439 {
441 return -1;
442 }
443
444 size_t numWrite = 0;
445
446 while (numWrite < size)
447 {
448 ssize_t result = this->write (data + numWrite, size - numWrite);
449 if (result == -1)
450 {
451 if (lastError == Errc::TemporaryError)
452 {
453 if (this->waitUntil (false, true, deadline) == 0)
454 {
455 continue;
456 }
457 }
458
459 return -1;
460 }
461
462 numWrite += result;
463 }
464
465 return 0;
466 }
467
474 int setOption (Option option, int value) noexcept override
475 {
476 if (this->_state == State::Closed)
477 {
479 return -1;
480 }
481
482 int optlevel, optname;
483
484 switch (option)
485 {
486 case Option::NoDelay:
487 optlevel = IPPROTO_TCP;
488 optname = TCP_NODELAY;
489 break;
490
491 case Option::KeepIdle:
492 optlevel = IPPROTO_TCP;
493 optname = TCP_KEEPIDLE;
494 break;
495
496 case Option::KeepIntvl:
497 optlevel = IPPROTO_TCP;
498 optname = TCP_KEEPINTVL;
499 break;
500
501 case Option::KeepCount:
502 optlevel = IPPROTO_TCP;
503 optname = TCP_KEEPCNT;
504 break;
505
506 default:
507 return BasicRawSocket<Protocol>::setOption (option, value);
508 }
509
510 int result = ::setsockopt (this->_handle, optlevel, optname, &value, sizeof (value));
511 if (result == -1)
512 {
513 lastError = std::error_code (errno, std::generic_category ());
514 return -1;
515 }
516
517 return 0;
518 }
519
524 const Endpoint& remoteEndpoint () const noexcept
525 {
526 if (_remote == Endpoint ())
527 {
528 struct sockaddr_storage sa;
529 socklen_t sa_len = sizeof (struct sockaddr_storage);
530
531 if (::getpeername (this->_handle, reinterpret_cast<struct sockaddr*> (&sa), &sa_len) != -1)
532 {
533 _remote = Endpoint (reinterpret_cast<struct sockaddr*> (&sa), sa_len);
534 }
535 }
536
537 return _remote;
538 }
539
544 bool connecting () const noexcept
545 {
546 return (this->_state == State::Connecting);
547 }
548
555 bool connected () noexcept
556 {
557 if (this->_state == State::Connected)
558 {
559 return true;
560 }
561 else if (this->_state != State::Connecting)
562 {
563 return false;
564 }
565
566 int optval;
567 socklen_t optlen = sizeof (optval);
568
569 int result = ::getsockopt (this->_handle, SOL_SOCKET, SO_ERROR, &optval, &optlen);
570 if ((result == -1) || (optval != 0))
571 {
572 return false;
573 }
574
575 this->_state = State::Connected;
576
577 return true;
578 }
579
584 int mtu () const noexcept
585 {
586 if (this->_state == State::Closed)
587 {
589 return -1;
590 }
591
592 int result = -1, value = -1;
593 socklen_t valueLen = sizeof (value);
594
595 if (this->family () == AF_INET6)
596 {
597 result = ::getsockopt (this->_handle, IPPROTO_IPV6, IPV6_MTU, &value, &valueLen);
598 }
599 else if (this->family () == AF_INET)
600 {
601 result = ::getsockopt (this->_handle, IPPROTO_IP, IP_MTU, &value, &valueLen);
602 }
603 else
604 {
606 return -1;
607 }
608
609 if (result == -1)
610 {
611 lastError = std::error_code (errno, std::generic_category ());
612 return -1;
613 }
614
615 return value;
616 }
617
618 protected:
621 };
622
629 template <class Protocol>
631 {
632 return a.handle () < b.handle ();
633 }
634}
635
636#endif
asynchronous stream socket class.
Definition protocol.hpp:91
basic raw socket class.
Definition raw_socket.hpp:42
typename BasicSocket< Protocol >::Mode Mode
Definition raw_socket.hpp:45
ssize_t write(const char *data, size_t maxSize) noexcept
write data.
Definition raw_socket.hpp:247
typename BasicSocket< Protocol >::State State
Definition raw_socket.hpp:46
Option
socket options.
Definition raw_socket.hpp:53
BasicRawSocket & operator=(const BasicRawSocket &other)=delete
copy assignment operator.
ssize_t read(char *data, size_t maxSize) noexcept
read data.
Definition raw_socket.hpp:211
virtual int setOption(Option option, int value) noexcept
set the given option to the given value.
Definition raw_socket.hpp:277
BasicRawSocket() noexcept
default constructor.
Definition raw_socket.hpp:76
virtual int open(const Protocol &protocol=Protocol()) noexcept
open socket using the given protocol.
Definition socket.hpp:167
std::chrono::steady_clock::time_point TimePoint
Definition socket.hpp:60
Mode mode() const noexcept
get the blocking mode of the socket.
Definition socket.hpp:373
Protocol _protocol
protocol.
Definition socket.hpp:476
Mode _mode
socket mode.
Definition socket.hpp:470
virtual void close() noexcept
close the socket.
Definition socket.hpp:202
int family() const noexcept
get socket address family.
Definition socket.hpp:346
@ NonBlocking
Definition socket.hpp:68
int waitUntil(bool wantRead, bool wantWrite, TimePoint deadline) const noexcept
wait for the socket handle to become ready, giving up at the given time point.
Definition socket.hpp:417
State _state
socket state.
Definition socket.hpp:467
int protocol() const noexcept
get socket protocol.
Definition socket.hpp:364
int _handle
socket handle.
Definition socket.hpp:473
basic stream socket class.
Definition stream_socket.hpp:44
BasicStreamSocket(int fd, const Endpoint &remote, Mode mode=Mode::NonBlocking)
create the socket instance adopting an accepted file descriptor.
Definition stream_socket.hpp:80
BasicStreamSocket(BasicStreamSocket &&other) noexcept
move constructor.
Definition stream_socket.hpp:111
int readExactly(char *data, size_t size, TimePoint deadline) noexcept
read data until size is reached, an error occurred or the deadline expired.
Definition stream_socket.hpp:374
void close() noexcept override
close the socket handle.
Definition stream_socket.hpp:320
~BasicStreamSocket()=default
destroy the instance.
bool waitDisconnected(std::chrono::nanoseconds timeout)
wait until the connection as been shut down, giving up after the given duration.
Definition stream_socket.hpp:272
bool waitConnected()
block until connected.
Definition stream_socket.hpp:178
int setOption(Option option, int value) noexcept override
set the given option to the given value.
Definition stream_socket.hpp:474
BasicStreamSocket(const BasicStreamSocket &other)=delete
copy constructor.
typename BasicRawSocket< Protocol >::Option Option
Definition stream_socket.hpp:52
typename BasicRawSocket< Protocol >::State State
Definition stream_socket.hpp:53
Endpoint _remote
remote endpoint.
Definition stream_socket.hpp:620
bool waitConnected(TimePoint deadline)
block until connected, giving up at the given time point.
Definition stream_socket.hpp:198
bool waitDisconnected(TimePoint deadline)
wait until the connection as been shut down, giving up at the given time point.
Definition stream_socket.hpp:282
bool waitConnected(std::chrono::nanoseconds timeout)
block until connected, giving up after the given duration.
Definition stream_socket.hpp:188
int writeExactly(const char *data, size_t size, std::chrono::nanoseconds timeout) noexcept
write data until size is reached, an error occurred or the given duration elapsed.
Definition stream_socket.hpp:424
typename Protocol::Endpoint Endpoint
Definition stream_socket.hpp:54
std::unique_ptr< BasicStreamSocket< Protocol > > Ptr
Definition stream_socket.hpp:50
int writeExactly(const char *data, size_t size, TimePoint deadline) noexcept
write data until size is reached, an error occurred or the deadline expired.
Definition stream_socket.hpp:436
ssize_t read(char *data, size_t maxSize) noexcept
read data.
Definition stream_socket.hpp:332
BasicStreamSocket & operator=(const BasicStreamSocket &other)=delete
copy assignment operator.
typename BasicRawSocket< Protocol >::Mode Mode
Definition stream_socket.hpp:51
BasicStreamSocket() noexcept
default constructor.
Definition stream_socket.hpp:60
int connect(const Endpoint &endpoint) noexcept
make a connection to the given endpoint.
Definition stream_socket.hpp:141
int readExactly(char *data, size_t size) noexcept
read data until size is reached or an error occurred.
Definition stream_socket.hpp:350
BasicStreamSocket(Mode mode) noexcept
create instance specifying the mode.
Definition stream_socket.hpp:69
bool waitDisconnected()
wait until the connection as been shut down. return true if the connection as been shut down,...
Definition stream_socket.hpp:262
const Endpoint & remoteEndpoint() const noexcept
determine the remote endpoint associated with this socket.
Definition stream_socket.hpp:524
int writeExactly(const char *data, size_t size) noexcept
write data until size is reached or an error occurred.
Definition stream_socket.hpp:412
int disconnect() noexcept
shutdown the connection.
Definition stream_socket.hpp:223
int mtu() const noexcept
get socket mtu.
Definition stream_socket.hpp:584
int readExactly(char *data, size_t size, std::chrono::nanoseconds timeout) noexcept
read data until size is reached, an error occurred or the given duration elapsed.
Definition stream_socket.hpp:362
bool connecting() const noexcept
check if the socket is connecting.
Definition stream_socket.hpp:544
bool connected() noexcept
check if the socket is connected.
Definition stream_socket.hpp:555
typename BasicRawSocket< Protocol >::TimePoint TimePoint
Definition stream_socket.hpp:55
Definition acceptor.hpp:32
bool operator<(const BasicDatagramSocket< Protocol > &a, const BasicDatagramSocket< Protocol > &b) noexcept
compare if socket handle is inferior.
Definition datagram_socket.hpp:394
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:195
#define JOIN_UNLIKELY(x)
Definition utils.hpp:46