Add a new bind() and bind6() interface and make REUSEPORT as an option.

SO_REUSEADDR is to reuse a socket in time-wait state, this is often turned on
by server applicationis, but if a port is being used, the server could fail,
this is expected. But with SO_REUSEPORT option, it even can open a port which
is being used, this is a more error prone than SO_REUSEADDR, because we
normally don't want to share a port with irrelevant application without
notice, the behavior should not be default.
This commit is contained in:
Xu Yifeng 2016-11-09 17:05:50 +08:00
parent efdeadef2a
commit 0ad635c5d4
10 changed files with 192 additions and 4 deletions

View File

@ -89,6 +89,20 @@ public:
///
/// Calls to connect cannot() come before calls to bind().
void bind(const SocketAddress& address, bool reuseAddress, bool reusePort);
/// Bind a local address to the socket.
///
/// This is usually only done when establishing a server
/// socket.
///
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
///
/// If reusePort is true, sets the SO_REUSEPORT
/// socket option.
///
/// Calls to connect cannot() come before calls to bind().
int sendBytes(const void* buffer, int length, int flags = 0);
/// Sends the contents of the given buffer through
/// the socket.

View File

@ -82,6 +82,20 @@ public:
///
/// Calls to connect() cannot come before calls to bind().
void bind(const SocketAddress& address, bool reuseAddress, bool reusePort);
/// Bind a local address to the socket.
///
/// This is usually only done when establishing a server
/// socket.
///
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
///
/// If reusePort is true, sets the SO_REUSEPORT
/// socket option.
///
/// Calls to connect() cannot come before calls to bind().
int sendBytes(const void* buffer, int length, int flags = 0);
/// Sends the contents of the given buffer through
/// the socket.

View File

@ -82,6 +82,19 @@ public:
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
virtual void bind(const SocketAddress& address, bool reuseAddress, bool reusePort);
/// Binds a local address to the socket.
///
/// This is usually only done when establishing a server
/// socket. TCP clients should not bind a socket to a
/// specific address.
///
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
///
/// If reuseAddress is true, sets the SO_REUSEPORT
/// socket option.
virtual void bind(Poco::UInt16 port, bool reuseAddress = false);
/// Binds a local port to the socket.
///
@ -91,6 +104,18 @@ public:
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
virtual void bind(Poco::UInt16 port, bool reuseAddress, bool reusePort);
/// Binds a local port to the socket.
///
/// This is usually only done when establishing a server
/// socket.
///
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
///
/// If reusePort is true, sets the SO_REUSEPORT
/// socket option.
virtual void bind6(const SocketAddress& address, bool reuseAddress = false, bool ipV6Only = false);
/// Binds a local IPv6 address to the socket.
///
@ -108,6 +133,26 @@ public:
/// If the library has not been built with IPv6 support,
/// a Poco::NotImplementedException will be thrown.
virtual void bind6(const SocketAddress& address, bool reuseAddress, bool reusePort, bool ipV6Only);
/// Binds a local IPv6 address to the socket.
///
/// This is usually only done when establishing a server
/// socket. TCP clients should not bind a socket to a
/// specific address.
///
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
///
/// If reusePort is true, sets the SO_REUSEPORT
/// socket option.
///
/// The given address must be an IPv6 address. The
/// IPPROTO_IPV6/IPV6_V6ONLY option is set on the socket
/// according to the ipV6Only parameter.
///
/// If the library has not been built with IPv6 support,
/// a Poco::NotImplementedException will be thrown.
virtual void bind6(Poco::UInt16 port, bool reuseAddress = false, bool ipV6Only = false);
/// Binds a local IPv6 port to the socket.
///
@ -124,6 +169,24 @@ public:
/// If the library has not been built with IPv6 support,
/// a Poco::NotImplementedException will be thrown.
virtual void bind6(Poco::UInt16 port, bool reuseAddress, bool reusePort, bool ipV6Only);
/// Binds a local IPv6 port to the socket.
///
/// This is usually only done when establishing a server
/// socket.
///
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
///
/// If reusePort is true, sets the SO_REUSEPORT
/// socket option.
/// The given address must be an IPv6 address. The
/// IPPROTO_IPV6/IPV6_V6ONLY option is set on the socket
/// according to the ipV6Only parameter.
///
/// If the library has not been built with IPv6 support,
/// a Poco::NotImplementedException will be thrown.
virtual void listen(int backlog = 64);
/// Puts the socket into listening state.
///

