Skip to content

Commit

Permalink
Changed connect() w/ timeout to use poll() on non-Windows systems.
Browse files Browse the repository at this point in the history
  • Loading branch information
fpagliughi committed Apr 28, 2023
1 parent dcbd7af commit 0974f86
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 24 deletions.
20 changes: 14 additions & 6 deletions include/sockpp/inet_address.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ namespace sockpp {
class inet_address : public sock_address
{
/** The underlying C struct for IPv4 addresses */
sockaddr_in addr_;
sockaddr_in addr_{};

/** The size of the underlying address struct, in bytes */
static constexpr size_t SZ = sizeof(sockaddr_in);
Expand All @@ -78,7 +78,7 @@ class inet_address : public sock_address
* Constructs an empty address.
* The address is initialized to all zeroes.
*/
inet_address() : addr_() {}
inet_address() =default;
/**
* Constructs an address for any iface using the specified port.
* This is a convenient way for a server to specify an address that will
Expand Down Expand Up @@ -123,19 +123,27 @@ class inet_address : public sock_address
* Constructs the address by copying the specified structure.
* @param addr The other address
*/
inet_address(const sockaddr_in& addr) : addr_(addr) {}
inet_address(const sockaddr_in& addr) : addr_{addr} {}
/**
* Constructs the address by copying the specified address.
* @param addr The other address
*/
inet_address(const inet_address& addr) : addr_(addr.addr_) {}
inet_address(const inet_address& addr) : addr_{addr.addr_} {}
/**
* Checks if the address is set to some value.
* This doesn't attempt to determine if the address is valid, simply
* that it's not all zero.
* This doesn't attempt to determine if the address is valid, simply
* that it's not all zero.
* @return bool
*/
bool is_set() const;
/**
* Determines if the address is set to some value.
* This doesn't attempt to determine if the address is valid, simply
* that the family has been set properly.
* @return @em true if this has been set as some AF_INET address,
* whether or not is is valid.
*/
operator bool() const { return addr_.sin_family == ADDRESS_FAMILY; }
/**
* Attempts to resolve the host name into a 32-bit internet address.
* @param saddr The string host name.
Expand Down
1 change: 1 addition & 0 deletions include/sockpp/sock_address.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ namespace sockpp {

/**
* Generic socket address.
*
* Abstract base class for socket addresses. The underlying C socket
* functions typically take or return an address as a `sockaddr` pointer and
* length. So derived classes that wrap this to convert themselves to
Expand Down
57 changes: 39 additions & 18 deletions src/connector.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// stream_connector.cpp
// connector.cpp
//
// --------------------------------------------------------------------------
// This file is part of the "sockpp" C++ socket library.
//
// Copyright (c) 2014-2017 Frank Pagliughi
// Copyright (c) 2014-2023 Frank Pagliughi
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -36,7 +36,6 @@

#include "sockpp/connector.h"
#include <cerrno>

#if defined(_WIN32)
// Winsock calls return non-POSIX error codes
#undef EINPROGRESS
Expand All @@ -47,8 +46,15 @@

#undef EWOULDBLOCK
#define EWOULDBLOCK WSAEWOULDBLOCK
#else
#include <sys/poll.h>
#if defined(__APPLE__)
#include <net/if.h>
#endif
#endif

using namespace std::chrono;

namespace sockpp {

/////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -89,24 +95,37 @@ bool connector::connect(const sock_address& addr, std::chrono::microseconds time
if (!recreate(addr))
return false;

// Out new socket is definitely in blocking mode;
// make it non-blocking to do this
set_non_blocking(true);
bool non_blocking =
#if defined(_WIN32)
false;
#else
is_non_blocking();
#endif

if (!non_blocking)
set_non_blocking(true);

// TODO: Reimplement with poll() for systems with lots of sockets.

if (!check_ret_bool(::connect(handle(), addr.sockaddr_ptr(), addr.size()))) {
if (last_error() == EINPROGRESS || last_error() == EWOULDBLOCK) {
// Non-blocking connect -- call `select` to wait until the timeout:
// Note: Windows returns errors in exceptset so check it too, the
// logic afterwords doesn't change
fd_set readset;
FD_ZERO(&readset);
FD_SET(handle(), &readset);
fd_set writeset = readset;
fd_set exceptset = readset;
timeval tv = to_timeval(timeout);
int n = check_ret(::select(int(handle())+1, &readset, &writeset, &exceptset, &tv));
// TODO: Windows has a WSAPoll() function we can use.
#if defined(_WIN32)
// Non-blocking connect -- call `select` to wait until the timeout:
// Note: Windows returns errors in exceptset so check it too, the
// logic afterwords doesn't change
fd_set readset;
FD_ZERO(&readset);
FD_SET(handle(), &readset);
fd_set writeset = readset;
fd_set exceptset = readset;
timeval tv = to_timeval(timeout);
int n = check_ret(::select(int(handle())+1, &readset, &writeset, &exceptset, &tv));
#else
pollfd fds = { handle(), POLLIN|POLLOUT, 0 };
int ms = int(duration_cast<milliseconds>(timeout).count());
int n = check_ret(::poll(&fds, 1, ms));
#endif

if (n > 0) {
// Got a socket event, but it might be an error, so check:
Expand All @@ -125,8 +144,10 @@ bool connector::connect(const sock_address& addr, std::chrono::microseconds time
}
}

// Restore the default (blocking) mode for a new socket.
set_non_blocking(false);
// Restore blocking mode for socket, if needed.
if (!non_blocking)
set_non_blocking(false);

return true;
}

Expand Down

0 comments on commit 0974f86

Please sign in to comment.