13#include <util/threadinterrupt.h>
48ssize_t
Sock::Send(
const void* data,
size_t len,
int flags)
const
50 return send(
m_socket,
static_cast<const char*
>(data), len, flags);
53ssize_t
Sock::Recv(
void* buf,
size_t len,
int flags)
const
55 return recv(
m_socket,
static_cast<char*
>(buf), len, flags);
60 return connect(
m_socket, addr, addr_len);
63int Sock::Bind(
const sockaddr* addr, socklen_t addr_len)
const
65 return bind(
m_socket, addr, addr_len);
73std::unique_ptr<Sock>
Sock::Accept(sockaddr* addr, socklen_t* addr_len)
const
81 std::unique_ptr<Sock> sock;
83 const auto socket = accept(
m_socket, addr, addr_len);
84 if (socket != accept_error) {
86 sock = std::make_unique<Sock>(socket);
87 }
catch (
const std::exception&) {
101 return getsockopt(
m_socket, level, opt_name,
static_cast<char*
>(opt_val), opt_len);
106 return setsockopt(
m_socket, level, opt_name,
static_cast<const char*
>(opt_val), opt_len);
111 return getsockname(
m_socket, name, name_len);
122 const int flags{fcntl(
m_socket, F_GETFL, 0)};
135#if defined(USE_POLL) || defined(WIN32)
149 std::shared_ptr<const Sock> shared{std::shared_ptr<const Sock>{},
this};
153 if (!
WaitMany(timeout, events_per_sock)) {
157 if (occurred !=
nullptr) {
158 *occurred = events_per_sock.begin()->second.occurred;
168 timeout = std::min(timeout, std::chrono::milliseconds{std::numeric_limits<int>::max()});
171 std::vector<pollfd> pfds;
172 for (
const auto& [sock, events] : events_per_sock) {
174 auto& pfd = pfds.back();
175 pfd.fd = sock->m_socket;
177 pfd.events |= POLLIN;
180 pfd.events |= POLLOUT;
188 assert(pfds.size() == events_per_sock.size());
190 for (
auto& [sock, events] : events_per_sock) {
191 assert(sock->m_socket ==
static_cast<SOCKET>(pfds[i].fd));
193 if (pfds[i].revents & POLLIN) {
196 if (pfds[i].revents & POLLOUT) {
199 if (pfds[i].revents & (POLLERR | POLLHUP)) {
215 for (
const auto& [sock, events] : events_per_sock) {
216 if (!sock->IsSelectable()) {
219 const auto& s = sock->m_socket;
228 socket_max = std::max(socket_max, s);
234 if (select(socket_max + 1, &recv, &send, &err, &tv) ==
SOCKET_ERROR) {
238 for (
auto& [sock, events] : events_per_sock) {
239 const auto& s = sock->m_socket;
241 if (FD_ISSET(s, &recv)) {
244 if (FD_ISSET(s, &send)) {
247 if (FD_ISSET(s, &err)) {
257 std::chrono::milliseconds timeout,
267 sent +=
static_cast<size_t>(
ret);
268 if (sent == data.size()) {
280 if (now >= deadline) {
282 "Send timeout (sent only %u of %u bytes before that)", sent, data.size()));
287 "Send interrupted (sent only %u of %u bytes before that)", sent, data.size()));
292 const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{
MAX_WAIT_FOR_IO});
298 std::chrono::milliseconds timeout,
305 std::chrono::milliseconds timeout,
307 size_t max_data)
const
311 bool terminator_found{
false};
322 if (data.size() >= max_data) {
323 throw std::runtime_error(
324 strprintf(
"Received too many bytes without a terminator (%u)", data.size()));
329 const ssize_t peek_ret{
Recv(buf, std::min(
sizeof(buf), max_data - data.size()), MSG_PEEK)};
340 throw std::runtime_error(
"Connection unexpectedly closed by peer");
342 auto end = buf + peek_ret;
343 auto terminator_pos = std::find(buf, end, terminator);
344 terminator_found = terminator_pos != end;
346 const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
347 static_cast<size_t>(peek_ret)};
349 const ssize_t read_ret{
Recv(buf, try_len, 0)};
351 if (read_ret < 0 ||
static_cast<size_t>(read_ret) != try_len) {
352 throw std::runtime_error(
353 strprintf(
"recv() returned %u bytes on attempt to read %u bytes but previous "
354 "peek claimed %u bytes are available",
355 read_ret, try_len, peek_ret));
359 const size_t append_len{terminator_found ? try_len - 1 : try_len};
361 data.append(buf, buf + append_len);
363 if (terminator_found) {
370 if (now >= deadline) {
372 "Receive timeout (received %u bytes without terminator before that)", data.size()));
377 "Receive interrupted (received %u bytes without terminator before that)",
382 const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{
MAX_WAIT_FOR_IO});
390 errmsg =
"not connected";
395 switch (
Recv(&c,
sizeof(c), MSG_PEEK)) {
436 return Win32ErrorString(err);
A helper class for interruptible sleeps.
static constexpr Event RecvEvent
If passed to Wait(), then it will wait for readiness to read from the socket.
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.
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.
static constexpr Event SendEvent
If passed to Wait(), then it will wait for readiness to send to the socket.
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.
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 ErrorEvent
Ignored if passed to Wait(), but could be set in the occurred events if an exceptional condition has ...
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()
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
constexpr auto MAX_WAIT_FOR_IO
Maximum time to wait for I/O readiness.
bool IOErrorIsPermanent(int err)
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
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.
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
constexpr int64_t count_milliseconds(std::chrono::milliseconds t)