Files
sm-ext-socket/Socket.cpp
T

768 lines
24 KiB
C++

#include "Socket.h"
#include <assert.h>
#include <cstdio>
#include <exception>
#include <boost/bind.hpp>
#include "Callback.h"
#include "CallbackHandler.h"
#include "SocketHandler.h"
using namespace boost::asio::ip;
template <class SocketType>
Socket<SocketType>::Socket(SM_SocketType st,
typename SocketType::socket* asioSocket) : connectCallback(NULL),
incomingCallback(NULL),
receiveCallback(NULL),
sendqueueEmptyCallback(NULL),
disconnectCallback(NULL),
errorCallback(NULL),
smCallbackArg(0),
sendQueueLength(0),
sm_sockettype(st),
socket(NULL),
localEndpoint(NULL),
localEndpointMutex(NULL),
tcpAcceptor(NULL),
tcpAcceptorMutex(NULL) {
if (asioSocket != NULL) {
socket = asioSocket;
}
}
template <class SocketType>
Socket<SocketType>::~Socket() {
if (socket) {
boost::mutex::scoped_lock l(socketMutex);
socket->close();
delete socket;
socket = NULL;
}
if (tcpAcceptor) {
boost::mutex::scoped_lock l(*tcpAcceptorMutex);
tcpAcceptor->close();
delete tcpAcceptor;
tcpAcceptor = NULL;
}
if (localEndpoint) {
boost::mutex::scoped_lock l(*localEndpointMutex);
delete localEndpoint;
localEndpoint = NULL;
}
// wait for all callbacks to terminate
//boost::unique_lock<boost::shared_mutex> l(handlerMutex);
handlerMutex.lock();
boost::mutex::scoped_lock socketLock(socketMutex);
if (tcpAcceptorMutex) delete tcpAcceptorMutex;
if (localEndpointMutex) delete localEndpointMutex;
while (!socketOptionQueue.empty()) {
delete socketOptionQueue.front();
socketOptionQueue.pop();
}
}
template <class SocketType>
void Socket<SocketType>::ReceiveHandler(char* buf, size_t bufferSize, size_t bytesTransferred, const boost::system::error_code& errorCode, boost::shared_lock<boost::shared_mutex>* handlerLock) {
if (!errorCode) {
boost::mutex::scoped_lock l(socketMutex);
if (socket) {
if (bytesTransferred) callbackHandler.AddCallback(new Callback(CallbackEvent_Receive, this, buf, bytesTransferred));
socket->async_receive(boost::asio::buffer(buf, bufferSize),
boost::bind(&Socket<SocketType>::ReceiveHandler,
this,
buf,
bufferSize,
boost::asio::placeholders::bytes_transferred,
boost::asio::placeholders::error,
handlerLock));
return;
}
}
if (errorCode) {
if (errorCode == boost::asio::error::eof ||
errorCode == boost::asio::error::connection_reset ||
errorCode == boost::asio::error::connection_aborted) {
// asio indicates disconnect
boost::mutex::scoped_lock l(socketMutex);
if (socket) callbackHandler.AddCallback(new Callback(CallbackEvent_Disconnect, this));
} else if (errorCode != boost::asio::error::operation_aborted) {
// error
boost::mutex::scoped_lock l(socketMutex);
if (socket) callbackHandler.AddCallback(new Callback(CallbackEvent_Error, this, SM_ErrorType_RECV_ERROR, errorCode.value()));
}
}
delete[] buf;
delete handlerLock;
}
template <class SocketType>
bool Socket<SocketType>::IsOpen() {
boost::mutex::scoped_lock l(socketMutex);
return (socket && socket->is_open());
}
template <class SocketType>
bool Socket<SocketType>::Bind(const char* hostname, uint16_t port, bool async) {
typename SocketType::resolver* resolver = NULL;
boost::shared_lock<boost::shared_mutex>* handlerLock = NULL;
try {
if (localEndpoint) {
// TODO: make sure endpoint is not in use
//localEndpointInitialized = false;
//delete localEndpoint;
return false;
}
char sPort[6];
snprintf(sPort, sizeof(sPort), "%hu", port);
if (async) {
resolver = new typename SocketType::resolver(*socketHandler.ioService);
handlerLock = new boost::shared_lock<boost::shared_mutex>(handlerMutex);
resolver->async_resolve(typename SocketType::resolver::query(SocketType::v4(), hostname, sPort),
boost::bind(&Socket<SocketType>::BindPostResolveHandler,
this,
resolver,
boost::asio::placeholders::iterator,
boost::asio::placeholders::error,
handlerLock));
} else {
typename SocketType::resolver syncResolver(*socketHandler.ioService);
typename SocketType::resolver::iterator endpointIterator = syncResolver.resolve(typename SocketType::resolver::query(SocketType::v4(), hostname, sPort));
if (!localEndpoint) {
localEndpointMutex = new boost::mutex();
boost::mutex::scoped_lock l(*localEndpointMutex);
localEndpoint = new typename SocketType::endpoint(endpointIterator->endpoint());
}
}
return true;
} catch (std::exception&) {
if (resolver) delete resolver;
if (handlerLock) delete handlerLock;
}
return false;
}
template <class SocketType>
void Socket<SocketType>::BindPostResolveHandler(typename SocketType::resolver* resolver, typename SocketType::resolver::iterator endpointIterator, const boost::system::error_code& errorCode, boost::shared_lock<boost::shared_mutex>* handlerLock) {
if (!errorCode) {
if (!localEndpoint) {
localEndpointMutex = new boost::mutex();
boost::mutex::scoped_lock l(*localEndpointMutex);
localEndpoint = new typename SocketType::endpoint(*endpointIterator);
}
} else if (errorCode != boost::asio::error::operation_aborted) {
callbackHandler.AddCallback(new Callback(CallbackEvent_Error, this, SM_ErrorType_BIND_ERROR, errorCode.value()));
}
delete resolver;
delete handlerLock;
}
template <class SocketType>
bool Socket<SocketType>::Connect(const char* hostname, uint16_t port, bool async) {
typename SocketType::resolver* resolver = NULL;
boost::shared_lock<boost::shared_mutex>* handlerLock = NULL;
try {
char sPort[6];
snprintf(sPort, sizeof(sPort), "%hu", port);
if (!socket) InitializeSocket();
if (async) {
resolver = new typename SocketType::resolver(*socketHandler.ioService);
handlerLock = new boost::shared_lock<boost::shared_mutex>(handlerMutex);
resolver->async_resolve(typename SocketType::resolver::query(SocketType::v4(), hostname, sPort),
boost::bind(&Socket<SocketType>::ConnectPostResolveHandler,
this,
resolver,
boost::asio::placeholders::iterator,
boost::asio::placeholders::error,
handlerLock));
} else {
typename SocketType::resolver syncResolver(*socketHandler.ioService);
typename SocketType::resolver::iterator endpointIterator = syncResolver.resolve(typename SocketType::resolver::query(SocketType::v4(), hostname, sPort));
boost::system::error_code error = boost::asio::error::host_not_found;
typename SocketType::resolver::iterator end;
while (error && endpointIterator != end) {
boost::mutex::scoped_lock l(socketMutex);
if (socket) {
socket->connect(*endpointIterator++, error);
if (error) socket->close();
} else {
throw std::logic_error("Operation cancelled.");
}
}
if (error) throw boost::system::system_error(error);
ReceiveHandler(new char[16384], 16384, 0, boost::system::errc::make_error_code(boost::system::errc::success), new boost::shared_lock<boost::shared_mutex>(handlerMutex));
}
return true;
} catch (std::exception&) {
if (resolver) delete resolver;
if (handlerLock) delete handlerLock;
}
return false;
}
template <class SocketType>
void Socket<SocketType>::ConnectPostResolveHandler(typename SocketType::resolver* resolver, typename SocketType::resolver::iterator endpointIterator, const boost::system::error_code& errorCode, boost::shared_lock<boost::shared_mutex>* handlerLock) {
if (!errorCode) {
typename SocketType::endpoint endpoint = *endpointIterator;
boost::mutex::scoped_lock l(socketMutex);
if (socket) {
socket->async_connect(endpoint,
boost::bind(&Socket<SocketType>::ConnectPostConnectHandler,
this,
resolver,
++endpointIterator,
boost::asio::placeholders::error,
handlerLock));
return;
}
}
if (errorCode && errorCode != boost::asio::error::operation_aborted) {
boost::mutex::scoped_lock l(socketMutex);
if (socket) callbackHandler.AddCallback(new Callback(CallbackEvent_Error, this, SM_ErrorType_CONNECT_ERROR, errorCode.value()));
}
delete resolver;
delete handlerLock;
}
template <class SocketType>
void Socket<SocketType>::ConnectPostConnectHandler(typename SocketType::resolver* resolver, typename SocketType::resolver::iterator endpointIterator, const boost::system::error_code& errorCode, boost::shared_lock<boost::shared_mutex>* handlerLock) {
if (!errorCode) {
{ // lock
boost::mutex::scoped_lock l(socketMutex);
if (socket) {
callbackHandler.AddCallback(new Callback(CallbackEvent_Connect, this));
}
} // ~lock
ReceiveHandler(new char[16384], 16384, 0, boost::system::errc::make_error_code(boost::system::errc::success), handlerLock);
delete resolver;
return;
} else if (endpointIterator != typename SocketType::resolver::iterator()) {
{ // lock
boost::mutex::scoped_lock l(socketMutex);
if (socket) {
socket->close();
}
} // ~lock
ConnectPostResolveHandler(resolver, endpointIterator, boost::system::errc::make_error_code(boost::system::errc::success), handlerLock);
return;
}
if (errorCode && errorCode != boost::asio::error::operation_aborted) {
boost::mutex::scoped_lock l(socketMutex);
if (socket) callbackHandler.AddCallback(new Callback(CallbackEvent_Error, this, SM_ErrorType_CONNECT_ERROR, errorCode.value()));
}
delete resolver;
delete handlerLock;
}
template <class SocketType>
bool Socket<SocketType>::Disconnect() {
boost::mutex::scoped_lock l(socketMutex);
if (!socket) return false;
try {
socket->close();
return true;
} catch (std::exception&) {
}
return false;
}
template <class SocketType>
bool Socket<SocketType>::Listen() {
return false;
}
template<> void Socket<tcp>::ListenIncomingHandler(tcp::socket* newAsioSocket, const boost::system::error_code& errorCode, boost::shared_lock<boost::shared_mutex>* handlerLock);
template<>
bool Socket<tcp>::Listen() {
boost::shared_lock<boost::shared_mutex>* handlerLock = NULL;
tcp::socket* nextAsioSocket = NULL;
try {
if (!localEndpoint) throw std::logic_error("local endpoint not initialized, call bind() first");
if (!tcpAcceptor) {
tcpAcceptorMutex = new boost::mutex();
boost::mutex::scoped_lock tcpAcceptorLock(*tcpAcceptorMutex);
boost::mutex::scoped_lock locelEndpointLock(*localEndpointMutex);
tcpAcceptor = new tcp::acceptor(*socketHandler.ioService, *localEndpoint);
while (!socketOptionQueue.empty()) {
SetOption(socketOptionQueue.front()->option, socketOptionQueue.front()->value, false);
delete socketOptionQueue.front();
socketOptionQueue.pop();
}
}
boost::mutex::scoped_lock l(*tcpAcceptorMutex);
handlerLock = new boost::shared_lock<boost::shared_mutex>(handlerMutex);
nextAsioSocket = new tcp::socket(*socketHandler.ioService);
tcpAcceptor->async_accept(*nextAsioSocket,
boost::bind(&Socket<tcp>::ListenIncomingHandler,
this,
nextAsioSocket,
boost::asio::placeholders::error,
handlerLock));
return true;
} catch (std::exception&) {
if (handlerLock) delete handlerLock;
if (nextAsioSocket) delete nextAsioSocket;
}
return false;
}
template <class SocketType>
void Socket<SocketType>::ListenIncomingHandler(tcp::socket* newAsioSocket, const boost::system::error_code& errorCode, boost::shared_lock<boost::shared_mutex>* handlerLock) {
// invalid
}
template<>
void Socket<tcp>::ListenIncomingHandler(tcp::socket* newAsioSocket, const boost::system::error_code& errorCode, boost::shared_lock<boost::shared_mutex>* handlerLock) {
if (!errorCode) {
boost::mutex::scoped_lock l(*tcpAcceptorMutex);
if (tcpAcceptor) {
Socket<tcp>* newSocket = socketHandler.CreateSocket<tcp>(sm_sockettype);
newSocket->socket = newAsioSocket;
callbackHandler.AddCallback(new Callback(CallbackEvent_Incoming, this, newSocket, newAsioSocket->remote_endpoint()));
newSocket->ReceiveHandler(new char[16384], 16384, 0, boost::system::errc::make_error_code(boost::system::errc::success), new boost::shared_lock<boost::shared_mutex>(newSocket->handlerMutex));
tcp::socket* nextAsioSocket = new tcp::socket(*socketHandler.ioService);
tcpAcceptor->async_accept(*nextAsioSocket,
boost::bind(&Socket<tcp>::ListenIncomingHandler,
this,
nextAsioSocket,
boost::asio::placeholders::error,
handlerLock));
return;
}
}
if (errorCode && errorCode != boost::asio::error::operation_aborted) {
callbackHandler.AddCallback(new Callback(CallbackEvent_Error, this, SM_ErrorType_LISTEN_ERROR, errorCode.value()));
}
delete newAsioSocket;
delete handlerLock;
}
template <class SocketType>
bool Socket<SocketType>::Send(const std::string& data, bool async) {
char* buf = NULL;
boost::shared_lock<boost::shared_mutex>* handlerLock = NULL;
try {
if (!socket && !tcpAcceptor) throw std::logic_error("can't send without connection");
if (async) {
char* buf = new char[data.length()];
memcpy(buf, data.data(), data.length());
sendQueueLength++;
handlerLock = new boost::shared_lock<boost::shared_mutex>(handlerMutex);
boost::mutex::scoped_lock l(socketMutex);
if (socket) {
socket->async_send(boost::asio::buffer(buf, data.length()),
boost::bind(&Socket<SocketType>::SendPostSendHandler,
this,
buf,
boost::asio::placeholders::bytes_transferred,
boost::asio::placeholders::error,
handlerLock));
} else {
throw new std::logic_error("Operation cancelled.");
}
} else {
boost::mutex::scoped_lock l(socketMutex);
if (socket) {
socket->send(boost::asio::buffer(data, data.length()));
} else {
throw std::logic_error("Operation cancelled.");
}
}
return true;
} catch (std::exception&) {
if (buf) delete[] buf;
if (handlerLock) delete handlerLock;
}
return false;
}
template <class SocketType>
void Socket<SocketType>::SendPostSendHandler(char* buf, size_t bytesTransferred, const boost::system::error_code& errorCode, boost::shared_lock<boost::shared_mutex>* handlerLock) {
// TODO: handle incomplete sends
if (--sendQueueLength == 0 && sendqueueEmptyCallback) {
boost::mutex::scoped_lock l(socketMutex);
if (socket) callbackHandler.AddCallback(new Callback(CallbackEvent_SendQueueEmpty, this));
}
if (errorCode && errorCode != boost::asio::error::operation_aborted) {
boost::mutex::scoped_lock l(socketMutex);
if (socket) callbackHandler.AddCallback(new Callback(CallbackEvent_Error, this, SM_ErrorType_SEND_ERROR, errorCode.value()));
}
delete[] buf;
delete handlerLock;
}
template <class SocketType>
bool Socket<SocketType>::SendTo(const std::string& data, const char* hostname, uint16_t port, bool async) {
return false;
}
template <>
bool Socket<udp>::SendTo(const std::string& data, const char* hostname, uint16_t port, bool async) {
char* buf = NULL;
udp::resolver* resolver = NULL;
boost::shared_lock<boost::shared_mutex>* handlerLock = NULL;
try {
char sPort[6];
snprintf(sPort, sizeof(sPort), "%hu", port);
if (!socket) InitializeSocket();
if (async) {
buf = new char[data.length()];
memcpy(buf, data.data(), data.length());
sendQueueLength++;
resolver = new udp::resolver(*socketHandler.ioService);
handlerLock = new boost::shared_lock<boost::shared_mutex>(handlerMutex);
resolver->async_resolve(udp::resolver::query(udp::v4(), hostname, sPort),
boost::bind(&Socket<udp>::SendToPostResolveHandler,
this,
resolver,
boost::asio::placeholders::iterator,
buf,
data.length(),
boost::asio::placeholders::error,
handlerLock));
} else {
udp::resolver syncResolver(*socketHandler.ioService);
udp::resolver::iterator endpointIterator = syncResolver.resolve(udp::resolver::query(udp::v4(), hostname, sPort));
boost::mutex::scoped_lock l(socketMutex);
if (socket) {
socket->send_to(boost::asio::buffer(data, data.length()), *endpointIterator);
} else {
throw std::logic_error("Operation cancelled.");
}
}
return true;
} catch (std::exception&) {
if (resolver) delete resolver;
if (buf) delete[] buf;
if (handlerLock) delete handlerLock;
}
return false;
}
template <class SocketType>
void Socket<SocketType>::SendToPostResolveHandler(typename SocketType::resolver* resolver, typename SocketType::resolver::iterator endpointIterator, char* buf, size_t bufLen, const boost::system::error_code& errorCode, boost::shared_lock<boost::shared_mutex>* handlerLock) {
if (!errorCode) {
typename SocketType::endpoint endpoint = *endpointIterator;
boost::mutex::scoped_lock l(socketMutex);
if (socket) {
socket->async_send_to(boost::asio::buffer(buf, bufLen),
endpoint,
boost::bind(&Socket<SocketType>::SendToPostSendHandler,
this,
resolver,
++endpointIterator,
buf,
bufLen,
boost::asio::placeholders::bytes_transferred,
boost::asio::placeholders::error,
handlerLock));
return;
}
}
if (errorCode && errorCode != boost::asio::error::operation_aborted) {
boost::mutex::scoped_lock l(socketMutex);
if (socket) callbackHandler.AddCallback(new Callback(CallbackEvent_Error, this, SM_ErrorType_NO_HOST, errorCode.value()));
}
delete resolver;
delete[] buf;
delete handlerLock;
}
template <class SocketType>
void Socket<SocketType>::SendToPostSendHandler(typename SocketType::resolver* resolver, typename SocketType::resolver::iterator endpointIterator, char* buf, size_t bufLen, size_t bytesTransferred, const boost::system::error_code& errorCode, boost::shared_lock<boost::shared_mutex>* handlerLock) {
if (!errorCode) {
if (--sendQueueLength == 0 && sendqueueEmptyCallback) {
boost::mutex::scoped_lock l(socketMutex);
if (socket) callbackHandler.AddCallback(new Callback(CallbackEvent_SendQueueEmpty, this));
}
} else if (endpointIterator != typename SocketType::resolver::iterator()) {
SendToPostResolveHandler(resolver, endpointIterator, buf, bufLen, boost::system::errc::make_error_code(boost::system::errc::success), handlerLock);
return;
} else {
if (errorCode != boost::asio::error::operation_aborted) {
boost::mutex::scoped_lock l(socketMutex);
if (socket) callbackHandler.AddCallback(new Callback(CallbackEvent_Error, this, SM_ErrorType_SEND_ERROR, errorCode.value()));
}
}
delete resolver;
delete[] buf;
delete handlerLock;
}
template <class SocketType>
bool Socket<SocketType>::SetOption(SM_SocketOption so, int value, bool lock) {
boost::mutex::scoped_lock* l = NULL;
try {
if (socket) {
if (lock) l = new boost::mutex::scoped_lock(socketMutex);
if (!socket) return false;
switch (so) {
case SM_SO_SocketBroadcast:
socket->set_option(boost::asio::socket_base::broadcast(value!=0));
break;
case SM_SO_SocketReuseAddr:
socket->set_option(boost::asio::socket_base::reuse_address(value!=0));
break;
case SM_SO_SocketKeepAlive:
socket->set_option(boost::asio::socket_base::keep_alive(value!=0));
break;
case SM_SO_SocketLinger:
socket->set_option(boost::asio::socket_base::linger(value>0, value));
break;
case SM_SO_SocketOOBInline:
// TODO: implement?
if (l) delete l;
return false;
case SM_SO_SocketSendBuffer:
socket->set_option(boost::asio::socket_base::send_buffer_size(value));
break;
case SM_SO_SocketReceiveBuffer:
socket->set_option(boost::asio::socket_base::receive_buffer_size(value));
break;
case SM_SO_SocketDontRoute:
socket->set_option(boost::asio::socket_base::do_not_route(value!=0));
break;
case SM_SO_SocketReceiveLowWatermark:
socket->set_option(boost::asio::socket_base::receive_low_watermark(value));
break;
case SM_SO_SocketReceiveTimeout:
// TODO: implement?
if (l) delete l;
return false;
case SM_SO_SocketSendLowWatermark:
socket->set_option(boost::asio::socket_base::send_low_watermark(value));
break;
case SM_SO_SocketSendTimeout:
// TODO: implement?
if (l) delete l;
return false;
default:
if (l) delete l;
return false;
}
} else if (tcpAcceptor) {
if (lock) l = new boost::mutex::scoped_lock(*tcpAcceptorMutex);
if (!tcpAcceptor) return false;
switch (so) {
case SM_SO_SocketBroadcast:
tcpAcceptor->set_option(boost::asio::socket_base::broadcast(value!=0));
break;
case SM_SO_SocketReuseAddr:
tcpAcceptor->set_option(boost::asio::socket_base::reuse_address(value!=0));
break;
case SM_SO_SocketKeepAlive:
tcpAcceptor->set_option(boost::asio::socket_base::keep_alive(value!=0));
break;
case SM_SO_SocketLinger:
tcpAcceptor->set_option(boost::asio::socket_base::linger(value>0, value));
break;
case SM_SO_SocketOOBInline:
// TODO: implement?
if (l) delete l;
return false;
case SM_SO_SocketSendBuffer:
tcpAcceptor->set_option(boost::asio::socket_base::send_buffer_size(value));
break;
case SM_SO_SocketReceiveBuffer:
tcpAcceptor->set_option(boost::asio::socket_base::receive_buffer_size(value));
break;
case SM_SO_SocketDontRoute:
tcpAcceptor->set_option(boost::asio::socket_base::do_not_route(value!=0));
break;
case SM_SO_SocketReceiveLowWatermark:
tcpAcceptor->set_option(boost::asio::socket_base::receive_low_watermark(value));
break;
case SM_SO_SocketReceiveTimeout:
// TODO: implement?
if (l) delete l;
return false;
case SM_SO_SocketSendLowWatermark:
tcpAcceptor->set_option(boost::asio::socket_base::send_low_watermark(value));
break;
case SM_SO_SocketSendTimeout:
// TODO: implement?
if (l) delete l;
return false;
default:
if (l) delete l;
return false;
}
} else {
socketOptionQueue.push(new SocketOption(so, value));
}
if (l) delete l;
return true;
} catch (std::exception&) {
if (l) delete l;
return false;
}
}
template <class SocketType>
void Socket<SocketType>::InitializeSocket() {
assert(!socket);
boost::mutex::scoped_lock l(socketMutex);
if (!socket) {
if (localEndpointMutex) {
boost::mutex::scoped_lock l(*localEndpointMutex);
if (localEndpoint) {
socket = new typename SocketType::socket(*socketHandler.ioService, *localEndpoint);
} else {
socket = new typename SocketType::socket(*socketHandler.ioService, typename SocketType::endpoint(SocketType::v4(), 0));
}
} else {
socket = new typename SocketType::socket(*socketHandler.ioService);
}
if (!socket->is_open()) socket->open(SocketType::v4());
while (!socketOptionQueue.empty()) {
SetOption(socketOptionQueue.front()->option, socketOptionQueue.front()->value, false);
delete socketOptionQueue.front();
socketOptionQueue.pop();
}
}
}
template Socket<tcp>::Socket(SM_SocketType, tcp::socket*);
template Socket<tcp>::~Socket();
template bool Socket<tcp>::IsOpen();
template bool Socket<tcp>::Bind(const char*, uint16_t, bool);
template bool Socket<tcp>::Connect(const char*, uint16_t, bool);
template bool Socket<tcp>::Disconnect();
template bool Socket<tcp>::Send(const std::string&, bool);
template bool Socket<tcp>::SendTo(const std::string&, const char*, uint16_t, bool);
template bool Socket<tcp>::SetOption(SM_SocketOption, int, bool);
template Socket<udp>::Socket(SM_SocketType, udp::socket*);
template Socket<udp>::~Socket();
template bool Socket<udp>::IsOpen();
template bool Socket<udp>::Bind(const char*, uint16_t, bool);
template bool Socket<udp>::Connect(const char*, uint16_t, bool);
template bool Socket<udp>::Disconnect();
template bool Socket<udp>::Listen();
template bool Socket<udp>::Send(const std::string&, bool);
template bool Socket<udp>::SetOption(SM_SocketOption, int, bool);