View File

@ -86,6 +86,19 @@ public:
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
virtual void bind(const SocketAddress& address, bool reuseAddress, bool reusePort );
/// Bind a local address to the socket.
///
/// This is usually only done when establishing a server
/// socket. TCP clients should not bind a socket to a
/// specific address.
///
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
///
/// If reusePort is true, sets the SO_REUSEPORT
/// socket option.
virtual void bind6(const SocketAddress& address, bool reuseAddress = false, bool ipV6Only = false);
/// Bind a local IPv6 address to the socket.
///
@ -103,6 +116,26 @@ public:
/// If the library has not been built with IPv6 support,
/// a Poco::NotImplementedException will be thrown.
virtual void bind6(const SocketAddress& address, bool reuseAddress, bool reusePort, bool ipV6Only);
/// Bind a local IPv6 address to the socket.
///
/// This is usually only done when establishing a server
/// socket. TCP clients should not bind a socket to a
/// specific address.
///
/// If reuseAddress is true, sets the SO_REUSEADDR
/// socket option.
///
/// If reusePort is true, sets the SO_REUSEPORT
/// socket option.
///
/// The given address must be an IPv6 address. The
/// IPPROTO_IPV6/IPV6_V6ONLY option is set on the socket
/// according to the ipV6Only parameter.
///
/// If the library has not been built with IPv6 support,
/// a Poco::NotImplementedException will be thrown.
virtual void listen(int backlog = 64);
/// Puts the socket into listening state.
///

View File

@ -52,7 +52,9 @@ public:
virtual void connect(const SocketAddress& address, const Poco::Timespan& timeout);
virtual void connectNB(const SocketAddress& address);
virtual void bind(const SocketAddress& address, bool reuseAddress = false);
virtual void bind(const SocketAddress& address, bool reuseAddress, bool reusePort);
virtual void bind6(const SocketAddress& address, bool reuseAddress = false, bool ipV6Only = false);
virtual void bind6(const SocketAddress& address, bool reuseAddress, bool reusePort, bool ipV6Only);
virtual void listen(int backlog = 64);
virtual void close();
virtual void shutdownReceive();

View File

