// ********************************************************************** // // Copyright (c) 2003-2010 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #include #include #include #include #include #include #include #include #include #ifdef ICE_USE_IOCP # include #endif using namespace std; using namespace Ice; using namespace IceInternal; NativeInfoPtr IceInternal::TcpAcceptor::getNativeInfo() { return this; } #ifdef ICE_USE_IOCP AsyncInfo* IceInternal::TcpAcceptor::getAsyncInfo(SocketOperation op) { assert(op == SocketOperationRead); return &_info; } #endif void IceInternal::TcpAcceptor::close() { if(_traceLevels->network >= 1) { Trace out(_logger, _traceLevels->networkCat); out << "stopping to accept tcp connections at " << toString(); } SOCKET fd = _fd; _fd = INVALID_SOCKET; closeSocket(fd); } void IceInternal::TcpAcceptor::listen() { try { doListen(_fd, _backlog); } catch(...) { _fd = INVALID_SOCKET; throw; } if(_traceLevels->network >= 1) { Trace out(_logger, _traceLevels->networkCat); out << "accepting tcp connections at " << toString(); vector interfaces = getHostsForEndpointExpand(inetAddrToString(_addr), _instance->protocolSupport(), true); if(!interfaces.empty()) { out << "\nlocal interfaces: "; out << IceUtilInternal::joinString(interfaces, ", "); } } } #ifdef ICE_USE_IOCP void IceInternal::TcpAcceptor::startAccept() { LPFN_ACCEPTEX AcceptEx = NULL; // a pointer to the 'AcceptEx()' function GUID GuidAcceptEx = WSAID_ACCEPTEX; // The Guid DWORD dwBytes; if(WSAIoctl(_fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GuidAcceptEx), &AcceptEx, sizeof(AcceptEx), &dwBytes, NULL, NULL) == SOCKET_ERROR) { SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } assert(_acceptFd == INVALID_SOCKET); _acceptFd = createSocket(false, _addr.ss_family); const int sz = static_cast(_acceptBuf.size() / 2); if(!AcceptEx(_fd, _acceptFd, &_acceptBuf[0], 0, sz, sz, &_info.count, #if defined(_MSC_VER) && (_MSC_VER < 1300) // COMPILER FIX: VC60 reinterpret_cast(&_info) #else &_info #endif )) { if(!wouldBlock()) { SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } } } void IceInternal::TcpAcceptor::finishAccept() { if(_info.count == SOCKET_ERROR || _fd == INVALID_SOCKET) { closeSocketNoThrow(_acceptFd); _acceptFd = INVALID_SOCKET; _acceptError = _info.error; } } #endif TransceiverPtr IceInternal::TcpAcceptor::accept() { SOCKET fd; #ifndef ICE_USE_IOCP fd = doAccept(_fd); #else if(_acceptFd == INVALID_SOCKET) { SocketException ex(__FILE__, __LINE__); ex.error = _acceptError; throw ex; } if(setsockopt(_acceptFd, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char*)&_acceptFd, sizeof(_acceptFd)) == SOCKET_ERROR) { closeSocketNoThrow(_acceptFd); _acceptFd = INVALID_SOCKET; SocketException ex(__FILE__, __LINE__); ex.error = getSocketErrno(); throw ex; } fd = _acceptFd; _acceptFd = INVALID_SOCKET; #endif if(_traceLevels->network >= 1) { Trace out(_logger, _traceLevels->networkCat); out << "accepted tcp connection\n" << fdToString(fd); } return new TcpTransceiver(_instance, fd, true); } string IceInternal::TcpAcceptor::toString() const { return addrToString(_addr); } int IceInternal::TcpAcceptor::effectivePort() const { return getPort(_addr); } IceInternal::TcpAcceptor::TcpAcceptor(const InstancePtr& instance, const string& host, int port, ProtocolSupport protocol) : _instance(instance), _traceLevels(instance->traceLevels()), _logger(instance->initializationData().logger), _addr(getAddressForServer(host, port, protocol)) #ifdef ICE_USE_IOCP , _acceptFd(INVALID_SOCKET), _info(SocketOperationRead) #endif { #ifdef SOMAXCONN _backlog = instance->initializationData().properties->getPropertyAsIntWithDefault("Ice.TCP.Backlog", SOMAXCONN); #else _backlog = instance->initializationData().properties->getPropertyAsIntWithDefault("Ice.TCP.Backlog", 511); #endif _fd = createSocket(false, _addr.ss_family); #ifdef ICE_USE_IOCP _acceptBuf.resize((sizeof(sockaddr_storage) + 16) * 2); #endif setBlock(_fd, false); setTcpBufSize(_fd, _instance->initializationData().properties, _logger); #ifndef _WIN32 // // Enable SO_REUSEADDR on Unix platforms to allow re-using the // socket even if it's in the TIME_WAIT state. On Windows, // this doesn't appear to be necessary and enabling // SO_REUSEADDR would actually not be a good thing since it // allows a second process to bind to an address even it's // already bound by another process. // // TODO: using SO_EXCLUSIVEADDRUSE on Windows would probably // be better but it's only supported by recent Windows // versions (XP SP2, Windows Server 2003). // setReuseAddress(_fd, true); #endif if(_traceLevels->network >= 2) { Trace out(_logger, _traceLevels->networkCat); out << "attempting to bind to tcp socket " << toString(); } const_cast(_addr) = doBind(_fd, _addr); } IceInternal::TcpAcceptor::~TcpAcceptor() { assert(_fd == INVALID_SOCKET); #ifdef ICE_USE_IOCP assert(_acceptFd == INVALID_SOCKET); #endif }