71 return connect(
m_socket, addr, addr_len);
76 return getsockopt(
m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
85 if (requested &
RECV) {
88 if (requested &
SEND) {
96 if (occurred !=
nullptr) {
98 if (fd.revents & POLLIN) {
101 if (fd.revents & POLLOUT) {
114 FD_ZERO(&fdset_recv);
115 FD_ZERO(&fdset_send);
117 if (requested &
RECV) {
121 if (requested &
SEND) {
131 if (occurred !=
nullptr) {
133 if (FD_ISSET(
m_socket, &fdset_recv)) {
136 if (FD_ISSET(
m_socket, &fdset_send)) {
146 std::chrono::milliseconds timeout,
149 const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
153 const ssize_t ret{
Send(data.data() + sent, data.size() - sent,
MSG_NOSIGNAL)};
156 sent +=
static_cast<size_t>(ret);
157 if (sent == data.size()) {
167 const auto now = GetTime<std::chrono::milliseconds>();
169 if (now >= deadline) {
171 "Send timeout (sent only %u of %u bytes before that)", sent, data.size()));
176 "Send interrupted (sent only %u of %u bytes before that)", sent, data.size()));
181 const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{
MAX_WAIT_FOR_IO});
187 std::chrono::milliseconds timeout,
189 size_t max_data)
const 191 const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
193 bool terminator_found{
false};
204 if (data.size() >= max_data) {
205 throw std::runtime_error(
206 strprintf(
"Received too many bytes without a terminator (%u)", data.size()));
211 const ssize_t peek_ret{
Recv(buf, std::min(
sizeof(buf), max_data - data.size()), MSG_PEEK)};
222 throw std::runtime_error(
"Connection unexpectedly closed by peer");
224 auto end = buf + peek_ret;
225 auto terminator_pos = std::find(buf, end, terminator);
226 terminator_found = terminator_pos != end;
228 const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
229 static_cast<size_t>(peek_ret)};
231 const ssize_t read_ret{
Recv(buf, try_len, 0)};
233 if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
234 throw std::runtime_error(
235 strprintf(
"recv() returned %u bytes on attempt to read %u bytes but previous " 236 "peek claimed %u bytes are available",
237 read_ret, try_len, peek_ret));
241 const size_t append_len{terminator_found ? try_len - 1 : try_len};
243 data.append(buf, buf + append_len);
245 if (terminator_found) {
250 const auto now = GetTime<std::chrono::milliseconds>();
252 if (now >= deadline) {
254 "Receive timeout (received %u bytes without terminator before that)", data.size()));
259 "Receive interrupted (received %u bytes without terminator before that)",
264 const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{
MAX_WAIT_FOR_IO});
272 errmsg =
"not connected";
277 switch (
Recv(&c,
sizeof(c), MSG_PEEK)) {
299 if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
300 nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
301 buf, ARRAYSIZE(buf),
nullptr))
303 return strprintf(
"%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,
wchar_t>().to_bytes(buf), err);
307 return strprintf(
"Unknown error (%d)", err);
318 #ifdef STRERROR_R_CHAR_P 319 s = strerror_r(err, buf,
sizeof(buf));
322 if (strerror_r(err, buf,
sizeof(buf)))
334 int ret = closesocket(hSocket);
336 int ret = close(hSocket);
static bool IsSelectableSocket(const SOCKET &s)
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()
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.
virtual void Reset()
Close if non-empty.
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.
bool CloseSocket(SOCKET &hSocket)
Close socket and set hSocket to INVALID_SOCKET.
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
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 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 SOCKET Release()
Get the value of the contained socket and drop ownership.
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.