@ -83,6 +83,12 @@ void DatagramSocket::bind(const SocketAddress& address, bool reuseAddress)
}
void DatagramSocket::bind(const SocketAddress& address, bool reuseAddress, bool reusePort)
{
impl()->bind(address, reuseAddress, reusePort);
}
int DatagramSocket::sendBytes(const void* buffer, int length, int flags)
{
return impl()->sendBytes(buffer, length, flags);

View File

@ -86,6 +86,12 @@ void RawSocket::bind(const SocketAddress& address, bool reuseAddress)
}
void RawSocket::bind(const SocketAddress& address, bool reuseAddress, bool reusePort)
{
impl()->bind(address, reuseAddress, reusePort);
}
int RawSocket::sendBytes(const void* buffer, int length, int flags)
{
return impl()->sendBytes(buffer, length, flags);

View File

@ -80,6 +80,12 @@ void ServerSocket::bind(const SocketAddress& address, bool reuseAddress)
}
void ServerSocket::bind(const SocketAddress& address, bool reuseAddress, bool reusePort)
{
impl()->bind(address, reuseAddress, reusePort);
}
void ServerSocket::bind(Poco::UInt16 port, bool reuseAddress)
{
IPAddress wildcardAddr;
@ -88,12 +94,24 @@ void ServerSocket::bind(Poco::UInt16 port, bool reuseAddress)
}
void ServerSocket::bind(Poco::UInt16 port, bool reuseAddress, bool reusePort)
{
IPAddress wildcardAddr;
SocketAddress address(wildcardAddr, port);
impl()->bind(address, reuseAddress, reusePort);
}
void ServerSocket::bind6(const SocketAddress& address, bool reuseAddress, bool ipV6Only)
{
impl()->bind6(address, reuseAddress, ipV6Only);
}
void ServerSocket::bind6(const SocketAddress& address, bool reuseAddress, bool reusePort, bool ipV6Only)
{
impl()->bind6(address, reuseAddress, reusePort, ipV6Only);
}
void ServerSocket::bind6(Poco::UInt16 port, bool reuseAddress, bool ipV6Only)
{
#if defined(POCO_HAVE_IPv6)
@ -106,6 +124,17 @@ void ServerSocket::bind6(Poco::UInt16 port, bool reuseAddress, bool ipV6Only)
}
void ServerSocket::bind6(Poco::UInt16 port, bool reuseAddress, bool reusePort, bool ipV6Only)
{
#if defined(POCO_HAVE_IPv6)
IPAddress wildcardAddr(IPAddress::IPv6);
SocketAddress address(wildcardAddr, port);
impl()->bind6(address, reuseAddress, reusePort, ipV6Only);
#else
throw Poco::NotImplementedException("No IPv6 support available");
#endif // POCO_HAVE_IPv6
}
void ServerSocket::listen(int backlog)
{
impl()->listen(backlog);

View File

@ -190,16 +190,21 @@ void SocketImpl::connectNB(const SocketAddress& address)
void SocketImpl::bind(const SocketAddress& address, bool reuseAddress)
{
bind(address, reuseAddress, true);
}
void SocketImpl::bind(const SocketAddress& address, bool reuseAddress, bool reusePort)
{
if (_sockfd == POCO_INVALID_SOCKET)
{
init(address.af());
}
if (reuseAddress)
{
setReuseAddress(true);
if (reusePort)
setReusePort(true);
}
#if defined(POCO_VXWORKS)
int rc = ::bind(_sockfd, (sockaddr*) address.addr(), address.length());
#else
@ -210,6 +215,12 @@ void SocketImpl::bind(const SocketAddress& address, bool reuseAddress)
void SocketImpl::bind6(const SocketAddress& address, bool reuseAddress, bool ipV6Only)
{
bind6(address, reuseAddress, true, ipV6Only);
}
void SocketImpl::bind6(const SocketAddress& address, bool reuseAddress, bool reusePort, bool ipV6Only)
{
#if defined(POCO_HAVE_IPv6)
if (address.family() != SocketAddress::IPv6)
@ -225,10 +236,9 @@ void SocketImpl::bind6(const SocketAddress& address, bool reuseAddress, bool ipV
if (ipV6Only) throw Poco::NotImplementedException("IPV6_V6ONLY not defined.");
#endif
if (reuseAddress)
{
setReuseAddress(true);
if (reusePort)
setReusePort(true);
}
int rc = ::bind(_sockfd, address.addr(), address.length());
if (rc != 0) error(address.toString());
#else

View File

@ -257,12 +257,23 @@ void WebSocketImpl::bind(const SocketAddress& address, bool reuseAddress)
}
void WebSocketImpl::bind(const SocketAddress& address, bool reuseAddress, bool reusePort)
{
throw Poco::InvalidAccessException("Cannot bind() a WebSocketImpl");
}
void WebSocketImpl::bind6(const SocketAddress& address, bool reuseAddress, bool ipV6Only)
{
throw Poco::InvalidAccessException("Cannot bind6() a WebSocketImpl");
}
void WebSocketImpl::bind6(const SocketAddress& address, bool reuseAddress, bool reusePort, bool ipV6Only)
{
throw Poco::InvalidAccessException("Cannot bind6() a WebSocketImpl");
}
void WebSocketImpl::listen(int backlog)
{
throw Poco::InvalidAccessException("Cannot listen() on a WebSocketImpl");