13#include <util/threadinterrupt.h>
59 return connect(
m_socket, addr, addr_len);
62int Sock::Bind(
const sockaddr* addr, socklen_t addr_len)
const
64 return bind(
m_socket, addr, addr_len);
72std::unique_ptr<Sock>
Sock::Accept(sockaddr* addr, socklen_t* addr_len)
const
80 std::unique_ptr<Sock> sock;
82 const auto socket = accept(
m_socket, addr, addr_len);
85 sock = std::make_unique<Sock>(socket);
86 }
catch (
const std::exception&) {
100 return getsockopt(
m_socket, level, opt_name,
static_cast<char*
>(opt_val), opt_len);
105 return setsockopt(
m_socket, level, opt_name,
static_cast<const char*
>(opt_val), opt_len);
134#if defined(USE_POLL) || defined(WIN32)
146 std::shared_ptr<const Sock> shared{
this, [](
const Sock*) {}};
150 if (!
WaitMany(timeout, events_per_sock)) {
154 if (occurred !=
nullptr) {
155 *occurred = events_per_sock.begin()->second.occurred;
164 std::vector<pollfd> pfds;
165 for (
const auto& [sock, events] : events_per_sock) {
167 auto& pfd = pfds.back();
168 pfd.fd = sock->m_socket;
169 if (events.requested &
RECV) {
170 pfd.events |= POLLIN;
172 if (events.requested &
SEND) {
173 pfd.events |= POLLOUT;
181 assert(pfds.size() == events_per_sock.size());
183 for (
auto& [sock, events] : events_per_sock) {
184 assert(sock->m_socket ==
static_cast<SOCKET>(pfds[i].fd));
186 if (pfds[i].revents & POLLIN) {
187 events.occurred |=
RECV;
189 if (pfds[i].revents & POLLOUT) {
190 events.occurred |=
SEND;
192 if (pfds[i].revents & (POLLERR | POLLHUP)) {
193 events.occurred |=
ERR;
208 for (
const auto& [sock, events] : events_per_sock) {
209 if (!sock->IsSelectable()) {
212 const auto&
s = sock->m_socket;
213 if (events.requested &
RECV) {
216 if (events.requested &
SEND) {
220 socket_max = std::max(socket_max,
s);
229 for (
auto& [sock, events] : events_per_sock) {
230 const auto&
s = sock->m_socket;
232 if (FD_ISSET(
s, &recv)) {
233 events.occurred |=
RECV;
235 if (FD_ISSET(
s, &
send)) {
236 events.occurred |=
SEND;
238 if (FD_ISSET(
s, &err)) {
239 events.occurred |=
ERR;
248 std::chrono::milliseconds timeout,
251 const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
258 sent +=
static_cast<size_t>(
ret);
259 if (sent ==
data.size()) {
269 const auto now = GetTime<std::chrono::milliseconds>();
271 if (now >= deadline) {
273 "Send timeout (sent only %u of %u bytes before that)", sent,
data.size()));
278 "Send interrupted (sent only %u of %u bytes before that)", sent,
data.size()));
283 const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{
MAX_WAIT_FOR_IO});
289 std::chrono::milliseconds timeout,
296 std::chrono::milliseconds timeout,
298 size_t max_data)
const
300 const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
302 bool terminator_found{
false};
313 if (
data.size() >= max_data) {
314 throw std::runtime_error(
315 strprintf(
"Received too many bytes without a terminator (%u)",
data.size()));
320 const ssize_t peek_ret{
Recv(buf, std::min(
sizeof(buf), max_data -
data.size()), MSG_PEEK)};
331 throw std::runtime_error(
"Connection unexpectedly closed by peer");
333 auto end = buf + peek_ret;
334 auto terminator_pos = std::find(buf, end, terminator);
335 terminator_found = terminator_pos != end;
337 const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
338 static_cast<size_t>(peek_ret)};
340 const ssize_t read_ret{
Recv(buf, try_len, 0)};
342 if (read_ret < 0 ||
static_cast<size_t>(read_ret) != try_len) {
343 throw std::runtime_error(
344 strprintf(
"recv() returned %u bytes on attempt to read %u bytes but previous "
345 "peek claimed %u bytes are available",
346 read_ret, try_len, peek_ret));
350 const size_t append_len{terminator_found ? try_len - 1 : try_len};
352 data.append(buf, buf + append_len);
354 if (terminator_found) {
359 const auto now = GetTime<std::chrono::milliseconds>();
361 if (now >= deadline) {
363 "Receive timeout (received %u bytes without terminator before that)",
data.size()));
368 "Receive interrupted (received %u bytes without terminator before that)",
373 const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{
MAX_WAIT_FOR_IO});
381 errmsg =
"not connected";
386 switch (
Recv(&c,
sizeof(c), MSG_PEEK)) {
427 return Win32ErrorString(err);
A helper class for interruptible sleeps.
RAII helper class that manages a socket and closes it automatically when it goes out of scope.
virtual std::unique_ptr< Sock > Accept(sockaddr *addr, socklen_t *addr_len) const
accept(2) wrapper.
virtual ssize_t Send(const void *data, size_t len, int flags) const
send(2) wrapper.
static constexpr Event SEND
If passed to Wait(), then it will wait for readiness to send to the socket.
SOCKET m_socket
Contained socket.
Sock & operator=(const Sock &)=delete
Copy assignment operator, disabled because closing the same socket twice is undesirable.
virtual int Bind(const sockaddr *addr, socklen_t addr_len) const
bind(2) wrapper.
virtual bool Wait(std::chrono::milliseconds timeout, Event requested, Event *occurred=nullptr) const
Wait for readiness for input (recv) or output (send).
virtual ~Sock()
Destructor, close the socket or do nothing if empty.
virtual void SendComplete(std::span< const unsigned char > data, std::chrono::milliseconds timeout, CThreadInterrupt &interrupt) const
Send the given data, retrying on transient errors.
virtual int GetSockName(sockaddr *name, socklen_t *name_len) const
getsockname(2) wrapper.
void Close()
Close m_socket if it is not INVALID_SOCKET.
virtual bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock &events_per_sock) const
Same as Wait(), but wait on many sockets within the same timeout.
static constexpr Event ERR
Ignored if passed to Wait(), but could be set in the occurred events if an exceptional condition has ...
virtual bool IsConnected(std::string &errmsg) const
Check if still connected.
virtual int SetSockOpt(int level, int opt_name, const void *opt_val, socklen_t opt_len) const
setsockopt(2) wrapper.
static constexpr Event RECV
If passed to Wait(), then it will wait for readiness to read from the socket.
virtual int GetSockOpt(int level, int opt_name, void *opt_val, socklen_t *opt_len) const
getsockopt(2) wrapper.
virtual int Connect(const sockaddr *addr, socklen_t addr_len) const
connect(2) wrapper.
virtual ssize_t Recv(void *buf, size_t len, int flags) const
recv(2) wrapper.
virtual std::string RecvUntilTerminator(uint8_t terminator, std::chrono::milliseconds timeout, CThreadInterrupt &interrupt, size_t max_data) const
Read from socket until a terminator character is encountered.
virtual int Listen(int backlog) const
listen(2) wrapper.
virtual bool SetNonBlocking() const
Set the non-blocking option on the socket.
std::unordered_map< std::shared_ptr< const Sock >, Events, HashSharedPtrSock, EqualSharedPtrSock > EventsPerSock
On which socket to wait for what events in WaitMany().
virtual bool IsSelectable() const
Check if the underlying socket can be used for select(2) (or the Wait() method).
bool operator==(SOCKET s) const
Check if the internal socket is equal to s.
#define WSAGetLastError()
static bool IOErrorIsPermanent(int err)
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
static constexpr auto MAX_WAIT_FOR_IO
Maximum time to wait for I/O readiness.
constexpr auto MakeUCharSpan(const V &v) -> decltype(UCharSpanCast(std::span{v}))
Like the std::span constructor, but for (const) unsigned char member types only.
Auxiliary requested/occurred events to wait for in WaitMany().
std::string SysErrorString(int err)
Return system error string from errno value.
struct timeval MillisToTimeval(int64_t nTimeout)
Convert milliseconds to a struct timeval for e.g.
constexpr int64_t count_milliseconds(std::chrono::milliseconds t)