diff --git a/Net/include/Poco/Net/ServerSocket.h b/Net/include/Poco/Net/ServerSocket.h index 62dd932e56..e13d46ea0f 100644 --- a/Net/include/Poco/Net/ServerSocket.h +++ b/Net/include/Poco/Net/ServerSocket.h @@ -60,6 +60,10 @@ class Net_API ServerSocket: public Socket /// After successful construction, the server socket /// is ready to accept connections. + static ServerSocket fromFileDescriptor(poco_socket_t fd); + // Creates a socket from an existing file descriptor. + // Ownership is taken by poco + virtual ~ServerSocket(); /// Destroys the ServerSocket. diff --git a/Net/include/Poco/Net/Socket.h b/Net/include/Poco/Net/Socket.h index be1bf606a0..331657aebc 100644 --- a/Net/include/Poco/Net/Socket.h +++ b/Net/include/Poco/Net/Socket.h @@ -85,6 +85,10 @@ class Net_API Socket #endif // POCO_NEW_STATE_ON_MOVE + static Socket fromFileDescriptor(poco_socket_t fd); + // Creates a socket from an existing file descriptor. + // Ownership is taken by poco + virtual ~Socket(); /// Destroys the Socket and releases the /// SocketImpl. diff --git a/Net/include/Poco/Net/SocketImpl.h b/Net/include/Poco/Net/SocketImpl.h index 84fb517555..ad0f1b44fe 100644 --- a/Net/include/Poco/Net/SocketImpl.h +++ b/Net/include/Poco/Net/SocketImpl.h @@ -142,6 +142,13 @@ class Net_API SocketImpl: public Poco::RefCountedObject /// If the library has not been built with IPv6 support, /// a Poco::NotImplementedException will be thrown. + void useFileDescriptor(poco_socket_t fd); + /// Use a external file descriptor for the socket. Required to be careful + /// about what kind of file descriptor you're passing to make sure it's compatable + /// with how you plan on using it. These specifics are platform-specific. + /// Not valid to call this if the internal socket is already initialized. + /// Poco takes ownership of the file descriptor, closing it when this socket is closed. + virtual void listen(int backlog = 64); /// Puts the socket into listening state. /// diff --git a/Net/src/ServerSocket.cpp b/Net/src/ServerSocket.cpp index b5cc4572c7..52dcf4c835 100644 --- a/Net/src/ServerSocket.cpp +++ b/Net/src/ServerSocket.cpp @@ -57,6 +57,14 @@ ServerSocket::ServerSocket(SocketImpl* pImpl, bool ignore): Socket(pImpl) } +ServerSocket ServerSocket::fromFileDescriptor(poco_socket_t fd) +{ + ServerSocket s; + s.impl()->useFileDescriptor(fd); + return s; +} + + ServerSocket::~ServerSocket() { } diff --git a/Net/src/Socket.cpp b/Net/src/Socket.cpp index 75d48de481..cd62f40288 100644 --- a/Net/src/Socket.cpp +++ b/Net/src/Socket.cpp @@ -54,6 +54,15 @@ Socket::Socket(const Socket& socket): _pImpl->duplicate(); } + +Socket Socket::fromFileDescriptor(poco_socket_t fd) +{ + Socket s; + s.impl()->useFileDescriptor(fd); + return s; +} + + #if POCO_NEW_STATE_ON_MOVE Socket::Socket(Socket&& socket): diff --git a/Net/src/SocketImpl.cpp b/Net/src/SocketImpl.cpp index f35a41996e..36902fa4c5 100644 --- a/Net/src/SocketImpl.cpp +++ b/Net/src/SocketImpl.cpp @@ -276,6 +276,14 @@ void SocketImpl::bind6(const SocketAddress& address, bool reuseAddress, bool reu } +void SocketImpl::useFileDescriptor(poco_socket_t fd) +{ + poco_assert (_sockfd == POCO_INVALID_SOCKET); + + _sockfd = fd; +} + + void SocketImpl::listen(int backlog) { if (_sockfd == POCO_INVALID_SOCKET) throw InvalidSocketException(); diff --git a/Net/testsuite/src/EchoServer.cpp b/Net/testsuite/src/EchoServer.cpp index 48817683f5..4ee68b0702 100644 --- a/Net/testsuite/src/EchoServer.cpp +++ b/Net/testsuite/src/EchoServer.cpp @@ -9,6 +9,7 @@ #include "EchoServer.h" +#include "Poco/Net/ServerSocket.h" #include "Poco/Net/StreamSocket.h" #include "Poco/Net/SocketAddress.h" #include "Poco/Timespan.h" @@ -42,6 +43,17 @@ EchoServer::EchoServer(const Poco::Net::SocketAddress& address): } +EchoServer::EchoServer(const Poco::Net::ServerSocket& sock): + _socket(sock), + _thread("EchoServer"), + _stop(false), + _done(false) +{ + _thread.start(*this); + _ready.wait(); +} + + EchoServer::~EchoServer() { _stop = true; diff --git a/Net/testsuite/src/EchoServer.h b/Net/testsuite/src/EchoServer.h index 3df637450d..9a149fe670 100644 --- a/Net/testsuite/src/EchoServer.h +++ b/Net/testsuite/src/EchoServer.h @@ -31,6 +31,9 @@ class EchoServer: public Poco::Runnable EchoServer(const Poco::Net::SocketAddress& address); /// Creates the EchoServer using the given address. + EchoServer(const Poco::Net::ServerSocket& sock); + /// Creates the EchoServer using the already created socket + ~EchoServer(); /// Destroys the EchoServer. diff --git a/Net/testsuite/src/SocketTest.cpp b/Net/testsuite/src/SocketTest.cpp index c8976a1fb3..17289f7f37 100644 --- a/Net/testsuite/src/SocketTest.cpp +++ b/Net/testsuite/src/SocketTest.cpp @@ -598,6 +598,51 @@ void SocketTest::testUnixLocalAbstract() } +void SocketTest::testUseFd() +{ +#ifdef POCO_OS_FAMILY_WINDOWS + struct addrinfo addr_hint = {}; + addr_hint.ai_family = AF_INET; + addr_hint.ai_socktype = SOCK_STREAM; + addr_hint.ai_protocol = IPPROTO_TCP; + addr_hint.ai_flags = AI_PASSIVE; + struct addrinfo* addr_result; + getaddrinfo(nullptr, "0", &addr_hint, &addr_result); + poco_socket_t listenfd = socket(addr_result->ai_family, addr_result->ai_socktype, addr_result->ai_protocol); + bind(listenfd, addr_result->ai_addr, (int)addr_result->ai_addrlen); + freeaddrinfo(addr_result); + listen(listenfd, SOMAXCONN); + SOCKADDR_IN serv_addr; + int addr_len = sizeof(serv_addr); + getsockname(listenfd, (SOCKADDR*)&serv_addr, &addr_len); + auto server_port = ntohs(serv_addr.sin_port); +#elif defined(POCO_OS_FAMILY_UNIX) + poco_socket_t listenfd = socket(AF_INET, SOCK_STREAM, 0); + struct sockaddr_in serv_addr = {}; + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + serv_addr.sin_port = htons(0); + bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); + listen(listenfd, 1); + socklen_t len = sizeof(serv_addr); + getsockname(listenfd, (struct sockaddr*)&serv_addr, &len); + auto server_port = ntohs(serv_addr.sin_port); +#else + std::cout << "[USE FD TEST DISABLED]"; + return; +#endif + EchoServer server(ServerSocket::fromFileDescriptor(listenfd)); + StreamSocket ss; + ss.connect(SocketAddress("127.0.0.1", server_port)); + int n = ss.sendBytes("hello", 5); + assertTrue (n == 5); + char buffer[256]; + n = ss.receiveBytes(buffer, sizeof(buffer)); + assertTrue (n == 5); + assertTrue (std::string(buffer, n) == "hello"); + ss.close(); +} + void SocketTest::onReadable(bool& b) { @@ -650,6 +695,7 @@ CppUnit::Test* SocketTest::suite() CppUnit_addTest(pSuite, SocketTest, testSelect3); CppUnit_addTest(pSuite, SocketTest, testEchoUnixLocal); CppUnit_addTest(pSuite, SocketTest, testUnixLocalAbstract); + CppUnit_addTest(pSuite, SocketTest, testUseFd); return pSuite; } diff --git a/Net/testsuite/src/SocketTest.h b/Net/testsuite/src/SocketTest.h index e3b69e0f69..badaea9139 100644 --- a/Net/testsuite/src/SocketTest.h +++ b/Net/testsuite/src/SocketTest.h @@ -43,6 +43,7 @@ class SocketTest: public CppUnit::TestCase void testSelect3(); void testEchoUnixLocal(); void testUnixLocalAbstract(); + void testUseFd(); void setUp(); void tearDown();