
The _Tgt and _TgtImpl types that implement type-erasure didn't agree on the virtual interface, so failed as soon as they were instantiated. With Clang they failed even sooner. The interface was also dependent on whether RTTI was enabled or not. This patch fixes the broken virtual functions and makes the type work without RTTI, by using a pointer to a specialization of a function template (similar to the approaches in std::function and std::any). The changes to the virtual functions would be an ABI change, except that the previous code didn't even compile if instantiated. This is experimental TS material anyway. PR libstdc++/94203 * include/experimental/executor (executor::executor(Executor)): Call make_shared directly instead of _M_create. Create _Tgt1 object. (executor::executor(allocator_arg_t, const ProtoAlloc&, Executor)): Call allocate_shared directly instead of _M_create. Create _Tgt2 object. (executor::target_type): Add cast needed for new _Tgt interface. (executor::target): Define when RTTI is disabled. Use _Tgt::_M_func. (executor::_Tgt): Define the same interface whether RTTI is enabled or not. (executor::_Tgt::target_type, executor::_Tgt::target): Do not use std::type_info in the interface. (executor::_Tgt::_M_func): Add data member. (executor::_TgtImpl): Replace with _Tgt1 and _Tgt2 class templates. (executor::_Tgt1::_S_func): Define function to access target without depending on RTTI. (executor::_M_create): Remove. (operator==, operator!=): Simplify comparisons for executor. * include/experimental/socket (is_error_code_enum<socket_errc>): Define specialization before use. * testsuite/experimental/net/executor/1.cc: New test.
2624 lines
74 KiB
C++
2624 lines
74 KiB
C++
// <experimental/socket> -*- C++ -*-
|
|
|
|
// Copyright (C) 2015-2020 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// Under Section 7 of GPL version 3, you are granted additional
|
|
// permissions described in the GCC Runtime Library Exception, version
|
|
// 3.1, as published by the Free Software Foundation.
|
|
|
|
// You should have received a copy of the GNU General Public License and
|
|
// a copy of the GCC Runtime Library Exception along with this program;
|
|
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
/** @file experimental/socket
|
|
* This is a TS C++ Library header.
|
|
* @ingroup networking-ts
|
|
*/
|
|
|
|
#ifndef _GLIBCXX_EXPERIMENTAL_SOCKET
|
|
#define _GLIBCXX_EXPERIMENTAL_SOCKET
|
|
|
|
#pragma GCC system_header
|
|
|
|
#if __cplusplus >= 201402L
|
|
|
|
#include <experimental/netfwd>
|
|
#include <experimental/buffer>
|
|
#include <experimental/io_context>
|
|
#include <experimental/bits/net.h>
|
|
#include <streambuf>
|
|
#include <istream>
|
|
#include <bits/unique_ptr.h>
|
|
#if _GLIBCXX_HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
# ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
# include <sys/socket.h> // socket etc
|
|
# endif
|
|
# ifdef _GLIBCXX_HAVE_SYS_IOCTL_H
|
|
# include <sys/ioctl.h> // ioctl
|
|
# endif
|
|
# ifdef _GLIBCXX_HAVE_SYS_UIO_H
|
|
# include <sys/uio.h> // iovec
|
|
# endif
|
|
# ifdef _GLIBCXX_HAVE_POLL_H
|
|
# include <poll.h> // poll, pollfd, POLLIN, POLLOUT, POLLERR
|
|
# endif
|
|
# ifdef _GLIBCXX_HAVE_FCNTL_H
|
|
# include <fcntl.h> // fcntl, F_GETFL, F_SETFL
|
|
# endif
|
|
#endif
|
|
|
|
namespace std _GLIBCXX_VISIBILITY(default)
|
|
{
|
|
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
|
namespace experimental
|
|
{
|
|
namespace net
|
|
{
|
|
inline namespace v1
|
|
{
|
|
|
|
/** @addtogroup networking-ts
|
|
* @{
|
|
*/
|
|
|
|
enum class socket_errc { // TODO decide values
|
|
already_open = 3,
|
|
not_found = 4
|
|
};
|
|
|
|
} // namespace v1
|
|
} // namespace net
|
|
} // namespace experimental
|
|
|
|
template<>
|
|
struct is_error_code_enum<experimental::net::v1::socket_errc>
|
|
: public true_type {};
|
|
|
|
namespace experimental
|
|
{
|
|
namespace net
|
|
{
|
|
inline namespace v1
|
|
{
|
|
const error_category& socket_category() noexcept
|
|
{
|
|
struct __cat : error_category
|
|
{
|
|
const char* name() const noexcept { return "socket"; }
|
|
|
|
std::string message(int __e) const
|
|
{
|
|
if (__e == (int)socket_errc::already_open)
|
|
return "already open";
|
|
else if (__e == (int)socket_errc::not_found)
|
|
return "endpoint not found";
|
|
return "socket error";
|
|
}
|
|
|
|
virtual void __message(int) { } // TODO dual ABI XXX
|
|
};
|
|
static __cat __c;
|
|
return __c;
|
|
}
|
|
|
|
inline error_code
|
|
make_error_code(socket_errc __e) noexcept
|
|
{ return error_code(static_cast<int>(__e), socket_category()); }
|
|
|
|
inline error_condition
|
|
make_error_condition(socket_errc __e) noexcept
|
|
{ return error_condition(static_cast<int>(__e), socket_category()); }
|
|
|
|
template<typename _Tp, typename = __void_t<>>
|
|
struct __is_endpoint_impl : false_type
|
|
{ };
|
|
|
|
// Check Endpoint requirements.
|
|
template<typename _Tp>
|
|
auto
|
|
__endpoint_reqs(const _Tp* __a = 0)
|
|
-> enable_if_t<__and_<
|
|
is_default_constructible<_Tp>,
|
|
__is_value_constructible<_Tp>,
|
|
is_same<decltype(__a->__protocol()), typename _Tp::protocol_type>
|
|
>::value,
|
|
__void_t< typename _Tp::protocol_type::endpoint >>;
|
|
|
|
template<typename _Tp>
|
|
struct __is_endpoint_impl<_Tp, decltype(__endpoint_reqs<_Tp>())>
|
|
: true_type
|
|
{ };
|
|
|
|
template<typename _Tp>
|
|
struct __is_endpoint : __is_endpoint_impl<_Tp>
|
|
{ };
|
|
|
|
// TODO Endpoint reqs for extensible implementations
|
|
// TODO _Protocol reqs
|
|
// TODO AcceptableProtocol reqs
|
|
// TODO GettableSocket reqs
|
|
// TODO SettableSocket reqs
|
|
// TODO BooleanSocketOption reqs
|
|
// TODO IntegerSocketOption reqs
|
|
// TODO _IoControlCommand reqs
|
|
// TODO _ConnectCondition reqs
|
|
|
|
/** @brief Sockets
|
|
* @{
|
|
*/
|
|
|
|
class socket_base
|
|
{
|
|
public:
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
struct broadcast : __sockopt_crtp<broadcast, bool>
|
|
{
|
|
using __sockopt_crtp::__sockopt_crtp;
|
|
|
|
static const int _S_level = SOL_SOCKET;
|
|
static const int _S_name = SO_BROADCAST;
|
|
};
|
|
|
|
struct debug : __sockopt_crtp<debug, bool>
|
|
{
|
|
using __sockopt_crtp::__sockopt_crtp;
|
|
|
|
static const int _S_level = SOL_SOCKET;
|
|
static const int _S_name = SO_DEBUG;
|
|
};
|
|
|
|
struct do_not_route : __sockopt_crtp<do_not_route, bool>
|
|
{
|
|
using __sockopt_crtp::__sockopt_crtp;
|
|
|
|
static const int _S_level = SOL_SOCKET;
|
|
static const int _S_name = SO_DONTROUTE;
|
|
};
|
|
|
|
struct keep_alive : __sockopt_crtp<keep_alive, bool>
|
|
{
|
|
using __sockopt_crtp::__sockopt_crtp;
|
|
|
|
static const int _S_level = SOL_SOCKET;
|
|
static const int _S_name = SO_KEEPALIVE;
|
|
};
|
|
|
|
struct linger : __sockopt_crtp<linger, ::linger>
|
|
{
|
|
using __sockopt_crtp::__sockopt_crtp;
|
|
|
|
linger() noexcept = default;
|
|
|
|
linger(bool __e, chrono::seconds __t) noexcept
|
|
{
|
|
enabled(__e);
|
|
timeout(__t);
|
|
}
|
|
|
|
bool
|
|
enabled() const noexcept
|
|
{ return _M_value.l_onoff != 0; }
|
|
|
|
void
|
|
enabled(bool __e) noexcept
|
|
{ _M_value.l_onoff = int(__e); }
|
|
|
|
chrono::seconds
|
|
timeout() const noexcept
|
|
{ return chrono::seconds(_M_value.l_linger); }
|
|
|
|
void
|
|
timeout(chrono::seconds __t) noexcept
|
|
{ _M_value.l_linger = __t.count(); }
|
|
|
|
static const int _S_level = SOL_SOCKET;
|
|
static const int _S_name = SO_LINGER;
|
|
};
|
|
|
|
struct out_of_band_inline : __sockopt_crtp<out_of_band_inline, bool>
|
|
{
|
|
using __sockopt_crtp::__sockopt_crtp;
|
|
|
|
static const int _S_level = SOL_SOCKET;
|
|
static const int _S_name = SO_OOBINLINE;
|
|
};
|
|
|
|
struct receive_buffer_size : __sockopt_crtp<receive_buffer_size>
|
|
{
|
|
using __sockopt_crtp::__sockopt_crtp;
|
|
|
|
static const int _S_level = SOL_SOCKET;
|
|
static const int _S_name = SO_RCVBUF;
|
|
};
|
|
|
|
struct receive_low_watermark : __sockopt_crtp<receive_low_watermark>
|
|
{
|
|
using __sockopt_crtp::__sockopt_crtp;
|
|
|
|
static const int _S_level = SOL_SOCKET;
|
|
static const int _S_name = SO_RCVLOWAT;
|
|
};
|
|
|
|
struct reuse_address : __sockopt_crtp<reuse_address, bool>
|
|
{
|
|
using __sockopt_crtp::__sockopt_crtp;
|
|
|
|
static const int _S_level = SOL_SOCKET;
|
|
static const int _S_name = SO_REUSEADDR;
|
|
};
|
|
|
|
struct send_buffer_size : __sockopt_crtp<send_buffer_size>
|
|
{
|
|
using __sockopt_crtp::__sockopt_crtp;
|
|
|
|
static const int _S_level = SOL_SOCKET;
|
|
static const int _S_name = SO_SNDBUF;
|
|
};
|
|
|
|
struct send_low_watermark : __sockopt_crtp<send_low_watermark>
|
|
{
|
|
using __sockopt_crtp::__sockopt_crtp;
|
|
|
|
static const int _S_level = SOL_SOCKET;
|
|
static const int _S_name = SO_SNDLOWAT;
|
|
};
|
|
|
|
enum shutdown_type : int
|
|
{
|
|
__shutdown_receive = SHUT_RD,
|
|
__shutdown_send = SHUT_WR,
|
|
__shutdown_both = SHUT_RDWR
|
|
};
|
|
static constexpr shutdown_type shutdown_receive = __shutdown_receive;
|
|
static constexpr shutdown_type shutdown_send = __shutdown_send;
|
|
static constexpr shutdown_type shutdown_both = __shutdown_both;
|
|
|
|
#ifdef _GLIBCXX_HAVE_POLL_H
|
|
enum wait_type : int
|
|
{
|
|
__wait_read = POLLIN,
|
|
__wait_write = POLLOUT,
|
|
__wait_error = POLLERR
|
|
};
|
|
static constexpr wait_type wait_read = __wait_read;
|
|
static constexpr wait_type wait_write = __wait_write;
|
|
static constexpr wait_type wait_error = __wait_error;
|
|
#endif
|
|
|
|
enum message_flags : int
|
|
{
|
|
__message_peek = MSG_PEEK,
|
|
__message_oob = MSG_OOB,
|
|
__message_dontroute = MSG_DONTROUTE
|
|
};
|
|
static constexpr message_flags message_peek = __message_peek;
|
|
static constexpr message_flags message_out_of_band = __message_oob;
|
|
static constexpr message_flags message_do_not_route = __message_dontroute;
|
|
|
|
static const int max_listen_connections = SOMAXCONN;
|
|
#endif
|
|
|
|
protected:
|
|
socket_base() = default;
|
|
~socket_base() = default;
|
|
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
struct __msg_hdr : ::msghdr
|
|
{
|
|
#ifdef IOV_MAX
|
|
using __iovec_array = array<::iovec, IOV_MAX>;
|
|
#elif _GLIBCXX_HAVE_UNISTD_H
|
|
struct __iovec_array
|
|
{
|
|
__iovec_array() : _M_ptr(new ::iovec[size()]) { }
|
|
|
|
::iovec& operator[](size_t __n) noexcept { return _M_ptr[__n]; }
|
|
|
|
::iovec* data() noexcept { return _M_ptr.get(); }
|
|
|
|
static size_t size()
|
|
{
|
|
static const size_t __iov_max = ::sysconf(_SC_IOV_MAX);
|
|
return __iov_max;
|
|
}
|
|
|
|
private:
|
|
unique_ptr<::iovec[]> _M_ptr;
|
|
};
|
|
#else
|
|
using __iovec_array = array<::iovec, 16>;
|
|
#endif
|
|
|
|
__iovec_array _M_iov;
|
|
|
|
template<typename _BufferSequence>
|
|
explicit
|
|
__msg_hdr(const _BufferSequence& __buffers)
|
|
: msghdr()
|
|
{
|
|
auto __buf = net::buffer_sequence_begin(__buffers);
|
|
const auto __bufend = net::buffer_sequence_end(__buffers);
|
|
size_t __len = 0;
|
|
while (__buf != __bufend && __len != _M_iov.size())
|
|
{
|
|
_M_iov[__len].iov_base = (void*)__buf->data();
|
|
_M_iov[__len].iov_len = __buf->size();
|
|
++__buf;
|
|
++__len;
|
|
}
|
|
this->msg_iovlen = __len;
|
|
this->msg_iov = _M_iov.data();
|
|
}
|
|
|
|
template<typename _BufferSequence, typename _Endpoint>
|
|
__msg_hdr(const _BufferSequence& __buffers, const _Endpoint& __ep)
|
|
: __msg_hdr(__buffers)
|
|
{
|
|
this->msg_name = __ep.data();
|
|
this->msg_namelen = __ep.size();
|
|
}
|
|
};
|
|
#endif
|
|
};
|
|
|
|
constexpr socket_base::message_flags
|
|
operator&(socket_base::message_flags __f1, socket_base::message_flags __f2)
|
|
{ return socket_base::message_flags( int(__f1) & int(__f2) ); }
|
|
|
|
constexpr socket_base::message_flags
|
|
operator|(socket_base::message_flags __f1, socket_base::message_flags __f2)
|
|
{ return socket_base::message_flags( int(__f1) | int(__f2) ); }
|
|
|
|
constexpr socket_base::message_flags
|
|
operator^(socket_base::message_flags __f1, socket_base::message_flags __f2)
|
|
{ return socket_base::message_flags( int(__f1) ^ int(__f2) ); }
|
|
|
|
constexpr socket_base::message_flags
|
|
operator~(socket_base::message_flags __f)
|
|
{ return socket_base::message_flags( ~int(__f) ); }
|
|
|
|
inline socket_base::message_flags&
|
|
operator&=(socket_base::message_flags& __f1, socket_base::message_flags __f2)
|
|
{ return __f1 = (__f1 & __f2); }
|
|
|
|
inline socket_base::message_flags&
|
|
operator|=(socket_base::message_flags& __f1, socket_base::message_flags __f2)
|
|
{ return __f1 = (__f1 | __f2); }
|
|
|
|
inline socket_base::message_flags&
|
|
operator^=(socket_base::message_flags& __f1, socket_base::message_flags __f2)
|
|
{ return __f1 = (__f1 ^ __f2); }
|
|
|
|
#if _GLIBCXX_HAVE_UNISTD_H
|
|
|
|
class __socket_impl
|
|
{
|
|
protected:
|
|
|
|
using executor_type = io_context::executor_type;
|
|
using native_handle_type = int;
|
|
|
|
explicit
|
|
__socket_impl(io_context& __ctx) : _M_ctx(std::addressof(__ctx)) { }
|
|
|
|
__socket_impl(__socket_impl&& __rhs)
|
|
: _M_ctx(__rhs._M_ctx),
|
|
_M_sockfd(std::exchange(__rhs._M_sockfd, -1)),
|
|
_M_bits(std::exchange(__rhs._M_bits, {}))
|
|
{ }
|
|
|
|
__socket_impl&
|
|
operator=(__socket_impl&& __rhs)
|
|
{
|
|
_M_ctx = __rhs._M_ctx;
|
|
_M_sockfd = std::exchange(__rhs._M_sockfd, -1);
|
|
_M_bits = std::exchange(__rhs._M_bits, {});
|
|
return *this;
|
|
}
|
|
|
|
~__socket_impl() = default;
|
|
|
|
__socket_impl(const __socket_impl&) = delete;
|
|
__socket_impl& operator=(const __socket_impl&) = delete;
|
|
|
|
executor_type get_executor() noexcept { return _M_ctx->get_executor(); }
|
|
|
|
native_handle_type native_handle() noexcept { return _M_sockfd; }
|
|
|
|
bool is_open() const noexcept { return _M_sockfd != -1; }
|
|
|
|
void
|
|
close(error_code& __ec)
|
|
{
|
|
if (is_open())
|
|
{
|
|
cancel(__ec);
|
|
if (!__ec)
|
|
{
|
|
if (::close(_M_sockfd) == -1)
|
|
__ec.assign(errno, generic_category());
|
|
else
|
|
{
|
|
get_executor().context()._M_remove_fd(_M_sockfd);
|
|
_M_sockfd = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cancel(error_code& __ec) { _M_ctx->cancel(_M_sockfd, __ec); }
|
|
|
|
void
|
|
non_blocking(bool __mode, error_code&)
|
|
{ _M_bits.non_blocking = __mode; }
|
|
|
|
bool non_blocking() const { return _M_bits.non_blocking; }
|
|
|
|
void
|
|
native_non_blocking(bool __mode, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_FCNTL_H
|
|
int __flags = ::fcntl(_M_sockfd, F_GETFL, 0);
|
|
if (__flags >= 0)
|
|
{
|
|
if (__mode)
|
|
__flags |= O_NONBLOCK;
|
|
else
|
|
__flags &= ~O_NONBLOCK;
|
|
__flags = ::fcntl(_M_sockfd, F_SETFL, __flags);
|
|
}
|
|
if (__flags == -1)
|
|
__ec.assign(errno, generic_category());
|
|
else
|
|
{
|
|
__ec.clear();
|
|
_M_bits.native_non_blocking = __mode;
|
|
}
|
|
#else
|
|
__ec = std::make_error_code(std::errc::not_supported);
|
|
#endif
|
|
}
|
|
|
|
bool
|
|
native_non_blocking() const
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_FCNTL_H
|
|
if (_M_bits.native_non_blocking == -1)
|
|
{
|
|
const int __flags = ::fcntl(_M_sockfd, F_GETFL, 0);
|
|
if (__flags == -1)
|
|
return 0;
|
|
_M_bits.native_non_blocking = __flags & O_NONBLOCK;
|
|
}
|
|
return _M_bits.native_non_blocking;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
io_context* _M_ctx;
|
|
int _M_sockfd{-1};
|
|
struct {
|
|
unsigned non_blocking : 1;
|
|
mutable signed native_non_blocking : 2;
|
|
unsigned enable_connection_aborted : 1;
|
|
} _M_bits{};
|
|
};
|
|
|
|
template<typename _Protocol>
|
|
class __basic_socket_impl : public __socket_impl
|
|
{
|
|
using __base = __socket_impl;
|
|
|
|
protected:
|
|
using protocol_type = _Protocol;
|
|
using endpoint_type = typename protocol_type::endpoint;
|
|
|
|
explicit
|
|
__basic_socket_impl(io_context& __ctx) : __base(__ctx) { }
|
|
|
|
__basic_socket_impl(__basic_socket_impl&&) = default;
|
|
|
|
template<typename _OtherProtocol>
|
|
__basic_socket_impl(__basic_socket_impl<_OtherProtocol>&& __rhs)
|
|
: __base(std::move(__rhs)), _M_protocol(std::move(__rhs._M_protocol))
|
|
{ }
|
|
|
|
__basic_socket_impl&
|
|
operator=(__basic_socket_impl&& __rhs)
|
|
{
|
|
if (this == std::addressof(__rhs))
|
|
return *this;
|
|
_M_close();
|
|
__base::operator=(std::move(__rhs));
|
|
return *this;
|
|
}
|
|
|
|
~__basic_socket_impl() { _M_close(); }
|
|
|
|
__basic_socket_impl(const __basic_socket_impl&) = delete;
|
|
__basic_socket_impl& operator=(const __basic_socket_impl&) = delete;
|
|
|
|
void
|
|
open(const protocol_type& __protocol, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
if (is_open())
|
|
__ec = socket_errc::already_open;
|
|
else
|
|
{
|
|
_M_protocol = __protocol;
|
|
_M_sockfd = ::socket(__protocol.family(), __protocol.type(),
|
|
__protocol.protocol());
|
|
if (is_open())
|
|
{
|
|
get_executor().context()._M_add_fd(_M_sockfd);
|
|
__ec.clear();
|
|
}
|
|
else
|
|
__ec.assign(errno, std::generic_category());
|
|
}
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
}
|
|
|
|
void
|
|
assign(const protocol_type& __protocol,
|
|
const native_handle_type& __native_socket,
|
|
error_code& __ec)
|
|
{
|
|
if (is_open())
|
|
__ec = socket_errc::already_open;
|
|
else
|
|
{
|
|
_M_protocol = __protocol;
|
|
_M_bits.native_non_blocking = -1;
|
|
_M_sockfd = __native_socket;
|
|
if (is_open())
|
|
{
|
|
get_executor().context()._M_add_fd(_M_sockfd);
|
|
__ec.clear();
|
|
}
|
|
else
|
|
__ec.assign(errno, std::generic_category());
|
|
}
|
|
}
|
|
|
|
template<typename _SettableSocketOption>
|
|
void
|
|
set_option(const _SettableSocketOption& __option, error_code& __ec)
|
|
{
|
|
int __result = ::setsockopt(_M_sockfd, __option.level(_M_protocol),
|
|
__option.name(_M_protocol),
|
|
__option.data(_M_protocol),
|
|
__option.size(_M_protocol));
|
|
if (__result == -1)
|
|
__ec.assign(errno, generic_category());
|
|
else
|
|
__ec.clear();
|
|
}
|
|
|
|
template<typename _GettableSocketOption>
|
|
void
|
|
get_option(_GettableSocketOption& __option, error_code& __ec) const
|
|
{
|
|
int __result = ::getsockopt(_M_sockfd, __option.level(_M_protocol),
|
|
__option.name(_M_protocol),
|
|
__option.data(_M_protocol),
|
|
__option.size(_M_protocol));
|
|
if (__result == -1)
|
|
__ec.assign(errno, generic_category());
|
|
else
|
|
__ec.clear();
|
|
}
|
|
|
|
template<typename _IoControlCommand>
|
|
void
|
|
io_control(_IoControlCommand& __command, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SYS_IOCTL_H
|
|
int __result = ::ioctl(_M_sockfd, __command.name(),
|
|
__command.data());
|
|
if (__result == -1)
|
|
__ec.assign(errno, generic_category());
|
|
else
|
|
__ec.clear();
|
|
#else
|
|
__ec = std::make_error_code(std::errc::not_supported);
|
|
#endif
|
|
}
|
|
|
|
endpoint_type
|
|
local_endpoint(error_code& __ec) const
|
|
{
|
|
endpoint_type __endpoint;
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socklen_t __endpoint_len = __endpoint.capacity();
|
|
if (::getsockname(_M_sockfd, (sockaddr*)__endpoint.data(),
|
|
&__endpoint_len) == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
return endpoint_type{};
|
|
}
|
|
__ec.clear();
|
|
__endpoint.resize(__endpoint_len);
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
return __endpoint;
|
|
}
|
|
|
|
void
|
|
bind(const endpoint_type& __endpoint, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
if (::bind(_M_sockfd, (sockaddr*)__endpoint.data(), __endpoint.size())
|
|
== -1)
|
|
__ec.assign(errno, generic_category());
|
|
else
|
|
__ec.clear();
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
}
|
|
|
|
_Protocol _M_protocol{ endpoint_type{}.protocol() };
|
|
|
|
private:
|
|
void
|
|
_M_close()
|
|
{
|
|
if (is_open())
|
|
{
|
|
error_code __ec;
|
|
cancel(__ec);
|
|
set_option(socket_base::linger{false, chrono::seconds{}}, __ec);
|
|
::close(_M_sockfd);
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename _Protocol>
|
|
class basic_socket
|
|
: public socket_base, private __basic_socket_impl<_Protocol>
|
|
{
|
|
using __base = __basic_socket_impl<_Protocol>;
|
|
|
|
public:
|
|
// types:
|
|
|
|
typedef io_context::executor_type executor_type;
|
|
typedef int native_handle_type;
|
|
typedef _Protocol protocol_type;
|
|
typedef typename protocol_type::endpoint endpoint_type;
|
|
|
|
// basic_socket operations:
|
|
|
|
executor_type get_executor() noexcept { return __base::get_executor(); }
|
|
|
|
native_handle_type
|
|
native_handle() noexcept { return __base::native_handle(); }
|
|
|
|
void
|
|
open(const protocol_type& __protocol = protocol_type())
|
|
{ open(__protocol, __throw_on_error{"basic_socket::open"}); }
|
|
|
|
void
|
|
open(const protocol_type& __protocol, error_code& __ec)
|
|
{ __base::open(__protocol, __ec); }
|
|
|
|
void
|
|
assign(const protocol_type& __protocol,
|
|
const native_handle_type& __native_socket)
|
|
{
|
|
assign(__protocol, __native_socket,
|
|
__throw_on_error{"basic_socket::assign"});
|
|
}
|
|
|
|
void
|
|
assign(const protocol_type& __protocol,
|
|
const native_handle_type& __native_socket,
|
|
error_code& __ec)
|
|
{ __base::assign(__protocol, __native_socket, __ec); }
|
|
|
|
bool is_open() const noexcept { return __base::is_open(); }
|
|
|
|
void close() { close(__throw_on_error{"basic_socket::close"}); }
|
|
|
|
void close(error_code& __ec) { __base::close(); }
|
|
|
|
void cancel() { cancel(__throw_on_error{"basic_socket::cancel"}); }
|
|
|
|
void cancel(error_code& __ec) { __base::cancel(__ec); }
|
|
|
|
template<typename _SettableSocketOption>
|
|
void
|
|
set_option(const _SettableSocketOption& __option)
|
|
{ set_option(__option, __throw_on_error{"basic_socket::set_option"}); }
|
|
|
|
template<typename _SettableSocketOption>
|
|
void
|
|
set_option(const _SettableSocketOption& __option, error_code& __ec)
|
|
{ __base::set_option(__option, __ec); }
|
|
|
|
template<typename _GettableSocketOption>
|
|
void
|
|
get_option(_GettableSocketOption& __option) const
|
|
{ get_option(__option, __throw_on_error{"basic_socket::get_option"}); }
|
|
|
|
template<typename _GettableSocketOption>
|
|
void
|
|
get_option(_GettableSocketOption& __option, error_code& __ec) const
|
|
{ __base::get_option(__option, __ec); }
|
|
|
|
template<typename _IoControlCommand>
|
|
void
|
|
io_control(_IoControlCommand& __command)
|
|
{
|
|
io_control(__command, __throw_on_error{"basic_socket::io_control"});
|
|
}
|
|
|
|
template<typename _IoControlCommand>
|
|
void
|
|
io_control(_IoControlCommand& __command, error_code& __ec)
|
|
{ __base::io_control(__command, __ec); }
|
|
|
|
void
|
|
non_blocking(bool __mode)
|
|
{ non_blocking(__mode, __throw_on_error{"basic_socket::non_blocking"}); }
|
|
|
|
void
|
|
non_blocking(bool __mode, error_code& __ec)
|
|
{ __base::non_blocking(__mode, __ec); }
|
|
|
|
bool non_blocking() const { return __base::non_blocking(); }
|
|
|
|
void
|
|
native_non_blocking(bool __mode)
|
|
{
|
|
native_non_blocking(__mode, __throw_on_error{
|
|
"basic_socket::native_non_blocking"});
|
|
}
|
|
|
|
void
|
|
native_non_blocking(bool __mode, error_code& __ec)
|
|
{ __base::native_non_blocking(__mode, __ec); }
|
|
|
|
bool
|
|
native_non_blocking() const
|
|
{ return __base::native_non_blocking(); }
|
|
|
|
bool at_mark() const
|
|
{ return at_mark(__throw_on_error{"basic_socket::at_mark"}); }
|
|
|
|
bool
|
|
at_mark(error_code& __ec) const
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SOCKATMARK
|
|
const int __result = ::sockatmark(native_handle());
|
|
if (__result == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
return false;
|
|
}
|
|
__ec.clear();
|
|
return (bool)__result;
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
size_t
|
|
available() const
|
|
{ return available(__throw_on_error{"basic_socket::available"}); }
|
|
|
|
size_t
|
|
available(error_code& __ec) const
|
|
{
|
|
if (!is_open())
|
|
{
|
|
__ec = std::make_error_code(errc::bad_file_descriptor);
|
|
return 0;
|
|
}
|
|
#if defined _GLIBCXX_HAVE_SYS_IOCTL_H && defined FIONREAD
|
|
int __avail = 0;
|
|
if (::ioctl(this->_M_sockfd, FIONREAD, &__avail) == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
return 0;
|
|
}
|
|
__ec.clear();
|
|
return __avail;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
bind(const endpoint_type& __endpoint)
|
|
{ return bind(__endpoint, __throw_on_error{"basic_socket::bind"}); }
|
|
|
|
void
|
|
bind(const endpoint_type& __endpoint, error_code& __ec)
|
|
{ __base::bind(__endpoint, __ec); }
|
|
|
|
void shutdown(shutdown_type __what)
|
|
{ return shutdown(__what, __throw_on_error{"basic_socket::shutdown"}); }
|
|
|
|
void
|
|
shutdown(shutdown_type __what, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
if (::shutdown(native_handle(), static_cast<int>(__what)) == -1)
|
|
__ec.assign(errno, generic_category());
|
|
else
|
|
__ec.clear();
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
}
|
|
|
|
endpoint_type
|
|
local_endpoint() const
|
|
{
|
|
return local_endpoint(
|
|
__throw_on_error{"basic_socket::local_endpoint"});
|
|
}
|
|
|
|
endpoint_type
|
|
local_endpoint(error_code& __ec) const
|
|
{ return __base::local_endpoint(__ec); }
|
|
|
|
endpoint_type
|
|
remote_endpoint() const
|
|
{
|
|
return remote_endpoint(
|
|
__throw_on_error{"basic_socket::remote_endpoint"});
|
|
}
|
|
|
|
endpoint_type
|
|
remote_endpoint(error_code& __ec) const
|
|
{
|
|
endpoint_type __endpoint;
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socklen_t __endpoint_len = __endpoint.capacity();
|
|
if (::getpeername(this->_M_sockfd, (sockaddr*)__endpoint.data(),
|
|
&__endpoint_len)
|
|
== -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
return endpoint_type{};
|
|
}
|
|
__ec.clear();
|
|
__endpoint.resize(__endpoint_len);
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
return __endpoint;
|
|
}
|
|
|
|
void
|
|
connect(const endpoint_type& __endpoint)
|
|
{
|
|
return connect(__endpoint, __throw_on_error{"basic_socket::connect"});
|
|
}
|
|
|
|
void
|
|
connect(const endpoint_type& __endpoint, error_code& __ec)
|
|
{
|
|
if (!is_open())
|
|
{
|
|
open(__endpoint.protocol(), __ec);
|
|
if (__ec)
|
|
return;
|
|
}
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
if (::connect(native_handle(), (const sockaddr*)__endpoint.data(),
|
|
__endpoint.size()) == -1)
|
|
__ec.assign(errno, generic_category());
|
|
else
|
|
__ec.clear();
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
}
|
|
|
|
template<typename _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code)>
|
|
async_connect(const endpoint_type& __endpoint,
|
|
_CompletionToken&& __token)
|
|
{
|
|
async_completion<_CompletionToken, void(error_code)> __init{__token};
|
|
|
|
if (!is_open())
|
|
{
|
|
error_code __ec;
|
|
open(__endpoint.protocol(), __ec);
|
|
if (__ec)
|
|
{
|
|
auto __ex = net::get_associated_executor(
|
|
__init.completion_handler, get_executor());
|
|
auto __a = get_associated_allocator(
|
|
__init.completion_handler, std::allocator<void>());
|
|
__ex.post(
|
|
[__h=std::move(__init.completion_handler), __ec]
|
|
() mutable
|
|
{ __h(__ec); }, __a);
|
|
return __init.result.get();
|
|
}
|
|
}
|
|
|
|
get_executor().context().async_wait( native_handle(),
|
|
socket_base::wait_read,
|
|
[__h = std::move(__init.completion_handler),
|
|
__ep = std::move(__endpoint),
|
|
__fd = native_handle()]
|
|
(error_code __ec) mutable {
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
if (!__ec && ::connect(__fd, (const sockaddr*)__ep.data(),
|
|
__ep.size()) == -1)
|
|
__ec.assign(errno, generic_category());
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
__h(__ec);
|
|
});
|
|
return __init.result.get();
|
|
}
|
|
|
|
void
|
|
wait(wait_type __w)
|
|
{ return wait(__w, __throw_on_error{"basic_socket::wait"}); }
|
|
|
|
void
|
|
wait(wait_type __w, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_POLL_H
|
|
::pollfd __fd;
|
|
__fd.fd = native_handle();
|
|
__fd.events = static_cast<int>(__w);
|
|
int __res = ::poll(&__fd, 1, -1);
|
|
if (__res == -1)
|
|
__ec.assign(errno, generic_category());
|
|
else
|
|
__ec.clear();
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
}
|
|
|
|
template<typename _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code)>
|
|
async_wait(wait_type __w, _CompletionToken&& __token)
|
|
{
|
|
async_completion<_CompletionToken, void(error_code)> __init{__token};
|
|
get_executor().context().async_wait( native_handle(),
|
|
static_cast<int>(__w),
|
|
[__h = std::move(__init.completion_handler)]
|
|
(error_code __ec) mutable {
|
|
__h(__ec);
|
|
});
|
|
return __init.result.get();
|
|
}
|
|
|
|
protected:
|
|
// construct / copy / destroy:
|
|
|
|
using __base::__base;
|
|
|
|
explicit
|
|
basic_socket(io_context& __ctx) : __base(__ctx) { }
|
|
|
|
basic_socket(io_context& __ctx, const protocol_type& __protocol)
|
|
: __base(__ctx)
|
|
{ open(__protocol); }
|
|
|
|
basic_socket(io_context& __ctx, const endpoint_type& __endpoint)
|
|
: basic_socket(std::addressof(__ctx), __endpoint.protocol())
|
|
{ bind(__endpoint); }
|
|
|
|
basic_socket(io_context& __ctx, const protocol_type& __protocol,
|
|
const native_handle_type& __native_socket)
|
|
: __base(__ctx)
|
|
{ assign(__protocol, __native_socket); }
|
|
|
|
basic_socket(const basic_socket&) = delete;
|
|
|
|
basic_socket(basic_socket&& __rhs) = default;
|
|
|
|
template<typename _OtherProtocol, typename _Requires
|
|
= _Require<is_convertible<_OtherProtocol, _Protocol>>>
|
|
basic_socket(basic_socket<_OtherProtocol>&& __rhs)
|
|
: __base(std::move(__rhs)) { }
|
|
|
|
~basic_socket() = default;
|
|
|
|
basic_socket& operator=(const basic_socket&) = delete;
|
|
|
|
basic_socket& operator=(basic_socket&& __rhs) = default;
|
|
|
|
template<typename _OtherProtocol>
|
|
enable_if_t<is_convertible<_OtherProtocol, _Protocol>::value,
|
|
basic_socket&>
|
|
operator=(basic_socket<_OtherProtocol>&& __rhs)
|
|
{ return *this = basic_socket{std::move(__rhs)}; }
|
|
};
|
|
|
|
template<typename _Protocol>
|
|
class basic_datagram_socket : public basic_socket<_Protocol>
|
|
{
|
|
using __base = basic_socket<_Protocol>;
|
|
|
|
public:
|
|
// types:
|
|
|
|
typedef int native_handle_type;
|
|
typedef _Protocol protocol_type;
|
|
typedef typename protocol_type::endpoint endpoint_type;
|
|
|
|
// construct / copy / destroy:
|
|
|
|
explicit
|
|
basic_datagram_socket(io_context& __ctx) : __base(__ctx) { }
|
|
|
|
basic_datagram_socket(io_context& __ctx, const protocol_type& __protocol)
|
|
: __base(__ctx, __protocol) { }
|
|
|
|
basic_datagram_socket(io_context& __ctx, const endpoint_type& __endpoint)
|
|
: __base(__ctx, __endpoint) { }
|
|
|
|
basic_datagram_socket(io_context& __ctx, const protocol_type& __protocol,
|
|
const native_handle_type& __native_socket)
|
|
: __base(__ctx, __protocol, __native_socket) { }
|
|
|
|
basic_datagram_socket(const basic_datagram_socket&) = delete;
|
|
|
|
basic_datagram_socket(basic_datagram_socket&& __rhs) = default;
|
|
|
|
template<typename _OtherProtocol, typename _Requires
|
|
= _Require<is_convertible<_OtherProtocol, _Protocol>>>
|
|
basic_datagram_socket(basic_datagram_socket<_OtherProtocol>&& __rhs)
|
|
: __base(std::move(__rhs)) { }
|
|
|
|
~basic_datagram_socket() = default;
|
|
|
|
basic_datagram_socket& operator=(const basic_datagram_socket&) = delete;
|
|
|
|
basic_datagram_socket& operator=(basic_datagram_socket&& __rhs) = default;
|
|
|
|
template<typename _OtherProtocol>
|
|
enable_if_t<is_convertible<_OtherProtocol, _Protocol>::value,
|
|
basic_datagram_socket&>
|
|
operator=(basic_datagram_socket<_OtherProtocol>&& __rhs)
|
|
{
|
|
__base::operator=(std::move(__rhs));
|
|
return *this;
|
|
}
|
|
|
|
// basic_datagram_socket operations:
|
|
|
|
template<typename _MutableBufferSequence>
|
|
size_t
|
|
receive(const _MutableBufferSequence& __buffers)
|
|
{
|
|
return receive(__buffers, socket_base::message_flags(),
|
|
__throw_on_error{"basic_datagram_socket::receive"});
|
|
}
|
|
|
|
template<typename _MutableBufferSequence>
|
|
size_t
|
|
receive(const _MutableBufferSequence& __buffers, error_code& __ec)
|
|
{ return receive(__buffers, socket_base::message_flags(), __ec); }
|
|
|
|
template<typename _MutableBufferSequence>
|
|
size_t
|
|
receive(const _MutableBufferSequence& __buffers,
|
|
socket_base::message_flags __flags)
|
|
{
|
|
return receive(__buffers, __flags,
|
|
__throw_on_error{"basic_datagram_socket::receive"});
|
|
}
|
|
|
|
template<typename _MutableBufferSequence>
|
|
size_t
|
|
receive(const _MutableBufferSequence& __buffers,
|
|
socket_base::message_flags __flags, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers);
|
|
ssize_t __result = ::recvmsg(this->native_handle(), &__msg,
|
|
static_cast<int>(__flags));
|
|
if (__result == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
return 0;
|
|
}
|
|
__ec.clear();
|
|
return __result;
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
template<typename _MutableBufferSequence, typename _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_receive(const _MutableBufferSequence& __buffers,
|
|
_CompletionToken&& __token)
|
|
{
|
|
return async_receive(__buffers, socket_base::message_flags(),
|
|
std::forward<_CompletionToken>(__token));
|
|
}
|
|
|
|
template<typename _MutableBufferSequence, typename _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_receive(const _MutableBufferSequence& __buffers,
|
|
socket_base::message_flags __flags,
|
|
_CompletionToken&& __token)
|
|
{
|
|
async_completion<_CompletionToken, void(error_code, size_t)>
|
|
__init{__token};
|
|
|
|
this->get_executor().context().async_wait(this->native_handle(),
|
|
socket_base::wait_read,
|
|
[__h = std::move(__init.completion_handler),
|
|
&__buffers, __flags = static_cast<int>(__flags),
|
|
__fd = this->native_handle()]
|
|
(error_code __ec) mutable {
|
|
if (__ec)
|
|
{
|
|
__h(__ec);
|
|
return;
|
|
}
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers);
|
|
ssize_t __result = ::recvmsg(__fd, &__msg, __flags);
|
|
if (__result == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
__result = 0;
|
|
}
|
|
else
|
|
__ec.clear();
|
|
__h(__ec, __result);
|
|
#else
|
|
__h(std::make_error_code(errc::operation_not_supported), 0);
|
|
#endif
|
|
});
|
|
return __init.result.get();
|
|
}
|
|
|
|
template<typename _MutableBufferSequence>
|
|
size_t
|
|
receive_from(const _MutableBufferSequence& __buffers,
|
|
endpoint_type& __sender)
|
|
{
|
|
return receive_from(__buffers, __sender,
|
|
socket_base::message_flags(),
|
|
__throw_on_error{
|
|
"basic_datagram_socket::receive_from"});
|
|
}
|
|
|
|
template<typename _MutableBufferSequence>
|
|
size_t
|
|
receive_from(const _MutableBufferSequence& __buffers,
|
|
endpoint_type& __sender, error_code& __ec)
|
|
{
|
|
return receive_from(__buffers, __sender,
|
|
socket_base::message_flags(), __ec);
|
|
}
|
|
|
|
template<typename _MutableBufferSequence>
|
|
size_t
|
|
receive_from(const _MutableBufferSequence& __buffers,
|
|
endpoint_type& __sender,
|
|
socket_base::message_flags __flags)
|
|
{
|
|
return receive_from(__buffers, __sender, __flags,
|
|
__throw_on_error{
|
|
"basic_datagram_socket::receive_from"});
|
|
}
|
|
|
|
template<typename _MutableBufferSequence>
|
|
size_t
|
|
receive_from(const _MutableBufferSequence& __buffers,
|
|
endpoint_type& __sender,
|
|
socket_base::message_flags __flags,
|
|
error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers, __sender);
|
|
ssize_t __result = ::recvmsg(this->native_handle(), &__msg,
|
|
static_cast<int>(__flags));
|
|
if (__result == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
return 0;
|
|
}
|
|
__ec.clear();
|
|
__sender.resize(__msg.msg_namelen);
|
|
return __result;
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
template<typename _MutableBufferSequence, typename _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_receive_from(const _MutableBufferSequence& __buffers,
|
|
endpoint_type& __sender,
|
|
_CompletionToken&& __token)
|
|
{
|
|
return async_receive_from(__buffers, __sender,
|
|
socket_base::message_flags(),
|
|
std::forward<_CompletionToken>(__token));
|
|
}
|
|
|
|
template<typename _MutableBufferSequence, typename _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_receive_from(const _MutableBufferSequence& __buffers,
|
|
endpoint_type& __sender,
|
|
socket_base::message_flags __flags,
|
|
_CompletionToken&& __token)
|
|
{
|
|
async_completion<_CompletionToken, void(error_code, size_t)>
|
|
__init{__token};
|
|
|
|
this->get_executor().context().async_wait( this->native_handle(),
|
|
socket_base::wait_read,
|
|
[__h = std::move(__init.completion_handler),
|
|
&__buffers, __flags = static_cast<int>(__flags),
|
|
__sender = std::move(__sender),
|
|
__fd = this->native_handle()]
|
|
(error_code __ec) mutable {
|
|
if (__ec)
|
|
{
|
|
__h(__ec);
|
|
return;
|
|
}
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers, __sender);
|
|
ssize_t __result = ::recvmsg(__fd, &__msg, __flags);
|
|
if (__result == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
__result = 0;
|
|
}
|
|
else
|
|
{
|
|
__ec.clear();
|
|
__sender.resize(__msg.msg_namelen);
|
|
}
|
|
__h(__ec, __result);
|
|
#else
|
|
__h(std::make_error_code(errc::operation_not_supported), 0);
|
|
#endif
|
|
});
|
|
return __init.result.get();
|
|
}
|
|
|
|
template<typename _ConstBufferSequence>
|
|
size_t
|
|
send(const _ConstBufferSequence& __buffers)
|
|
{
|
|
return send(__buffers, socket_base::message_flags(),
|
|
__throw_on_error{"basic_datagram_socket::send"});
|
|
}
|
|
|
|
template<typename _ConstBufferSequence>
|
|
size_t
|
|
send(const _ConstBufferSequence& __buffers, error_code& __ec)
|
|
{ return send(__buffers, socket_base::message_flags(), __ec); }
|
|
|
|
template<typename _ConstBufferSequence>
|
|
size_t
|
|
send(const _ConstBufferSequence& __buffers,
|
|
socket_base::message_flags __flags)
|
|
{
|
|
return send(__buffers, __flags,
|
|
__throw_on_error{"basic_datagram_socket::send"});
|
|
}
|
|
|
|
template<typename _ConstBufferSequence>
|
|
size_t
|
|
send(const _ConstBufferSequence& __buffers,
|
|
socket_base::message_flags __flags, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers);
|
|
ssize_t __result = ::sendmsg(this->native_handle(), &__msg,
|
|
static_cast<int>(__flags));
|
|
if (__result == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
return 0;
|
|
}
|
|
__ec.clear();
|
|
return __result;
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
template<typename _ConstBufferSequence, typename _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_send(const _ConstBufferSequence& __buffers,
|
|
_CompletionToken&& __token)
|
|
{
|
|
return async_send(__buffers, socket_base::message_flags(),
|
|
std::forward<_CompletionToken>(__token));
|
|
}
|
|
|
|
template<typename _ConstBufferSequence, typename _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_send(const _ConstBufferSequence& __buffers,
|
|
socket_base::message_flags __flags,
|
|
_CompletionToken&& __token)
|
|
{
|
|
async_completion<_CompletionToken, void(error_code, size_t)>
|
|
__init{__token};
|
|
|
|
this->get_executor().context().async_wait( this->native_handle(),
|
|
socket_base::wait_write,
|
|
[__h = std::move(__init.completion_handler),
|
|
&__buffers, __flags = static_cast<int>(__flags),
|
|
__fd = this->native_handle()]
|
|
(error_code __ec) mutable {
|
|
if (__ec)
|
|
{
|
|
__h(__ec);
|
|
return;
|
|
}
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers);
|
|
ssize_t __result = ::sendmsg(__fd, &__msg, __flags);
|
|
if (__result == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
__result = 0;
|
|
}
|
|
else
|
|
__ec.clear();
|
|
__h(__ec, __result);
|
|
#else
|
|
__h(std::make_error_code(errc::operation_not_supported), 0);
|
|
#endif
|
|
});
|
|
return __init.result.get();
|
|
}
|
|
|
|
template<typename _ConstBufferSequence>
|
|
size_t
|
|
send_to(const _ConstBufferSequence& __buffers,
|
|
const endpoint_type& __recipient)
|
|
{
|
|
return send_to(__buffers, __recipient,
|
|
socket_base::message_flags(),
|
|
__throw_on_error{"basic_datagram_socket::send_to"});
|
|
}
|
|
|
|
template<typename _ConstBufferSequence>
|
|
size_t
|
|
send_to(const _ConstBufferSequence& __buffers,
|
|
const endpoint_type& __recipient, error_code& __ec)
|
|
{
|
|
return send_to(__buffers, __recipient,
|
|
socket_base::message_flags(), __ec);
|
|
}
|
|
|
|
template<typename _ConstBufferSequence>
|
|
size_t
|
|
send_to(const _ConstBufferSequence& __buffers,
|
|
const endpoint_type& __recipient,
|
|
socket_base::message_flags __flags)
|
|
{
|
|
return send_to(__buffers, __recipient, __flags,
|
|
__throw_on_error{"basic_datagram_socket::send_to"});
|
|
}
|
|
|
|
template<typename _ConstBufferSequence>
|
|
size_t
|
|
send_to(const _ConstBufferSequence& __buffers,
|
|
const endpoint_type& __recipient,
|
|
socket_base::message_flags __flags, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers, __recipient);
|
|
ssize_t __result = ::sendmsg(this->native_handle(), &__msg,
|
|
static_cast<int>(__flags));
|
|
if (__result == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
return 0;
|
|
}
|
|
__ec.clear();
|
|
__recipient.resize(__msg.msg_namelen);
|
|
return __result;
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
template<typename _ConstBufferSequence, typename _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_send_to(const _ConstBufferSequence& __buffers,
|
|
const endpoint_type& __recipient,
|
|
_CompletionToken&& __token)
|
|
{
|
|
return async_send_to(__buffers, __recipient,
|
|
socket_base::message_flags(),
|
|
std::forward<_CompletionToken>(__token));
|
|
}
|
|
|
|
template<typename _ConstBufferSequence, typename _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_send_to(const _ConstBufferSequence& __buffers,
|
|
const endpoint_type& __recipient,
|
|
socket_base::message_flags __flags,
|
|
_CompletionToken&& __token)
|
|
{
|
|
async_completion<_CompletionToken, void(error_code, size_t)>
|
|
__init{__token};
|
|
|
|
this->get_executor().context().async_wait( this->native_handle(),
|
|
socket_base::wait_write,
|
|
[__h = std::move(__init.completion_handler),
|
|
&__buffers, __flags = static_cast<int>(__flags),
|
|
__recipient = std::move(__recipient),
|
|
__fd = this->native_handle()]
|
|
(error_code __ec) mutable {
|
|
if (__ec)
|
|
{
|
|
__h(__ec);
|
|
return;
|
|
}
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers, __recipient);
|
|
ssize_t __result = ::sendmsg(__fd, &__msg, __flags);
|
|
if (__result == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
__result = 0;
|
|
}
|
|
else
|
|
{
|
|
__ec.clear();
|
|
__recipient.resize(__msg.msg_namelen);
|
|
}
|
|
__h(__ec, __result);
|
|
#else
|
|
__h(std::make_error_code(errc::operation_not_supported), 0);
|
|
#endif
|
|
});
|
|
return __init.result.get();
|
|
}
|
|
};
|
|
|
|
template<typename _Protocol>
|
|
class basic_stream_socket : public basic_socket<_Protocol>
|
|
{
|
|
using __base = basic_socket<_Protocol>;
|
|
|
|
public:
|
|
// types:
|
|
|
|
typedef int native_handle_type;
|
|
typedef _Protocol protocol_type;
|
|
typedef typename protocol_type::endpoint endpoint_type;
|
|
|
|
// construct / copy / destroy:
|
|
|
|
explicit
|
|
basic_stream_socket(io_context& __ctx) : __base(__ctx) { }
|
|
|
|
basic_stream_socket(io_context& __ctx, const protocol_type& __protocol)
|
|
: __base(__ctx, __protocol) { }
|
|
|
|
basic_stream_socket(io_context& __ctx, const endpoint_type& __endpoint)
|
|
: __base(__ctx, __endpoint) { }
|
|
|
|
basic_stream_socket(io_context& __ctx, const protocol_type& __protocol,
|
|
const native_handle_type& __native_socket)
|
|
: __base(__ctx, __protocol, __native_socket) { }
|
|
|
|
basic_stream_socket(const basic_stream_socket&) = delete;
|
|
|
|
basic_stream_socket(basic_stream_socket&& __rhs) = default;
|
|
|
|
template<typename _OtherProtocol, typename _Requires
|
|
= _Require<is_convertible<_OtherProtocol, _Protocol>>>
|
|
basic_stream_socket(basic_stream_socket<_OtherProtocol>&& __rhs)
|
|
: __base(std::move(__rhs)) { }
|
|
|
|
~basic_stream_socket() = default;
|
|
|
|
basic_stream_socket& operator=(const basic_stream_socket&) = delete;
|
|
|
|
basic_stream_socket& operator=(basic_stream_socket&& __rhs) = default;
|
|
|
|
template<class _OtherProtocol>
|
|
enable_if_t<is_convertible<_OtherProtocol, _Protocol>::value,
|
|
basic_stream_socket&>
|
|
operator=(basic_stream_socket<_OtherProtocol>&& __rhs)
|
|
{
|
|
__base::operator=(std::move(__rhs));
|
|
return *this;
|
|
}
|
|
|
|
// basic_stream_socket operations:
|
|
|
|
template<class _MutableBufferSequence>
|
|
size_t
|
|
receive(const _MutableBufferSequence& __buffers)
|
|
{
|
|
return receive(__buffers, socket_base::message_flags(),
|
|
__throw_on_error{"basic_stream_socket::receive"});
|
|
}
|
|
|
|
template<class _MutableBufferSequence>
|
|
size_t
|
|
receive(const _MutableBufferSequence& __buffers, error_code& __ec)
|
|
{ return receive(__buffers, socket_base::message_flags(), __ec); }
|
|
|
|
template<class _MutableBufferSequence>
|
|
size_t
|
|
receive(const _MutableBufferSequence& __buffers,
|
|
socket_base::message_flags __flags)
|
|
{
|
|
return receive(__buffers, __flags,
|
|
__throw_on_error{"basic_stream_socket::receive"});
|
|
}
|
|
|
|
template<class _MutableBufferSequence>
|
|
size_t
|
|
receive(const _MutableBufferSequence& __buffers,
|
|
socket_base::message_flags __flags, error_code& __ec)
|
|
{
|
|
if (__buffer_empty(__buffers))
|
|
{
|
|
__ec.clear();
|
|
return 0;
|
|
}
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers);
|
|
ssize_t __result = ::recvmsg(this->native_handle(), &__msg,
|
|
static_cast<int>(__flags));
|
|
if (__result >= 0)
|
|
{
|
|
__ec.clear();
|
|
return __result;
|
|
}
|
|
__ec.assign(errno, generic_category());
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
template<class _MutableBufferSequence, class _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_receive(const _MutableBufferSequence& __buffers,
|
|
_CompletionToken&& __token)
|
|
{
|
|
return async_receive(__buffers, socket_base::message_flags(),
|
|
std::forward<_CompletionToken>(__token));
|
|
}
|
|
|
|
template<class _MutableBufferSequence, class _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_receive(const _MutableBufferSequence& __buffers,
|
|
socket_base::message_flags __flags,
|
|
_CompletionToken&& __token)
|
|
{
|
|
async_completion<_CompletionToken, void(error_code, size_t)>
|
|
__init{__token};
|
|
|
|
if (__buffer_empty(__buffers))
|
|
{
|
|
auto __ex = net::get_associated_executor(
|
|
__init.completion_handler, this->get_executor());
|
|
auto __a = get_associated_allocator(
|
|
__init.completion_handler, std::allocator<void>());
|
|
__ex.post(
|
|
[__h=std::move(__init.completion_handler)] () mutable
|
|
{ __h(error_code{}, 0); }, __a);
|
|
return __init.result.get();
|
|
}
|
|
|
|
this->get_executor().context().async_wait(this->native_handle(),
|
|
socket_base::wait_read,
|
|
[__h = std::move(__init.completion_handler),
|
|
&__buffers, __flags = static_cast<int>(__flags),
|
|
__fd = this->native_handle()]
|
|
(error_code __ec) mutable {
|
|
if (__ec)
|
|
{
|
|
__h(__ec);
|
|
return;
|
|
}
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers);
|
|
ssize_t __result = ::recvmsg(__fd, &__msg, __flags);
|
|
if (__result == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
__result = 0;
|
|
}
|
|
else
|
|
__ec.clear();
|
|
__h(__ec, __result);
|
|
#else
|
|
__h(std::make_error_code(errc::operation_not_supported), 0);
|
|
#endif
|
|
});
|
|
return __init.result.get();
|
|
}
|
|
|
|
template<class _ConstBufferSequence>
|
|
size_t
|
|
send(const _ConstBufferSequence& __buffers)
|
|
{
|
|
return send(__buffers, socket_base::message_flags(),
|
|
__throw_on_error{"basic_stream_socket::send"});
|
|
}
|
|
|
|
template<class _ConstBufferSequence>
|
|
size_t
|
|
send(const _ConstBufferSequence& __buffers, error_code& __ec)
|
|
{ return send(__buffers, socket_base::message_flags(), __ec); }
|
|
|
|
template<class _ConstBufferSequence>
|
|
size_t
|
|
send(const _ConstBufferSequence& __buffers,
|
|
socket_base::message_flags __flags)
|
|
{
|
|
return send(__buffers, socket_base::message_flags(),
|
|
__throw_on_error{"basic_stream_socket::send"});
|
|
}
|
|
|
|
template<class _ConstBufferSequence>
|
|
size_t
|
|
send(const _ConstBufferSequence& __buffers,
|
|
socket_base::message_flags __flags, error_code& __ec)
|
|
{
|
|
if (__buffer_empty(__buffers))
|
|
{
|
|
__ec.clear();
|
|
return 0;
|
|
}
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers);
|
|
ssize_t __result = ::sendmsg(this->native_handle(), &__msg,
|
|
static_cast<int>(__flags));
|
|
if (__result >= 0)
|
|
{
|
|
__ec.clear();
|
|
return __result;
|
|
}
|
|
__ec.assign(errno, generic_category());
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
template<class _ConstBufferSequence, class _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_send(const _ConstBufferSequence& __buffers,
|
|
_CompletionToken&& __token)
|
|
{
|
|
return async_send(__buffers, socket_base::message_flags(),
|
|
std::forward<_CompletionToken>(__token));
|
|
}
|
|
|
|
template<class _ConstBufferSequence, class _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_send(const _ConstBufferSequence& __buffers,
|
|
socket_base::message_flags __flags,
|
|
_CompletionToken&& __token)
|
|
{
|
|
async_completion<_CompletionToken, void(error_code, size_t)>
|
|
__init{__token};
|
|
|
|
if (__buffer_empty(__buffers))
|
|
{
|
|
auto __ex = net::get_associated_executor(
|
|
__init.completion_handler, this->get_executor());
|
|
auto __a = get_associated_allocator(
|
|
__init.completion_handler, std::allocator<void>());
|
|
__ex.post(
|
|
[__h=std::move(__init.completion_handler)] () mutable
|
|
{ __h(error_code{}, 0); }, __a);
|
|
return __init.result.get();
|
|
}
|
|
|
|
this->get_executor().context().async_wait(this->native_handle(),
|
|
socket_base::wait_write,
|
|
[__h = std::move(__init.completion_handler),
|
|
&__buffers, __flags = static_cast<int>(__flags),
|
|
__fd = this->native_handle()]
|
|
(error_code __ec) mutable {
|
|
if (__ec)
|
|
{
|
|
__h(__ec);
|
|
return;
|
|
}
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
socket_base::__msg_hdr __msg(__buffers);
|
|
ssize_t __result = ::sendmsg(__fd, &__msg, __flags);
|
|
if (__result == -1)
|
|
{
|
|
__ec.assign(errno, generic_category());
|
|
__result = 0;
|
|
}
|
|
else
|
|
__ec.clear();
|
|
__h(__ec, __result);
|
|
#else
|
|
__h(std::make_error_code(errc::operation_not_supported), 0);
|
|
#endif
|
|
});
|
|
return __init.result.get();
|
|
}
|
|
|
|
template<class _MutableBufferSequence>
|
|
size_t
|
|
read_some(const _MutableBufferSequence& __buffers)
|
|
{
|
|
return receive(__buffers,
|
|
__throw_on_error{"basic_stream_socket::read_some"});
|
|
}
|
|
|
|
template<class _MutableBufferSequence>
|
|
size_t
|
|
read_some(const _MutableBufferSequence& __buffers, error_code& __ec)
|
|
{ return receive(__buffers, __ec); }
|
|
|
|
template<class _MutableBufferSequence, class _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_read_some(const _MutableBufferSequence& __buffers,
|
|
_CompletionToken&& __token)
|
|
{
|
|
return async_receive(__buffers,
|
|
std::forward<_CompletionToken>(__token));
|
|
}
|
|
|
|
template<class _ConstBufferSequence>
|
|
size_t
|
|
write_some(const _ConstBufferSequence& __buffers)
|
|
{
|
|
return send(__buffers,
|
|
__throw_on_error{"basic_stream_socket:write_some"});
|
|
}
|
|
|
|
template<class _ConstBufferSequence>
|
|
size_t
|
|
write_some(const _ConstBufferSequence& __buffers, error_code& __ec)
|
|
{ return send(__buffers, __ec); }
|
|
|
|
template<class _ConstBufferSequence, class _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, size_t)>
|
|
async_write_some(const _ConstBufferSequence& __buffers,
|
|
_CompletionToken&& __token)
|
|
{
|
|
return async_send(__buffers,
|
|
std::forward<_CompletionToken>(__token));
|
|
}
|
|
};
|
|
|
|
template<typename _AcceptableProtocol>
|
|
class basic_socket_acceptor
|
|
: public socket_base, private __basic_socket_impl<_AcceptableProtocol>
|
|
{
|
|
using __base = __basic_socket_impl<_AcceptableProtocol>;
|
|
|
|
public:
|
|
// types:
|
|
|
|
typedef io_context::executor_type executor_type;
|
|
typedef int native_handle_type;
|
|
typedef _AcceptableProtocol protocol_type;
|
|
typedef typename protocol_type::endpoint endpoint_type;
|
|
typedef typename protocol_type::socket socket_type;
|
|
|
|
// construct / copy / destroy:
|
|
|
|
explicit
|
|
basic_socket_acceptor(io_context& __ctx)
|
|
: __base(__ctx), _M_protocol(endpoint_type{}.protocol()) { }
|
|
|
|
basic_socket_acceptor(io_context& __ctx,
|
|
const protocol_type& __protocol)
|
|
: __base(__ctx), _M_protocol(__protocol)
|
|
{ open(__protocol); }
|
|
|
|
basic_socket_acceptor(io_context& __ctx, const endpoint_type& __endpoint,
|
|
bool __reuse_addr = true)
|
|
: basic_socket_acceptor(__ctx, __endpoint.protocol())
|
|
{
|
|
if (__reuse_addr)
|
|
set_option(reuse_address(true));
|
|
bind(__endpoint);
|
|
listen();
|
|
}
|
|
|
|
basic_socket_acceptor(io_context& __ctx, const protocol_type& __protocol,
|
|
const native_handle_type& __native_acceptor)
|
|
: basic_socket_acceptor(__ctx, __protocol)
|
|
{ assign(__protocol, __native_acceptor); }
|
|
|
|
basic_socket_acceptor(const basic_socket_acceptor&) = delete;
|
|
|
|
basic_socket_acceptor(basic_socket_acceptor&&) = default;
|
|
|
|
template<typename _OtherProtocol, typename _Requires
|
|
= _Require<is_convertible<_OtherProtocol, protocol_type>>>
|
|
basic_socket_acceptor(basic_socket_acceptor<_OtherProtocol>&& __rhs)
|
|
: __base(std::move(__rhs)) { }
|
|
|
|
~basic_socket_acceptor() = default;
|
|
|
|
basic_socket_acceptor& operator=(const basic_socket_acceptor&) = delete;
|
|
|
|
basic_socket_acceptor& operator=(basic_socket_acceptor&&) = default;
|
|
|
|
template<class _OtherProtocol>
|
|
enable_if_t<is_convertible<_OtherProtocol, protocol_type>::value,
|
|
basic_socket_acceptor&>
|
|
operator=(basic_socket_acceptor<_OtherProtocol>&& __rhs)
|
|
{
|
|
__base::operator=(std::move(__rhs));
|
|
return *this;
|
|
}
|
|
|
|
// basic_socket_acceptor operations:
|
|
|
|
executor_type get_executor() noexcept { return __base::get_executor(); }
|
|
|
|
native_handle_type
|
|
native_handle() noexcept { return __base::native_handle(); }
|
|
|
|
void
|
|
open(const protocol_type& __protocol = protocol_type())
|
|
{ open(__protocol, __throw_on_error{"basic_socket_acceptor::open"}); }
|
|
|
|
void
|
|
open(const protocol_type& __protocol, error_code& __ec)
|
|
{ __base::open(__protocol, __ec); }
|
|
|
|
void
|
|
assign(const protocol_type& __protocol,
|
|
const native_handle_type& __native_acceptor)
|
|
{
|
|
assign(__protocol, __native_acceptor,
|
|
__throw_on_error{"basic_socket_acceptor::assign"});
|
|
}
|
|
|
|
void
|
|
assign(const protocol_type& __protocol,
|
|
const native_handle_type& __native_acceptor,
|
|
error_code& __ec)
|
|
{ __base::assign(__protocol, __native_acceptor, __ec); }
|
|
|
|
bool
|
|
is_open() const noexcept { return __base::is_open(); }
|
|
|
|
void
|
|
close() { close(__throw_on_error{"basic_socket_acceptor::close"}); }
|
|
|
|
void
|
|
close(error_code& __ec) { __base::_close(__ec); }
|
|
|
|
void
|
|
cancel() { cancel(__throw_on_error{"basic_socket_acceptor::cancel"}); }
|
|
|
|
void
|
|
cancel(error_code& __ec) { __base::cancel(__ec); }
|
|
|
|
template<typename _SettableSocketOption>
|
|
void
|
|
set_option(const _SettableSocketOption& __option)
|
|
{
|
|
set_option(__option,
|
|
__throw_on_error{"basic_socket_acceptor::set_option"});
|
|
}
|
|
|
|
template<typename _SettableSocketOption>
|
|
void
|
|
set_option(const _SettableSocketOption& __option, error_code& __ec)
|
|
{ __base::set_option(__option, __ec); }
|
|
|
|
template<typename _GettableSocketOption>
|
|
void
|
|
get_option(_GettableSocketOption& __option) const
|
|
{
|
|
get_option(__option,
|
|
__throw_on_error{"basic_socket_acceptor::get_option"});
|
|
}
|
|
|
|
template<typename _GettableSocketOption>
|
|
void
|
|
get_option(_GettableSocketOption& __option, error_code& __ec) const
|
|
{ __base::get_option(__option, __ec); }
|
|
|
|
template<typename _IoControlCommand>
|
|
void
|
|
io_control(_IoControlCommand& __command)
|
|
{
|
|
io_control(__command,
|
|
__throw_on_error{"basic_socket_acceptor::io_control"});
|
|
}
|
|
|
|
template<typename _IoControlCommand>
|
|
void
|
|
io_control(_IoControlCommand& __command, error_code& __ec)
|
|
{ __base::io_control(__command, __ec); }
|
|
|
|
void
|
|
non_blocking(bool __mode)
|
|
{
|
|
non_blocking(__mode,
|
|
__throw_on_error{"basic_socket_acceptor::non_blocking"});
|
|
}
|
|
|
|
void
|
|
non_blocking(bool __mode, error_code& __ec)
|
|
{ __base::non_blocking(__mode, __ec); }
|
|
|
|
bool non_blocking() const { return __base::non_blocking(); }
|
|
|
|
void
|
|
native_non_blocking(bool __mode)
|
|
{
|
|
native_non_blocking(__mode, __throw_on_error{
|
|
"basic_socket_acceptor::native_non_blocking"});
|
|
}
|
|
|
|
void
|
|
native_non_blocking(bool __mode, error_code& __ec)
|
|
{ __base::native_non_blocking(__mode, __ec); }
|
|
|
|
bool
|
|
native_non_blocking() const
|
|
{ return __base::native_non_blocking(); }
|
|
|
|
void
|
|
bind(const endpoint_type& __endpoint)
|
|
{
|
|
return bind(__endpoint,
|
|
__throw_on_error{"basic_socket_acceptor::bind"});
|
|
}
|
|
|
|
void
|
|
bind(const endpoint_type& __endpoint, error_code& __ec)
|
|
{ __base::bind(__endpoint, __ec); }
|
|
|
|
void
|
|
listen(int __backlog = max_listen_connections)
|
|
{
|
|
return listen(__backlog,
|
|
__throw_on_error{"basic_socket_acceptor::listen"});
|
|
}
|
|
|
|
void
|
|
listen(int __backlog, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
if (::listen(native_handle(), __backlog) == -1)
|
|
__ec.assign(errno, generic_category());
|
|
else
|
|
__ec.clear();
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
}
|
|
|
|
endpoint_type
|
|
local_endpoint() const
|
|
{
|
|
return local_endpoint(
|
|
__throw_on_error{"basic_socket_acceptor::local_endpoint"});
|
|
}
|
|
|
|
endpoint_type
|
|
local_endpoint(error_code& __ec) const
|
|
{ return __base::local_endpoint(__ec); }
|
|
|
|
void
|
|
enable_connection_aborted(bool __mode)
|
|
{ __base::_M_bits.enable_connection_aborted = __mode; }
|
|
|
|
bool
|
|
enable_connection_aborted() const
|
|
{ return __base::_M_bits.enable_connection_aborted; }
|
|
|
|
socket_type
|
|
accept()
|
|
{ return accept(__throw_on_error{"basic_socket_acceptor::accept"}); }
|
|
|
|
socket_type
|
|
accept(error_code& __ec)
|
|
{ return accept(get_executor().context(), __ec); }
|
|
|
|
socket_type accept(io_context& __ctx)
|
|
{
|
|
return accept(__ctx,
|
|
__throw_on_error{"basic_socket_acceptor::accept"});
|
|
}
|
|
|
|
socket_type
|
|
accept(io_context& __ctx, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
do
|
|
{
|
|
int __h = ::accept(native_handle(), nullptr, 0);
|
|
if (__h != -1)
|
|
{
|
|
__ec.clear();
|
|
return socket_type{__ctx, _M_protocol, __h};
|
|
}
|
|
} while (errno == ECONNABORTED && enable_connection_aborted());
|
|
__ec.assign(errno, generic_category());
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
return socket_type{__ctx};
|
|
}
|
|
|
|
template<class _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, socket_type)>
|
|
async_accept(_CompletionToken&& __token)
|
|
{
|
|
return async_accept(get_executor().context(),
|
|
std::forward<_CompletionToken>(__token));
|
|
}
|
|
|
|
template<class _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, socket_type)>
|
|
async_accept(io_context& __ctx, _CompletionToken&& __token)
|
|
{
|
|
async_completion<_CompletionToken, void(error_code, socket_type)>
|
|
__init{__token};
|
|
|
|
__ctx.get_executor().context().async_wait(native_handle(),
|
|
socket_base::wait_read,
|
|
[__h = std::move(__init.completion_handler),
|
|
__connabort = enable_connection_aborted(),
|
|
__fd = native_handle(),
|
|
__protocol = _M_protocol,
|
|
&__ctx
|
|
]
|
|
(error_code __ec) mutable {
|
|
if (__ec)
|
|
{
|
|
__h(__ec, socket_type(__ctx));
|
|
return;
|
|
}
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
do
|
|
{
|
|
int __newfd = ::accept(__fd, nullptr, 0);
|
|
if (__newfd != -1)
|
|
{
|
|
__ec.clear();
|
|
__h(__ec, socket_type{__ctx, __protocol, __newfd});
|
|
return;
|
|
}
|
|
} while (errno == ECONNABORTED && __connabort);
|
|
__ec.assign(errno, generic_category());
|
|
__h(__ec, socket_type(__ctx));
|
|
#else
|
|
__h(std::make_error_code(errc::operation_not_supported), 0);
|
|
#endif
|
|
});
|
|
return __init.result.get();
|
|
}
|
|
|
|
socket_type
|
|
accept(endpoint_type& __endpoint)
|
|
{
|
|
return accept(get_executor().context(), __endpoint,
|
|
__throw_on_error{"basic_socket_acceptor::accept"});
|
|
}
|
|
|
|
socket_type
|
|
accept(endpoint_type& __endpoint, error_code& __ec)
|
|
{ return accept(get_executor().context(), __endpoint, __ec); }
|
|
|
|
socket_type
|
|
accept(io_context& __ctx, endpoint_type& __endpoint)
|
|
{
|
|
return accept(__ctx, __endpoint,
|
|
__throw_on_error{"basic_socket_acceptor::accept"});
|
|
}
|
|
|
|
socket_type
|
|
accept(io_context& __ctx, endpoint_type& __endpoint, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
do
|
|
{
|
|
socklen_t __len = __endpoint.capacity();
|
|
int __h = ::accept(native_handle(), (sockaddr*)__endpoint.data(),
|
|
&__len);
|
|
if (__h != -1)
|
|
{
|
|
__endpoint.resize(__len);
|
|
return socket_type{__ctx, _M_protocol, __h};
|
|
}
|
|
} while (errno == ECONNABORTED && enable_connection_aborted());
|
|
__ec.assign(errno, generic_category());
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
return socket_type{__ctx};
|
|
}
|
|
|
|
template<class _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, socket_type)>
|
|
async_accept(endpoint_type& __endpoint,
|
|
_CompletionToken&& __token)
|
|
{
|
|
return async_accept(get_executor().context(), __endpoint,
|
|
std::forward<_CompletionToken>(__token));
|
|
}
|
|
|
|
template<class _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code, socket_type)>
|
|
async_accept(io_context& __ctx, endpoint_type& __endpoint,
|
|
_CompletionToken&& __token)
|
|
{
|
|
async_completion<_CompletionToken, void(error_code, socket_type)>
|
|
__init{__token};
|
|
|
|
__ctx.get_executor().context().async_wait(native_handle(),
|
|
socket_base::wait_read,
|
|
[__h = std::move(__init.completion_handler),
|
|
__ep = std::move(__endpoint),
|
|
__connabort = enable_connection_aborted(),
|
|
__fd = native_handle(),
|
|
&__ctx
|
|
]
|
|
(error_code __ec) mutable {
|
|
if (__ec)
|
|
{
|
|
__h(__ec, socket_type(__ctx));
|
|
return;
|
|
}
|
|
#ifdef _GLIBCXX_HAVE_SYS_SOCKET_H
|
|
do
|
|
{
|
|
socklen_t __len = __ep.capacity();
|
|
int __newfd = ::accept(__fd, __ep.data, &__len);
|
|
if (__newfd != -1)
|
|
{
|
|
__ep.resize(__len);
|
|
auto __protocol = __ep.protocol();
|
|
__ec.clear();
|
|
__h(__ec, socket_type{__ctx, __protocol, __newfd});
|
|
return;
|
|
}
|
|
} while (errno == ECONNABORTED && __connabort);
|
|
__ec.assign(errno, generic_category());
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
__h(__ec, socket_type(__ctx));
|
|
});
|
|
return __init.result.get();
|
|
}
|
|
|
|
void
|
|
wait(wait_type __w)
|
|
{ wait(__w, __throw_on_error{"basic_socket_acceptor::wait"}); }
|
|
|
|
void
|
|
wait(wait_type __w, error_code& __ec)
|
|
{
|
|
#ifdef _GLIBCXX_HAVE_POLL_H
|
|
::pollfd __fds;
|
|
__fds.fd = native_handle();
|
|
__fds.events = __w; // __w | POLLIN;
|
|
if (::poll(&__fds, 1, -1) == -1)
|
|
__ec.assign(errno, generic_category());
|
|
else
|
|
__ec.clear();
|
|
#else
|
|
__ec = std::make_error_code(errc::operation_not_supported);
|
|
#endif
|
|
}
|
|
|
|
template<class _CompletionToken>
|
|
__deduced_t<_CompletionToken, void(error_code)>
|
|
async_wait(wait_type __w, _CompletionToken&& __token)
|
|
{
|
|
async_completion<_CompletionToken, void(error_code)> __init{__token};
|
|
get_executor().context().async_wait( native_handle(),
|
|
static_cast<int>(__w),
|
|
[__h = std::move(__init.completion_handler)]
|
|
(error_code __ec) mutable {
|
|
__h(__ec);
|
|
});
|
|
return __init.result.get();
|
|
}
|
|
|
|
private:
|
|
protocol_type _M_protocol;
|
|
};
|
|
|
|
// @}
|
|
|
|
/** @brief Socket streams
|
|
* @{
|
|
*/
|
|
|
|
template<typename _Protocol, typename _Clock, typename _WaitTraits>
|
|
class basic_socket_streambuf : public basic_streambuf<char>
|
|
{
|
|
public:
|
|
// types:
|
|
|
|
typedef _Protocol protocol_type;
|
|
typedef typename protocol_type::endpoint endpoint_type;
|
|
typedef _Clock clock_type;
|
|
typedef typename clock_type::time_point time_point;
|
|
typedef typename clock_type::duration duration;
|
|
typedef _WaitTraits wait_traits_type;
|
|
|
|
// construct / copy / destroy:
|
|
|
|
basic_socket_streambuf() : _M_socket(_S_ctx()) { }
|
|
|
|
explicit
|
|
basic_socket_streambuf(basic_stream_socket<protocol_type> __s)
|
|
: _M_socket(std::move(__s)) { }
|
|
|
|
basic_socket_streambuf(const basic_socket_streambuf&) = delete;
|
|
|
|
basic_socket_streambuf(basic_socket_streambuf&& __rhs); // TODO
|
|
|
|
|
|
virtual ~basic_socket_streambuf(); // TODO
|
|
|
|
basic_socket_streambuf& operator=(const basic_socket_streambuf&) = delete;
|
|
|
|
basic_socket_streambuf& operator=(basic_socket_streambuf&& __rhs); // TODO
|
|
|
|
// members:
|
|
|
|
basic_socket_streambuf* connect(const endpoint_type& __e); // TODO
|
|
|
|
template<typename... _Args>
|
|
basic_socket_streambuf* connect(_Args&&... ); // TODO
|
|
|
|
basic_socket_streambuf* close(); // TODO
|
|
|
|
basic_socket<protocol_type>& socket() { return _M_socket; }
|
|
error_code error() const { return _M_ec; }
|
|
|
|
time_point expiry() const { return _M_expiry; }
|
|
|
|
void
|
|
expires_at(const time_point& __t)
|
|
{ _M_expiry = __t; }
|
|
|
|
void
|
|
expires_after(const duration& __d)
|
|
{ expires_at(clock_type::now() + __d); }
|
|
|
|
protected:
|
|
// overridden virtual functions: // TODO
|
|
virtual int_type underflow() override;
|
|
virtual int_type pbackfail(int_type __c = traits_type::eof()) override;
|
|
virtual int_type overflow(int_type __c = traits_type::eof()) override;
|
|
virtual int sync() override;
|
|
virtual streambuf* setbuf(char_type* __s, streamsize __n) override;
|
|
|
|
private:
|
|
static io_context&
|
|
_S_ctx()
|
|
{
|
|
static io_context __ctx;
|
|
return __ctx;
|
|
}
|
|
|
|
basic_stream_socket<protocol_type> _M_socket;
|
|
error_code _M_ec;
|
|
time_point _M_expiry{ time_point::max() };
|
|
};
|
|
|
|
template<typename _Protocol, class _Clock, typename _WaitTraits>
|
|
class basic_socket_iostream : public basic_iostream<char>
|
|
{
|
|
using __streambuf_type
|
|
= basic_socket_streambuf<_Protocol, _Clock, _WaitTraits>;
|
|
|
|
public:
|
|
// types:
|
|
|
|
typedef _Protocol protocol_type;
|
|
typedef typename protocol_type::endpoint endpoint_type;
|
|
typedef _Clock clock_type;
|
|
typedef typename clock_type::time_point time_point;
|
|
typedef typename clock_type::duration duration;
|
|
typedef _WaitTraits wait_traits_type;
|
|
|
|
// construct / copy / destroy:
|
|
|
|
// TODO base-from-member ?
|
|
basic_socket_iostream() : basic_iostream(nullptr), _M_sb()
|
|
{
|
|
this->init(std::addressof(_M_sb));
|
|
this->setf(std::ios::unitbuf);
|
|
}
|
|
|
|
explicit
|
|
basic_socket_iostream(basic_stream_socket<protocol_type> __s)
|
|
: basic_iostream(nullptr), _M_sb(std::move(__s))
|
|
{
|
|
this->init(std::addressof(_M_sb));
|
|
this->setf(std::ios::unitbuf);
|
|
}
|
|
|
|
basic_socket_iostream(const basic_socket_iostream&) = delete;
|
|
|
|
basic_socket_iostream(basic_socket_iostream&& __rhs)
|
|
: basic_iostream(nullptr), _M_sb(std::move(__rhs._M_sb))
|
|
// XXX ??? ^^^^^^^
|
|
{
|
|
// XXX ??? this->init(std::addressof(_M_sb));
|
|
this->set_rbduf(std::addressof(_M_sb));
|
|
}
|
|
|
|
template<typename... _Args>
|
|
explicit
|
|
basic_socket_iostream(_Args&&... __args)
|
|
: basic_iostream(nullptr), _M_sb()
|
|
{
|
|
this->init(std::addressof(_M_sb));
|
|
this->setf(std::ios::unitbuf);
|
|
connect(forward<_Args>(__args)...);
|
|
}
|
|
|
|
basic_socket_iostream& operator=(const basic_socket_iostream&) = delete;
|
|
|
|
basic_socket_iostream& operator=(basic_socket_iostream&& __rhs); // TODO
|
|
|
|
// members:
|
|
|
|
template<typename... _Args>
|
|
void
|
|
connect(_Args&&... __args)
|
|
{
|
|
if (rdbuf()->connect(forward<_Args>(__args)...) == nullptr)
|
|
this->setstate(failbit);
|
|
}
|
|
|
|
void
|
|
close()
|
|
{
|
|
if (rdbuf()->close() == nullptr)
|
|
this->setstate(failbit);
|
|
}
|
|
|
|
basic_socket_streambuf<protocol_type, clock_type, wait_traits_type>*
|
|
rdbuf() const
|
|
{ return const_cast<__streambuf_type*>(std::addressof(_M_sb)); }
|
|
|
|
basic_socket<protocol_type>& socket() { return rdbuf()->socket(); }
|
|
error_code error() const { return rdbuf()->error(); }
|
|
|
|
time_point expiry() const { return rdbuf()->expiry(); }
|
|
void expires_at(const time_point& __t) { rdbuf()->expires_at(__t); }
|
|
void expires_after(const duration& __d) { rdbuf()->expires_after(__d); }
|
|
|
|
private:
|
|
__streambuf_type _M_sb;
|
|
};
|
|
|
|
// @}
|
|
|
|
/** @brief synchronous connect operations
|
|
* @{
|
|
*/
|
|
|
|
template<typename _Protocol, typename _EndpointSequence,
|
|
typename _ConnectCondition>
|
|
inline typename _Protocol::endpoint
|
|
connect(basic_socket<_Protocol>& __s,
|
|
const _EndpointSequence& __endpoints,
|
|
_ConnectCondition __c, error_code& __ec)
|
|
{
|
|
__ec.clear();
|
|
bool __found = false;
|
|
for (auto& __ep : __endpoints)
|
|
{
|
|
if (__c(__ec, __ep))
|
|
{
|
|
__found = true;
|
|
__s.close(__ec);
|
|
if (!__ec)
|
|
__s.open(__ep.protocol(), __ec);
|
|
if (!__ec)
|
|
__s.connect(__ep, __ec);
|
|
if (!__ec)
|
|
return __ep;
|
|
}
|
|
}
|
|
if (!__found)
|
|
__ec = socket_errc::not_found;
|
|
return typename _Protocol::endpoint{};
|
|
}
|
|
|
|
template<typename _Protocol, typename _InputIterator,
|
|
typename _ConnectCondition>
|
|
inline _InputIterator
|
|
connect(basic_socket<_Protocol>& __s,
|
|
_InputIterator __first, _InputIterator __last,
|
|
_ConnectCondition __c, error_code& __ec)
|
|
{
|
|
__ec.clear();
|
|
bool __found = false;
|
|
for (auto __i = __first; __i != __last; ++__i)
|
|
{
|
|
if (__c(__ec, *__i))
|
|
{
|
|
__found = true;
|
|
__s.close(__ec);
|
|
if (!__ec)
|
|
__s.open(typename _Protocol::endpoint(*__i).protocol(), __ec);
|
|
if (!__ec)
|
|
__s.connect(*__i, __ec);
|
|
if (!__ec)
|
|
return __i;
|
|
}
|
|
}
|
|
if (!__found)
|
|
__ec = socket_errc::not_found;
|
|
return __last;
|
|
}
|
|
|
|
template<typename _Protocol, typename _EndpointSequence,
|
|
typename _ConnectCondition>
|
|
inline typename _Protocol::endpoint
|
|
connect(basic_socket<_Protocol>& __s,
|
|
const _EndpointSequence& __endpoints,
|
|
_ConnectCondition __c)
|
|
{
|
|
return net::connect(__s, __endpoints, __c, __throw_on_error{"connect"});
|
|
}
|
|
|
|
template<typename _Protocol, typename _InputIterator,
|
|
typename _ConnectCondition>
|
|
inline _InputIterator
|
|
connect(basic_socket<_Protocol>& __s,
|
|
_InputIterator __first, _InputIterator __last,
|
|
_ConnectCondition __c)
|
|
{
|
|
return net::connect(__s, __first, __last, __c,
|
|
__throw_on_error{"connect"});
|
|
}
|
|
|
|
template<typename _Protocol, typename _EndpointSequence>
|
|
inline typename _Protocol::endpoint
|
|
connect(basic_socket<_Protocol>& __s,
|
|
const _EndpointSequence& __endpoints)
|
|
{
|
|
return net::connect(__s, __endpoints, [](auto, auto){ return true; },
|
|
__throw_on_error{"connect"});
|
|
}
|
|
|
|
template<typename _Protocol, typename _EndpointSequence>
|
|
inline typename _Protocol::endpoint
|
|
connect(basic_socket<_Protocol>& __s,
|
|
const _EndpointSequence& __endpoints,
|
|
error_code& __ec)
|
|
{
|
|
return net::connect(__s, __endpoints, [](auto, auto){ return true; },
|
|
__ec);
|
|
}
|
|
|
|
template<typename _Protocol, typename _InputIterator>
|
|
inline _InputIterator
|
|
connect(basic_socket<_Protocol>& __s,
|
|
_InputIterator __first, _InputIterator __last)
|
|
{
|
|
return net::connect(__s, __first, __last, [](auto, auto){ return true; },
|
|
__throw_on_error{"connect"});
|
|
}
|
|
|
|
template<typename _Protocol, typename _InputIterator>
|
|
inline _InputIterator
|
|
connect(basic_socket<_Protocol>& __s,
|
|
_InputIterator __first, _InputIterator __last,
|
|
error_code& __ec)
|
|
{
|
|
return net::connect(__s, __first, __last, [](auto, auto){ return true; },
|
|
__ec);
|
|
}
|
|
|
|
// @}
|
|
|
|
/** @brief asynchronous connect operations
|
|
* @{
|
|
*/
|
|
|
|
template<typename _Protocol, typename _EndpointSequence,
|
|
typename _ConnectCondition, typename _CompletionToken>
|
|
inline
|
|
__deduced_t<_CompletionToken,
|
|
void(error_code, typename _Protocol::endpoint)>
|
|
async_connect(basic_socket<_Protocol>& __s,
|
|
const _EndpointSequence& __endpoints,
|
|
_ConnectCondition __c, _CompletionToken&& __token); // TODO
|
|
|
|
template<typename _Protocol, typename _EndpointSequence,
|
|
typename _CompletionToken>
|
|
inline
|
|
__deduced_t<_CompletionToken,
|
|
void(error_code, typename _Protocol::endpoint)>
|
|
async_connect(basic_socket<_Protocol>& __s,
|
|
const _EndpointSequence& __endpoints,
|
|
_CompletionToken&& __token)
|
|
{
|
|
return net::async_connect(__s, __endpoints,
|
|
[](auto, auto){ return true; },
|
|
forward<_CompletionToken>(__token));
|
|
}
|
|
|
|
template<typename _Protocol, typename _InputIterator,
|
|
typename _ConnectCondition, typename _CompletionToken>
|
|
inline
|
|
__deduced_t<_CompletionToken, void(error_code, _InputIterator)>
|
|
async_connect(basic_socket<_Protocol>& __s,
|
|
_InputIterator __first, _InputIterator __last,
|
|
_ConnectCondition __c, _CompletionToken&& __token); // TODO
|
|
|
|
template<typename _Protocol, typename _InputIterator,
|
|
typename _CompletionToken>
|
|
inline
|
|
__deduced_t<_CompletionToken, void(error_code, _InputIterator)>
|
|
async_connect(basic_socket<_Protocol>& __s,
|
|
_InputIterator __first, _InputIterator __last,
|
|
_CompletionToken&& __token)
|
|
{
|
|
return net::async_connect(__s, __first, __last,
|
|
[](auto, auto){ return true; },
|
|
forward<_CompletionToken>(__token));
|
|
}
|
|
|
|
// @}
|
|
|
|
#endif // _GLIBCXX_HAVE_UNISTD_H
|
|
|
|
// @}
|
|
|
|
} // namespace v1
|
|
} // namespace net
|
|
} // namespace experimental
|
|
|
|
_GLIBCXX_END_NAMESPACE_VERSION
|
|
} // namespace std
|
|
|
|
#endif // C++14
|
|
|
|
#endif // _GLIBCXX_EXPERIMENTAL_SOCKET
|