66 return connect(
m_socket, addr, addr_len);
69 int Sock::Bind(
const sockaddr* addr, socklen_t addr_len)
const 71 return bind(
m_socket, addr, addr_len);
79 std::unique_ptr<Sock>
Sock::Accept(sockaddr* addr, socklen_t* addr_len)
const 87 std::unique_ptr<Sock> sock;
89 const auto socket = accept(
m_socket, addr, addr_len);
92 sock = std::make_unique<Sock>(socket);
93 }
catch (
const std::exception&) {
107 return getsockopt(
m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
110 int Sock::SetSockOpt(
int level,
int opt_name,
const void* opt_val, socklen_t opt_len)
const 112 return setsockopt(
m_socket, level, opt_name, static_cast<const char*>(opt_val), opt_len);
125 std::shared_ptr<const Sock> shared{
this, [](
const Sock*) {}};
129 if (!
WaitMany(timeout, events_per_sock)) {
133 if (occurred !=
nullptr) {
134 *occurred = events_per_sock.begin()->second.occurred;
143 std::vector<pollfd> pfds;
144 for (
const auto& [sock, events] : events_per_sock) {
146 auto& pfd = pfds.back();
147 pfd.fd = sock->m_socket;
148 if (events.requested &
RECV) {
149 pfd.events |= POLLIN;
151 if (events.requested &
SEND) {
152 pfd.events |= POLLOUT;
160 assert(pfds.size() == events_per_sock.size());
162 for (
auto& [sock, events] : events_per_sock) {
163 assert(sock->m_socket == static_cast<SOCKET>(pfds[i].fd));
165 if (pfds[i].revents & POLLIN) {
166 events.occurred |=
RECV;
168 if (pfds[i].revents & POLLOUT) {
169 events.occurred |=
SEND;
171 if (pfds[i].revents & (POLLERR | POLLHUP)) {
172 events.occurred |=
ERR;
187 for (
const auto& [sock, events] : events_per_sock) {
188 const auto& s = sock->m_socket;
192 if (events.requested &
RECV) {
195 if (events.requested &
SEND) {
199 socket_max = std::max(socket_max, s);
208 for (
auto& [sock, events] : events_per_sock) {
209 const auto& s = sock->m_socket;
211 if (FD_ISSET(s, &recv)) {
212 events.occurred |=
RECV;
214 if (FD_ISSET(s, &
send)) {
215 events.occurred |=
SEND;
217 if (FD_ISSET(s, &err)) {
218 events.occurred |=
ERR;
227 std::chrono::milliseconds timeout,
230 const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
237 sent +=
static_cast<size_t>(
ret);
238 if (sent == data.size()) {
248 const auto now = GetTime<std::chrono::milliseconds>();
250 if (now >= deadline) {
252 "Send timeout (sent only %u of %u bytes before that)", sent, data.size()));
257 "Send interrupted (sent only %u of %u bytes before that)", sent, data.size()));
262 const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{
MAX_WAIT_FOR_IO});
268 std::chrono::milliseconds timeout,
270 size_t max_data)
const 272 const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
274 bool terminator_found{
false};
285 if (data.size() >= max_data) {
286 throw std::runtime_error(
287 strprintf(
"Received too many bytes without a terminator (%u)", data.size()));
292 const ssize_t peek_ret{
Recv(buf, std::min(
sizeof(buf), max_data - data.size()), MSG_PEEK)};
303 throw std::runtime_error(
"Connection unexpectedly closed by peer");
305 auto end = buf + peek_ret;
306 auto terminator_pos = std::find(buf, end, terminator);
307 terminator_found = terminator_pos != end;
309 const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
310 static_cast<size_t>(peek_ret)};
312 const ssize_t read_ret{
Recv(buf, try_len, 0)};
314 if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
315 throw std::runtime_error(
316 strprintf(
"recv() returned %u bytes on attempt to read %u bytes but previous " 317 "peek claimed %u bytes are available",
318 read_ret, try_len, peek_ret));
322 const size_t append_len{terminator_found ? try_len - 1 : try_len};
324 data.append(buf, buf + append_len);
326 if (terminator_found) {
331 const auto now = GetTime<std::chrono::milliseconds>();
333 if (now >= deadline) {
335 "Receive timeout (received %u bytes without terminator before that)", data.size()));
340 "Receive interrupted (received %u bytes without terminator before that)",
345 const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{
MAX_WAIT_FOR_IO});
353 errmsg =
"not connected";
358 switch (
Recv(&c,
sizeof(c), MSG_PEEK)) {
396 if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
397 nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
398 buf, ARRAYSIZE(buf),
nullptr))
400 return strprintf(
"%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,
wchar_t>().to_bytes(buf), err);
404 return strprintf(
"Unknown error (%d)", err);
static bool IsSelectableSocket(const SOCKET &s)
virtual int Bind(const sockaddr *addr, socklen_t addr_len) const
bind(2) wrapper.
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 void SendComplete(const std::string &data, std::chrono::milliseconds timeout, CThreadInterrupt &interrupt) const
Send the given data, retrying on transient errors.
SOCKET m_socket
Contained socket.
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 ssize_t Recv(void *buf, size_t len, int flags) const
recv(2) wrapper.
#define WSAGetLastError()
void Close()
Close m_socket if it is not INVALID_SOCKET.
virtual bool Wait(std::chrono::milliseconds timeout, Event requested, Event *occurred=nullptr) const
Wait for readiness for input (recv) or output (send).
static constexpr Event SEND
If passed to Wait(), then it will wait for readiness to send to the socket.
virtual int GetSockOpt(int level, int opt_name, void *opt_val, socklen_t *opt_len) const
getsockopt(2) wrapper.
std::string SysErrorString(int err)
Return system error string from errno value.
virtual std::unique_ptr< Sock > Accept(sockaddr *addr, socklen_t *addr_len) const
accept(2) wrapper.
std::unordered_map< std::shared_ptr< const Sock >, Events, HashSharedPtrSock, EqualSharedPtrSock > EventsPerSock
On which socket to wait for what events in WaitMany().
constexpr int64_t count_milliseconds(std::chrono::milliseconds t)
virtual ssize_t Send(const void *data, size_t len, int flags) const
send(2) wrapper.
virtual int SetSockOpt(int level, int opt_name, const void *opt_val, socklen_t opt_len) const
setsockopt(2) wrapper.
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
static constexpr Event ERR
Ignored if passed to Wait(), but could be set in the occurred events if an exceptional condition has ...
static constexpr Event RECV
If passed to Wait(), then it will wait for readiness to read from the socket.
static bool IOErrorIsPermanent(int err)
virtual int Listen(int backlog) const
listen(2) wrapper.
virtual SOCKET Get() const
Get the value of the contained socket.
struct timeval MillisToTimeval(int64_t nTimeout)
Convert milliseconds to a struct timeval for e.g.
Sock()
Default constructor, creates an empty object that does nothing when destroyed.
RAII helper class that manages a socket.
virtual ~Sock()
Destructor, close the socket or do nothing if empty.
virtual bool IsConnected(std::string &errmsg) const
Check if still connected.
virtual int GetSockName(sockaddr *name, socklen_t *name_len) const
getsockname(2) wrapper.
Auxiliary requested/occurred events to wait for in WaitMany().
Sock & operator=(const Sock &)=delete
Copy assignment operator, disabled because closing the same socket twice is undesirable.
virtual int Connect(const sockaddr *addr, socklen_t addr_len) const
connect(2) wrapper.
static constexpr auto MAX_WAIT_FOR_IO
Maximum time to wait for I/O readiness.