diff --git a/hazelcast/include/hazelcast/client/config/ClientNetworkConfig.h b/hazelcast/include/hazelcast/client/config/ClientNetworkConfig.h index a39cf631d1..a01e1c6d78 100644 --- a/hazelcast/include/hazelcast/client/config/ClientNetworkConfig.h +++ b/hazelcast/include/hazelcast/client/config/ClientNetworkConfig.h @@ -22,6 +22,7 @@ #include "hazelcast/util/HazelcastDll.h" #include "hazelcast/client/config/SSLConfig.h" #include "hazelcast/client/config/ClientAwsConfig.h" +#include "hazelcast/client/config/SocketOptions.h" #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) #pragma warning(push) @@ -180,6 +181,8 @@ namespace hazelcast { */ ClientNetworkConfig &addAddress(const Address &address); + SocketOptions &getSocketOptions(); + private: static int32_t CONNECTION_ATTEMPT_PERIOD; @@ -194,6 +197,7 @@ namespace hazelcast { std::vector
addressList; + SocketOptions socketOptions; }; } } diff --git a/hazelcast/include/hazelcast/client/config/SocketOptions.h b/hazelcast/include/hazelcast/client/config/SocketOptions.h new file mode 100644 index 0000000000..14fa317d6c --- /dev/null +++ b/hazelcast/include/hazelcast/client/config/SocketOptions.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2008-2018, Hazelcast, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HAZELCAST_CLIENT_CONFIG_SOCKETOPTIONS_H_ +#define HAZELCAST_CLIENT_CONFIG_SOCKETOPTIONS_H_ + +#include "hazelcast/util/HazelcastDll.h" + +#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) +#pragma warning(push) +#pragma warning(disable: 4251) //for dll export +#endif + +namespace hazelcast { + namespace client { + namespace config { + /** + * TCP Socket options + */ + class HAZELCAST_API SocketOptions { + public: + /** + * constant for kilobyte + */ + static const int KILO_BYTE = 1024; + + /** + * default buffer size of Bytes + */ + static const int DEFAULT_BUFFER_SIZE_BYTE = 128 * KILO_BYTE; + + SocketOptions(); + + /** + * TCP_NODELAY socket option + * + * @return true if enabled + */ + bool isTcpNoDelay() const; + + /** + * Enable/disable TCP_NODELAY socket option. + * + * @param tcpNoDelay + */ + SocketOptions &setTcpNoDelay(bool tcpNoDelay); + + /** + * SO_KEEPALIVE socket option + * + * @return true if enabled + */ + bool isKeepAlive() const; + + /** + * Enable/disable SO_KEEPALIVE socket option. + * + * @param keepAlive enabled if true + * @return SocketOptions configured + */ + SocketOptions &setKeepAlive(bool keepAlive); + + /** + * SO_REUSEADDR socket option. + * + * @return true if enabled + */ + bool isReuseAddress() const; + + /** + * Enable/disable the SO_REUSEADDR socket option. + * + * @param reuseAddress enabled if true + * @return SocketOptions configured + */ + SocketOptions &setReuseAddress(bool reuseAddress); + + /** + * Gets SO_LINGER with the specified linger time in seconds + * @return lingerSeconds value in seconds + */ + int getLingerSeconds() const; + + /** + * Enable/disable SO_LINGER with the specified linger time in seconds + * + * if set to a value of 0 or less then it is disabled. + * + * Default value is 3. + * + * @param lingerSeconds value in seconds + * @return SocketOptions configured + */ + SocketOptions &setLingerSeconds(int lingerSeconds); + + /** + * If set to 0 or less, then it is not set on the socket. + * + * The default value is DEFAULT_BUFFER_SIZE_BYTE + * + * Gets the SO_SNDBUF and SO_RCVBUF options value in bytes + * @return bufferSize Number of bytes + */ + int getBufferSizeInBytes() const; + + /** + * If set to 0 or less, then it is not set on the socket. + * + * The default value is DEFAULT_BUFFER_SIZE_BYTE + * + * Sets the SO_SNDBUF and SO_RCVBUF options to the specified value in bytes + * + * @param bufferSize Number of bytes + * @return SocketOptions configured + */ + SocketOptions &setBufferSizeInBytes(int bufferSize); + + private: + // socket options + + bool tcpNoDelay; + + bool keepAlive; + + bool reuseAddress; + + int lingerSeconds; + + int bufferSize; + + }; + + } + } +} + +#if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) +#pragma warning(pop) +#endif + +#endif /* HAZELCAST_CLIENT_CONFIG_SOCKETOPTIONS_H_ */ diff --git a/hazelcast/include/hazelcast/client/connection/IOSelector.h b/hazelcast/include/hazelcast/client/connection/IOSelector.h index 26d4cefa3b..30bb00f903 100644 --- a/hazelcast/include/hazelcast/client/connection/IOSelector.h +++ b/hazelcast/include/hazelcast/client/connection/IOSelector.h @@ -38,6 +38,9 @@ namespace hazelcast { namespace client { class Socket; + namespace config { + class SocketOptions; + } namespace connection { class ListenerTask; @@ -47,7 +50,7 @@ namespace hazelcast { class HAZELCAST_API IOSelector : public util::Runnable { public: - IOSelector(ClientConnectionManagerImpl &connectionManager); + IOSelector(ClientConnectionManagerImpl &connectionManager, const config::SocketOptions &socketOptions); virtual ~IOSelector(); @@ -89,6 +92,7 @@ namespace hazelcast { std::auto_ptr wakeUpSocket; util::ConcurrentQueue listenerTasks; util::AtomicBoolean isAlive; + const config::SocketOptions &socketOptions; }; } } diff --git a/hazelcast/include/hazelcast/client/connection/InSelector.h b/hazelcast/include/hazelcast/client/connection/InSelector.h index 6c4865e12b..3d553385db 100644 --- a/hazelcast/include/hazelcast/client/connection/InSelector.h +++ b/hazelcast/include/hazelcast/client/connection/InSelector.h @@ -28,7 +28,7 @@ namespace hazelcast { namespace connection { class HAZELCAST_API InSelector : public IOSelector { public: - InSelector(ClientConnectionManagerImpl &connectionManager); + InSelector(ClientConnectionManagerImpl &connectionManager, const config::SocketOptions &socketOptions); void listenInternal(); diff --git a/hazelcast/include/hazelcast/client/connection/OutSelector.h b/hazelcast/include/hazelcast/client/connection/OutSelector.h index efb47ec4e8..114c9fc483 100644 --- a/hazelcast/include/hazelcast/client/connection/OutSelector.h +++ b/hazelcast/include/hazelcast/client/connection/OutSelector.h @@ -34,7 +34,7 @@ namespace hazelcast { namespace connection { class HAZELCAST_API OutSelector : public IOSelector { public: - OutSelector(ClientConnectionManagerImpl &connectionManager); + OutSelector(ClientConnectionManagerImpl &connectionManager, const config::SocketOptions &socketOptions); void listenInternal(); diff --git a/hazelcast/include/hazelcast/client/internal/socket/SSLSocket.h b/hazelcast/include/hazelcast/client/internal/socket/SSLSocket.h index b0d2334a01..1aa722f87e 100644 --- a/hazelcast/include/hazelcast/client/internal/socket/SSLSocket.h +++ b/hazelcast/include/hazelcast/client/internal/socket/SSLSocket.h @@ -23,6 +23,7 @@ #include "hazelcast/client/Socket.h" #include "hazelcast/client/Address.h" +#include "hazelcast/client/config/SocketOptions.h" #include "hazelcast/util/AtomicBoolean.h" #if !defined(MSG_NOSIGNAL) @@ -54,7 +55,8 @@ namespace hazelcast { /** * Constructor */ - SSLSocket(const client::Address &address, asio::ssl::context &sslContext); + SSLSocket(const client::Address &address, asio::ssl::context &sslContext, + client::config::SocketOptions &socketOptions); /** * Destructor @@ -107,6 +109,7 @@ namespace hazelcast { std::vector getCiphers() const; std::auto_ptr
localSocketAddress() const; + private: SSLSocket(const Socket &rhs); @@ -137,6 +140,8 @@ namespace hazelcast { void checkDeadline(const asio::error_code &ec); + void setSocketOptions(); + client::Address remoteEndpoint; asio::io_service ioService; @@ -145,6 +150,7 @@ namespace hazelcast { asio::deadline_timer deadline; asio::error_code errorCode; int socketId; + const client::config::SocketOptions &socketOptions; }; std::ostream &operator<<(std::ostream &out, const SSLSocket::CipherInfo &info); diff --git a/hazelcast/include/hazelcast/client/internal/socket/TcpSocket.h b/hazelcast/include/hazelcast/client/internal/socket/TcpSocket.h index a5acb9bc01..256b42e059 100644 --- a/hazelcast/include/hazelcast/client/internal/socket/TcpSocket.h +++ b/hazelcast/include/hazelcast/client/internal/socket/TcpSocket.h @@ -35,19 +35,21 @@ typedef int socklen_t; #include #include #include +#include #endif -#include "hazelcast/client/Socket.h" +#include +#include +#include "hazelcast/client/Socket.h" +#include "hazelcast/client/config/SocketOptions.h" #include "hazelcast/client/Address.h" #include "hazelcast/util/AtomicBoolean.h" -#include -#include #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) #pragma warning(push) -#pragma warning(disable: 4251) //for dll export +#pragma warning(disable: 4251) //for dll export #endif #if !defined(MSG_NOSIGNAL) @@ -71,7 +73,7 @@ namespace hazelcast { /** * Constructor */ - TcpSocket(const client::Address &address); + TcpSocket(const client::Address &address, const client::config::SocketOptions *socketOptions); /** * Destructor @@ -119,6 +121,7 @@ namespace hazelcast { void setBlocking(bool blocking); std::auto_ptr
localSocketAddress() const; + private: TcpSocket(const Socket &rhs); @@ -128,6 +131,8 @@ namespace hazelcast { void throwIOException(int error, const char *methodName, const char *prefix) const; + void setSocketOptions(const client::config::SocketOptions &socketOptions); + const Address configAddress; struct addrinfo *serverInfo; diff --git a/hazelcast/src/hazelcast/client/config/ClientNetworkConfig.cpp b/hazelcast/src/hazelcast/client/config/ClientNetworkConfig.cpp index 5d0b7c8575..5278ee3565 100644 --- a/hazelcast/src/hazelcast/client/config/ClientNetworkConfig.cpp +++ b/hazelcast/src/hazelcast/client/config/ClientNetworkConfig.cpp @@ -108,6 +108,10 @@ namespace hazelcast { addressList.push_back(address); return *this; } + + SocketOptions &ClientNetworkConfig::getSocketOptions() { + return socketOptions; + } } } } diff --git a/hazelcast/src/hazelcast/client/config/SocketOptions.cpp b/hazelcast/src/hazelcast/client/config/SocketOptions.cpp new file mode 100644 index 0000000000..1d78b4f857 --- /dev/null +++ b/hazelcast/src/hazelcast/client/config/SocketOptions.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2008-2018, Hazelcast, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "hazelcast/client/config/SocketOptions.h" + +namespace hazelcast { + namespace client { + namespace config { + SocketOptions::SocketOptions() : tcpNoDelay(true), keepAlive(true), reuseAddress(true), lingerSeconds(3), + bufferSize(DEFAULT_BUFFER_SIZE_BYTE) {} + + bool SocketOptions::isTcpNoDelay() const { + return tcpNoDelay; + } + + SocketOptions &SocketOptions::setTcpNoDelay(bool tcpNoDelay) { + SocketOptions::tcpNoDelay = tcpNoDelay; + return *this; + } + + bool SocketOptions::isKeepAlive() const { + return keepAlive; + } + + SocketOptions &SocketOptions::setKeepAlive(bool keepAlive) { + SocketOptions::keepAlive = keepAlive; + return *this; + } + + bool SocketOptions::isReuseAddress() const { + return reuseAddress; + } + + SocketOptions &SocketOptions::setReuseAddress(bool reuseAddress) { + SocketOptions::reuseAddress = reuseAddress; + return *this; + } + + int SocketOptions::getLingerSeconds() const { + return lingerSeconds; + } + + SocketOptions &SocketOptions::setLingerSeconds(int lingerSeconds) { + SocketOptions::lingerSeconds = lingerSeconds; + return *this; + } + + int SocketOptions::getBufferSizeInBytes() const { + return bufferSize; + } + + SocketOptions &SocketOptions::setBufferSizeInBytes(int bufferSize) { + SocketOptions::bufferSize = bufferSize; + return *this; + } + + } + } +} diff --git a/hazelcast/src/hazelcast/client/connection/ClientConnectionManagerImpl.cpp b/hazelcast/src/hazelcast/client/connection/ClientConnectionManagerImpl.cpp index 4f597aa516..b745f82c86 100644 --- a/hazelcast/src/hazelcast/client/connection/ClientConnectionManagerImpl.cpp +++ b/hazelcast/src/hazelcast/client/connection/ClientConnectionManagerImpl.cpp @@ -66,8 +66,9 @@ namespace hazelcast { const boost::shared_ptr &addressTranslator, const std::vector > &addressProviders) : logger(client.getLogger()), client(client), - socketInterceptor(client.getClientConfig().getSocketInterceptor()), inSelector(*this), - outSelector(*this), + socketInterceptor(client.getClientConfig().getSocketInterceptor()), + inSelector(*this, client.getClientConfig().getNetworkConfig().getSocketOptions()), + outSelector(*this, client.getClientConfig().getNetworkConfig().getSocketOptions()), inSelectorThread(boost::shared_ptr(new util::RunnableDelegator(inSelector)), logger), outSelectorThread(boost::shared_ptr(new util::RunnableDelegator(outSelector)), logger), executionService(client.getClientExecutionService()), diff --git a/hazelcast/src/hazelcast/client/connection/IOSelector.cpp b/hazelcast/src/hazelcast/client/connection/IOSelector.cpp index fe40cf34cf..c21972fa16 100644 --- a/hazelcast/src/hazelcast/client/connection/IOSelector.cpp +++ b/hazelcast/src/hazelcast/client/connection/IOSelector.cpp @@ -38,9 +38,9 @@ namespace hazelcast { namespace client { namespace connection { - IOSelector::IOSelector(ClientConnectionManagerImpl &connectionManager) + IOSelector::IOSelector(ClientConnectionManagerImpl &connectionManager, const config::SocketOptions &socketOptions) : socketSet(connectionManager.getLogger()), connectionManager(connectionManager), - logger(connectionManager.getLogger()) { + logger(connectionManager.getLogger()), socketOptions(socketOptions) { t.tv_sec = 5; t.tv_usec = 0; } @@ -83,7 +83,7 @@ namespace hazelcast { else localAddress = "::1"; - wakeUpSocket.reset(new internal::socket::TcpSocket(Address(localAddress, p))); + wakeUpSocket.reset(new internal::socket::TcpSocket(Address(localAddress, p), NULL)); int error = wakeUpSocket->connect(5000); if (error == 0) { sleepingSocket.reset(serverSocket.accept()); diff --git a/hazelcast/src/hazelcast/client/connection/InSelector.cpp b/hazelcast/src/hazelcast/client/connection/InSelector.cpp index b5ed915ae8..ab39cbbab8 100644 --- a/hazelcast/src/hazelcast/client/connection/InSelector.cpp +++ b/hazelcast/src/hazelcast/client/connection/InSelector.cpp @@ -33,8 +33,8 @@ namespace hazelcast { namespace client { namespace connection { - InSelector::InSelector(ClientConnectionManagerImpl& connectionManager) - : IOSelector(connectionManager) { + InSelector::InSelector(ClientConnectionManagerImpl& connectionManager, const config::SocketOptions &socketOptions) + : IOSelector(connectionManager, socketOptions) { } bool InSelector::start() { diff --git a/hazelcast/src/hazelcast/client/connection/OutSelector.cpp b/hazelcast/src/hazelcast/client/connection/OutSelector.cpp index 8297309437..170c95493d 100644 --- a/hazelcast/src/hazelcast/client/connection/OutSelector.cpp +++ b/hazelcast/src/hazelcast/client/connection/OutSelector.cpp @@ -33,9 +33,8 @@ namespace hazelcast { namespace client { namespace connection { - OutSelector::OutSelector(ClientConnectionManagerImpl &connectionManager) - :IOSelector(connectionManager), wakeUpSocketSet(connectionManager.getLogger()) { - + OutSelector::OutSelector(ClientConnectionManagerImpl &connectionManager, const config::SocketOptions &socketOptions) + :IOSelector(connectionManager, socketOptions), wakeUpSocketSet(connectionManager.getLogger()) { } bool OutSelector::start() { diff --git a/hazelcast/src/hazelcast/client/internal/socket/SSLSocket.cpp b/hazelcast/src/hazelcast/client/internal/socket/SSLSocket.cpp index b000acb4c5..509dd21aa4 100644 --- a/hazelcast/src/hazelcast/client/internal/socket/SSLSocket.cpp +++ b/hazelcast/src/hazelcast/client/internal/socket/SSLSocket.cpp @@ -38,9 +38,9 @@ namespace hazelcast { namespace client { namespace internal { namespace socket { - SSLSocket::SSLSocket(const client::Address &address, asio::ssl::context &context) - : remoteEndpoint(address), sslContext(context), - deadline(ioService), socketId(-1) { + SSLSocket::SSLSocket(const client::Address &address, asio::ssl::context &context, + client::config::SocketOptions &socketOptions) : remoteEndpoint(address), + sslContext(context), deadline(ioService), socketId(-1), socketOptions(socketOptions) { socket = std::auto_ptr >( new asio::ssl::stream(ioService, sslContext)); } @@ -136,12 +136,7 @@ namespace hazelcast { socket->handshake(asio::ssl::stream::client); - int size = 32 * 1024; - socket->lowest_layer().set_option(asio::socket_base::receive_buffer_size(size)); - socket->lowest_layer().set_option(asio::socket_base::send_buffer_size(size)); - - // SO_NOSIGPIPE seems to be internally handled by asio on connect and accept. no such option - // is defined at the api, hence not setting this option + setSocketOptions(); setBlocking(false); socketId = socket->lowest_layer().native_handle(); @@ -243,6 +238,31 @@ namespace hazelcast { return (int) numBytes; } + void SSLSocket::setSocketOptions() { + asio::basic_socket > &lowestLayer = + socket->lowest_layer(); + + lowestLayer.set_option(asio::ip::tcp::no_delay(socketOptions.isTcpNoDelay())); + + lowestLayer.set_option(asio::socket_base::keep_alive(socketOptions.isKeepAlive())); + + lowestLayer.set_option(asio::socket_base::reuse_address(socketOptions.isReuseAddress())); + + int lingerSeconds = socketOptions.getLingerSeconds(); + if (lingerSeconds > 0) { + lowestLayer.set_option(asio::socket_base::linger(true, lingerSeconds)); + } + + int bufferSize = socketOptions.getBufferSizeInBytes(); + if (bufferSize > 0) { + lowestLayer.set_option(asio::socket_base::receive_buffer_size(bufferSize)); + lowestLayer.set_option(asio::socket_base::send_buffer_size(bufferSize)); + } + + // SO_NOSIGPIPE seems to be internally handled by asio on connect and accept. no such option + // is defined at the api, hence not setting this option + } + SSLSocket::ReadHandler::ReadHandler(size_t &numRead, asio::error_code &ec) : numRead(numRead), errorCode(ec) {} diff --git a/hazelcast/src/hazelcast/client/internal/socket/SocketFactory.cpp b/hazelcast/src/hazelcast/client/internal/socket/SocketFactory.cpp index 228672fb06..4b996c2197 100644 --- a/hazelcast/src/hazelcast/client/internal/socket/SocketFactory.cpp +++ b/hazelcast/src/hazelcast/client/internal/socket/SocketFactory.cpp @@ -85,17 +85,20 @@ namespace hazelcast { #else (void) clientContext; #endif - + return true; } std::auto_ptr SocketFactory::create(const Address &address) const { #ifdef HZ_BUILD_WITH_SSL if (sslContext.get()) { - return std::auto_ptr(new internal::socket::SSLSocket(address, *sslContext)); + return std::auto_ptr(new internal::socket::SSLSocket(address, *sslContext, + clientContext.getClientConfig().getNetworkConfig().getSocketOptions())); } #endif - return std::auto_ptr(new internal::socket::TcpSocket(address)); + + return std::auto_ptr(new internal::socket::TcpSocket(address, + &clientContext.getClientConfig().getNetworkConfig().getSocketOptions())); } } } diff --git a/hazelcast/src/hazelcast/client/internal/socket/TcpSocket.cpp b/hazelcast/src/hazelcast/client/internal/socket/TcpSocket.cpp index 4346b8ca58..cc0ca685bd 100644 --- a/hazelcast/src/hazelcast/client/internal/socket/TcpSocket.cpp +++ b/hazelcast/src/hazelcast/client/internal/socket/TcpSocket.cpp @@ -32,9 +32,10 @@ namespace hazelcast { namespace client { namespace internal { namespace socket { - TcpSocket::TcpSocket(const client::Address &address) : configAddress(address) { + TcpSocket::TcpSocket(const client::Address &address, const client::config::SocketOptions *socketOptions) + : configAddress(address) { #if defined(WIN32) || defined(_WIN32) || defined(WIN64) || defined(_WIN64) - int n= WSAStartup(MAKEWORD(2, 0), &wsa_data); + int n = WSAStartup(MAKEWORD(2, 0), &wsa_data); if(n == -1) throw exception::IOException("TcpSocket::TcpSocket ", "WSAStartup error"); #endif struct addrinfo hints; @@ -57,20 +58,12 @@ namespace hazelcast { if (-1 == socketId) { throwIOException("TcpSocket", "[TcpSocket::TcpSocket] Failed to obtain socket."); } - isOpen = true; - int size = 32 * 1024; - if (::setsockopt(socketId, SOL_SOCKET, SO_RCVBUF, (char *) &size, sizeof(size))) { - throwIOException("Socket", "Failed set socket receive buffer size."); - } - if (::setsockopt(socketId, SOL_SOCKET, SO_SNDBUF, (char *) &size, sizeof(size))) { - throwIOException("TcpSocket", "Failed set socket send buffer size."); - } - #if defined(SO_NOSIGPIPE) - int on = 1; - if (setsockopt(socketId, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(int))) { - throwIOException("TcpSocket", "Failed set socket option SO_NOSIGPIPE."); + + if (socketOptions) { + setSocketOptions(*socketOptions); } - #endif + + isOpen = true; } TcpSocket::TcpSocket(int socketId) @@ -296,6 +289,50 @@ namespace hazelcast { return std::auto_ptr
(); } } + + void TcpSocket::setSocketOptions(const client::config::SocketOptions &socketOptions) { + int optionValue = socketOptions.getBufferSizeInBytes(); + if (::setsockopt(socketId, SOL_SOCKET, SO_RCVBUF, (char *) &optionValue, sizeof(optionValue))) { + throwIOException("setSocketOptions", "Failed to set socket receive buffer size."); + } + if (::setsockopt(socketId, SOL_SOCKET, SO_SNDBUF, (char *) &optionValue, sizeof(optionValue))) { + throwIOException("setSocketOptions", "Failed to set socket send buffer size."); + } + + optionValue = socketOptions.isTcpNoDelay(); + if (::setsockopt(socketId, IPPROTO_TCP, TCP_NODELAY, (char *) &optionValue, sizeof(optionValue))) { + throwIOException("setSocketOptions", "Failed to set TCP_NODELAY option on the socket."); + } + + optionValue = socketOptions.isKeepAlive(); + if (::setsockopt(socketId, SOL_SOCKET, SO_KEEPALIVE, (char *) &optionValue, sizeof(optionValue))) { + throwIOException("setSocketOptions", "Failed to set SO_KEEPALIVE option on the socket."); + } + + optionValue = socketOptions.isReuseAddress(); + if (::setsockopt(socketId, SOL_SOCKET, SO_REUSEADDR, (char *) &optionValue, sizeof(optionValue))) { + throwIOException("setSocketOptions", "Failed to set SO_REUSEADDR option on the socket."); + } + + optionValue = socketOptions.getLingerSeconds(); + if (optionValue > 0) { + struct linger so_linger; + so_linger.l_onoff = 1; + so_linger.l_linger = optionValue; + + if (::setsockopt(socketId, SOL_SOCKET, SO_LINGER, (char *) &so_linger, sizeof(so_linger))) { + throwIOException("setSocketOptions", "Failed to set SO_LINGER option on the socket."); + } + } + + #if defined(SO_NOSIGPIPE) + optionValue = 1; + if (setsockopt(socketId, SOL_SOCKET, SO_NOSIGPIPE, (char *) &optionValue, sizeof(optionValue))) { + throwIOException("TcpSocket", "Failed to set socket option SO_NOSIGPIPE."); + } + #endif + + } } } } diff --git a/hazelcast/test/src/cluster/SocketOptionsTest.cpp b/hazelcast/test/src/cluster/SocketOptionsTest.cpp new file mode 100644 index 0000000000..f602002983 --- /dev/null +++ b/hazelcast/test/src/cluster/SocketOptionsTest.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2008-2018, Hazelcast, Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * This has to be the first include, so that Python.h is the first include. Otherwise, compilation warning such as + * "_POSIX_C_SOURCE" redefined occurs. + */ +#include "HazelcastServerFactory.h" + +#include "ClientTestSupport.h" +#include "HazelcastServer.h" +#include "hazelcast/client/HazelcastClient.h" + +namespace hazelcast { + namespace client { + namespace test { + class SocketOptionsTest : public ClientTestSupport { + }; + + TEST_F(SocketOptionsTest, testConfiguration) { + HazelcastServer instance(*g_srvFactory); + + const int bufferSize = 2 * 1024; + ClientConfig clientConfig; + clientConfig.getNetworkConfig().getSocketOptions().setKeepAlive(false).setReuseAddress( + true).setTcpNoDelay(false).setLingerSeconds(5).setBufferSizeInBytes(bufferSize); + + HazelcastClient client(clientConfig); + + config::SocketOptions &socketOptions = client.getClientConfig().getNetworkConfig().getSocketOptions(); + ASSERT_FALSE(socketOptions.isKeepAlive()); + ASSERT_FALSE(socketOptions.isTcpNoDelay()); + ASSERT_EQ(5, socketOptions.getLingerSeconds()); + ASSERT_EQ(bufferSize, socketOptions.getBufferSizeInBytes()); + } + } + } +}