Skip to content

Commit f151030

Browse files
committed
Use poll() to wait for incoming connections with a timeout
1 parent bff2e88 commit f151030

1 file changed

Lines changed: 38 additions & 20 deletions

File tree

include/tcp_socket.h

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -150,35 +150,53 @@ namespace ipsockets {
150150
socklen_t addr_len = sizeof (sockaddr_in_t);
151151
sockaddr_in_t addr_from = {};
152152

153-
#ifdef _WIN32 // WINDOWS OS
154-
// for windows ::accept will never be timeout and we should close sock connection to interrupt it or we should using poll()
155-
DWORD tv_ms = 1000; // get SO_RCVTIMEO if set (DWORD on Windows), fallback to 1000 ms
156-
int optlen = sizeof(tv_ms);
157-
if (getsockopt(this->sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv_ms, &optlen) == SOCKET_ERROR)
158-
tv_ms = 1000;
159-
160-
WSAPOLLFD pfd;
161-
pfd.fd = this->sock;
162-
pfd.events = POLLRDNORM; // wait incoming data
163-
164-
int rv = WSAPoll(&pfd, 1, (int)tv_ms);
165-
if (rv == 0) { // timeout
153+
// Use poll() to wait for incoming connections with a timeout.
154+
// On Windows ::accept() does not respect SO_RCVTIMEO, and on some
155+
// Linux versions SO_RCVTIMEO may not interrupt accept(). poll() solves
156+
// both issues and allows graceful interruption via close() from another
157+
// thread (poll returns POLLNVAL or an error on a closed fd).
158+
{
159+
int tv_ms = 1000;
160+
#ifdef _WIN32 // WINDOWS OS
161+
DWORD tv_dword = 1000;
162+
int optlen = sizeof (tv_dword);
163+
if (getsockopt (this->sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv_dword, &optlen) != SOCKET_ERROR)
164+
tv_ms = (int)tv_dword;
165+
166+
WSAPOLLFD pfd;
167+
pfd.fd = this->sock;
168+
pfd.events = POLLRDNORM;
169+
int rv = WSAPoll (&pfd, 1, tv_ms);
170+
#else // LINUX OS
171+
timeval tv_val = {};
172+
socklen_t tv_len = sizeof (tv_val);
173+
if (getsockopt (this->sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv_val, &tv_len) != SOCKET_ERROR)
174+
tv_ms = (int)(tv_val.tv_sec * 1000 + tv_val.tv_usec / 1000);
175+
if (tv_ms <= 0) tv_ms = 1000;
176+
177+
pollfd pfd;
178+
pfd.fd = this->sock;
179+
pfd.events = POLLIN;
180+
int rv = poll (&pfd, 1, tv_ms);
181+
#endif
182+
183+
if (rv == 0) {
166184
result.sock = INVALID_SOCKET;
167185
cerr = error_timeout;
168186
}
169-
else if (pfd.revents & POLLRDNORM) { // data ready
187+
else if (rv > 0 && (pfd.revents & (POLLIN
188+
#ifdef _WIN32
189+
| POLLRDNORM
190+
#endif
191+
))) {
170192
result.sock = ::accept (this->sock, (sockaddr*)&addr_from, &addr_len);
171193
cerr = this->_get_err ();
172194
}
173-
else { // error
195+
else {
174196
result.sock = INVALID_SOCKET;
175197
cerr = this->_get_err ();
176198
}
177-
#else // LINUX OS
178-
// for linux ::accept will be timeout by value SO_RCVTIMEO wich we set by setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof (tv));
179-
result.sock = ::accept (this->sock, (sockaddr*)&addr_from, &addr_len);
180-
cerr = this->_get_err ();
181-
#endif
199+
}
182200

183201
if (result.sock == INVALID_SOCKET) {
184202
this->log_and_return ('-', "accept", cerr);

0 commit comments

Comments
 (0)