nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: Filename : OVR_Unix_Socket.cpp nuclear@0: Content : Berkley sockets networking implementation nuclear@0: Created : July 1, 2014 nuclear@0: Authors : Kevin Jenkins nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: #include "OVR_Unix_Socket.h" nuclear@0: #include "../Kernel/OVR_Std.h" nuclear@0: #include "../Kernel/OVR_Allocator.h" nuclear@0: #include "../Kernel/OVR_Threads.h" // Thread::MSleep nuclear@0: #include "../Kernel/OVR_Log.h" nuclear@0: nuclear@0: #include nuclear@0: nuclear@0: namespace OVR { namespace Net { nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // BerkleySocket nuclear@0: nuclear@0: void BerkleySocket::Close() nuclear@0: { nuclear@0: if (TheSocket != INVALID_SOCKET) nuclear@0: { nuclear@0: close(TheSocket); nuclear@0: TheSocket = INVALID_SOCKET; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: SInt32 BerkleySocket::GetSockname(SockAddr *pSockAddrOut) nuclear@0: { nuclear@0: struct sockaddr_in6 sa; nuclear@0: memset(&sa,0,sizeof(sa)); nuclear@0: socklen_t size = sizeof(sa); nuclear@0: SInt32 i = getsockname(TheSocket, (sockaddr*)&sa, &size); nuclear@0: if (i>=0) nuclear@0: { nuclear@0: pSockAddrOut->Set(&sa); nuclear@0: } nuclear@0: return i; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // BitStream overloads for SockAddr nuclear@0: nuclear@0: BitStream& operator<<(BitStream& out, SockAddr& in) nuclear@0: { nuclear@0: out.WriteBits((const unsigned char*) &in.Addr6, sizeof(in.Addr6)*8, true); nuclear@0: return out; nuclear@0: } nuclear@0: nuclear@0: BitStream& operator>>(BitStream& in, SockAddr& out) nuclear@0: { nuclear@0: bool success = in.ReadBits((unsigned char*) &out.Addr6, sizeof(out.Addr6)*8, true); nuclear@0: OVR_ASSERT(success); nuclear@0: OVR_UNUSED(success); nuclear@0: return in; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // SockAddr nuclear@0: nuclear@0: SockAddr::SockAddr() nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: SockAddr::SockAddr(SockAddr* address) nuclear@0: { nuclear@0: Set(&address->Addr6); nuclear@0: } nuclear@0: nuclear@0: SockAddr::SockAddr(sockaddr_storage* storage) nuclear@0: { nuclear@0: Set(storage); nuclear@0: } nuclear@0: nuclear@0: SockAddr::SockAddr(sockaddr_in6* address) nuclear@0: { nuclear@0: Set(address); nuclear@0: } nuclear@0: nuclear@0: SockAddr::SockAddr(const char* hostAddress, UInt16 port, int sockType) nuclear@0: { nuclear@0: Set(hostAddress, port, sockType); nuclear@0: } nuclear@0: nuclear@0: void SockAddr::Set(const sockaddr_storage* storage) nuclear@0: { nuclear@0: memcpy(&Addr6, storage, sizeof(Addr6)); nuclear@0: } nuclear@0: nuclear@0: void SockAddr::Set(const sockaddr_in6* address) nuclear@0: { nuclear@0: memcpy(&Addr6, address, sizeof(Addr6)); nuclear@0: } nuclear@0: nuclear@0: void SockAddr::Set(const char* hostAddress, UInt16 port, int sockType) nuclear@0: { nuclear@0: memset(&Addr6, 0, sizeof(Addr6)); nuclear@0: nuclear@0: struct addrinfo hints; nuclear@0: nuclear@0: // make sure the struct is empty nuclear@0: memset(&hints, 0, sizeof (addrinfo)); nuclear@0: nuclear@0: hints.ai_socktype = sockType; // SOCK_DGRAM or SOCK_STREAM nuclear@0: hints.ai_flags = AI_PASSIVE; // fill in my IP for me nuclear@0: hints.ai_family = AF_UNSPEC ; nuclear@0: nuclear@0: if (SOCK_DGRAM == sockType) nuclear@0: { nuclear@0: hints.ai_protocol = IPPROTO_UDP; nuclear@0: } nuclear@0: else if (SOCK_STREAM == sockType) nuclear@0: { nuclear@0: hints.ai_protocol = IPPROTO_TCP; nuclear@0: } nuclear@0: nuclear@0: struct addrinfo* servinfo = NULL; // will point to the results nuclear@0: nuclear@0: char portStr[32]; nuclear@0: OVR_itoa(port, portStr, sizeof(portStr), 10); nuclear@0: int errcode = getaddrinfo(hostAddress, portStr, &hints, &servinfo); nuclear@0: nuclear@0: if (0 != errcode) nuclear@0: { nuclear@0: OVR::LogError("getaddrinfo error: %s", gai_strerror(errcode)); nuclear@0: } nuclear@0: nuclear@0: OVR_ASSERT(servinfo); nuclear@0: nuclear@0: if (servinfo) nuclear@0: { nuclear@0: memcpy(&Addr6, servinfo->ai_addr, sizeof(Addr6)); nuclear@0: nuclear@0: freeaddrinfo(servinfo); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: UInt16 SockAddr::GetPort() nuclear@0: { nuclear@0: return htons(Addr6.sin6_port); nuclear@0: } nuclear@0: nuclear@0: String SockAddr::ToString(bool writePort, char portDelineator) const nuclear@0: { nuclear@0: char dest[INET6_ADDRSTRLEN + 1]; nuclear@0: nuclear@0: int ret = getnameinfo((struct sockaddr*)&Addr6, nuclear@0: sizeof(struct sockaddr_in6), nuclear@0: dest, nuclear@0: INET6_ADDRSTRLEN, nuclear@0: NULL, nuclear@0: 0, nuclear@0: NI_NUMERICHOST); nuclear@0: if (ret != 0) nuclear@0: { nuclear@0: dest[0] = '\0'; nuclear@0: } nuclear@0: nuclear@0: if (writePort) nuclear@0: { nuclear@0: unsigned char ch[2]; nuclear@0: ch[0]=portDelineator; nuclear@0: ch[1]=0; nuclear@0: OVR_strcat(dest, 16, (const char*) ch); nuclear@0: OVR_itoa(ntohs(Addr6.sin6_port), dest+strlen(dest), 16, 10); nuclear@0: } nuclear@0: nuclear@0: return String(dest); nuclear@0: } nuclear@0: bool SockAddr::IsLocalhost() const nuclear@0: { nuclear@0: static const unsigned char localhost_bytes[] = nuclear@0: { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; nuclear@0: nuclear@0: return memcmp(Addr6.sin6_addr.s6_addr, localhost_bytes, 16) == 0; nuclear@0: } nuclear@0: bool SockAddr::operator==( const SockAddr& right ) const nuclear@0: { nuclear@0: return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) == 0; nuclear@0: } nuclear@0: nuclear@0: bool SockAddr::operator!=( const SockAddr& right ) const nuclear@0: { nuclear@0: return !(*this == right); nuclear@0: } nuclear@0: nuclear@0: bool SockAddr::operator>( const SockAddr& right ) const nuclear@0: { nuclear@0: return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) > 0; nuclear@0: } nuclear@0: nuclear@0: bool SockAddr::operator<( const SockAddr& right ) const nuclear@0: { nuclear@0: return memcmp(&Addr6, &right.Addr6, sizeof(Addr6)) < 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // Returns true on success nuclear@0: static bool SetSocketOptions(SocketHandle sock) nuclear@0: { nuclear@0: bool failed = false; nuclear@0: int sock_opt; nuclear@0: int sockError = 0; nuclear@0: nuclear@0: // This doubles the max throughput rate nuclear@0: sock_opt=1024*256; nuclear@0: sockError = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); nuclear@0: if (sockError != 0) nuclear@0: { nuclear@0: int errsv = errno; nuclear@0: OVR::LogError("[Socket] Failed SO_RCVBUF setsockopt, errno: %d", errsv); nuclear@0: failed = true; nuclear@0: } nuclear@0: nuclear@0: // This doesn't make much difference: 10% maybe nuclear@0: // Not supported on console 2 nuclear@0: sock_opt=1024*16; nuclear@0: sockError = setsockopt(sock, SOL_SOCKET, SO_SNDBUF, ( char * ) & sock_opt, sizeof ( sock_opt ) ); nuclear@0: if (sockError != 0) nuclear@0: { nuclear@0: int errsv = errno; nuclear@0: OVR::LogError("[Socket] Failed SO_SNDBUF setsockopt, errno: %d", errsv); nuclear@0: failed = true; nuclear@0: } nuclear@0: nuclear@0: // NOTE: This should be OVR_OS_BSD, not Mac. nuclear@0: #ifdef OVR_OS_MAC nuclear@0: int value = 1; nuclear@0: sockError = setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value)); nuclear@0: if (sockError != 0) nuclear@0: { nuclear@0: int errsv = errno; nuclear@0: OVR::LogError("[Socket] Failed SO_NOSIGPIPE setsockopt, errno: %d", errsv); nuclear@0: failed = true; nuclear@0: } nuclear@0: #endif nuclear@0: nuclear@0: // Reuse address is only needed for posix platforms, as it is the default nuclear@0: // on Windows platforms. nuclear@0: int optval = 1; nuclear@0: sockError = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int)); nuclear@0: if (sockError != 0) nuclear@0: { nuclear@0: int errsv = errno; nuclear@0: OVR::LogError("[Socket] Failed SO_REUSEADDR setsockopt, errno: %d", errsv); nuclear@0: failed = true; nuclear@0: } nuclear@0: nuclear@0: return !failed; nuclear@0: } nuclear@0: nuclear@0: void _Ioctlsocket(SocketHandle sock, unsigned long nonblocking) nuclear@0: { nuclear@0: int flags = fcntl(sock, F_GETFL, 0); nuclear@0: if (flags < 0) return; // return false nuclear@0: if (nonblocking == 0) { flags &= ~O_NONBLOCK; } nuclear@0: else { flags |= O_NONBLOCK; } nuclear@0: fcntl(sock, F_SETFL, flags); nuclear@0: } nuclear@0: nuclear@0: static SocketHandle BindShared(int ai_family, int ai_socktype, BerkleyBindParameters *pBindParameters) nuclear@0: { nuclear@0: SocketHandle sock; nuclear@0: nuclear@0: struct addrinfo hints; nuclear@0: memset(&hints, 0, sizeof (addrinfo)); // make sure the struct is empty nuclear@0: hints.ai_family = ai_family; nuclear@0: hints.ai_socktype = ai_socktype; nuclear@0: hints.ai_flags = AI_PASSIVE; // fill in my IP for me nuclear@0: struct addrinfo *servinfo=0, *aip; // will point to the results nuclear@0: char portStr[32]; nuclear@0: OVR_itoa(pBindParameters->Port, portStr, sizeof(portStr), 10); nuclear@0: nuclear@0: int errcode = 0; nuclear@0: if (!pBindParameters->Address.IsEmpty()) nuclear@0: errcode = getaddrinfo(pBindParameters->Address.ToCStr(), portStr, &hints, &servinfo); nuclear@0: else nuclear@0: errcode = getaddrinfo(0, portStr, &hints, &servinfo); nuclear@0: nuclear@0: if (0 != errcode) nuclear@0: { nuclear@0: OVR::LogError("getaddrinfo error: %s", gai_strerror(errcode)); nuclear@0: } nuclear@0: nuclear@0: for (aip = servinfo; aip != NULL; aip = aip->ai_next) nuclear@0: { nuclear@0: // Open socket. The address type depends on what nuclear@0: // getaddrinfo() gave us. nuclear@0: sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); nuclear@0: if (sock != 0) nuclear@0: { nuclear@0: SetSocketOptions(sock); nuclear@0: int ret = bind( sock, aip->ai_addr, (int) aip->ai_addrlen ); nuclear@0: if (ret>=0) nuclear@0: { nuclear@0: // The actual socket is always non-blocking nuclear@0: // I control blocking or not using WSAEventSelect nuclear@0: _Ioctlsocket(sock, 1); nuclear@0: freeaddrinfo(servinfo); nuclear@0: return sock; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: close(sock); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (servinfo) { freeaddrinfo(servinfo); } nuclear@0: return INVALID_SOCKET; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // UDPSocket nuclear@0: nuclear@0: UDPSocket::UDPSocket() nuclear@0: { nuclear@0: RecvBuf = new UByte[RecvBufSize]; nuclear@0: } nuclear@0: nuclear@0: UDPSocket::~UDPSocket() nuclear@0: { nuclear@0: delete[] RecvBuf; nuclear@0: } nuclear@0: nuclear@0: SocketHandle UDPSocket::Bind(BerkleyBindParameters *pBindParameters) nuclear@0: { nuclear@0: SocketHandle s = BindShared(AF_INET6, SOCK_DGRAM, pBindParameters); nuclear@0: if (s < 0) nuclear@0: return s; nuclear@0: nuclear@0: Close(); nuclear@0: TheSocket = s; nuclear@0: nuclear@0: return TheSocket; nuclear@0: } nuclear@0: nuclear@0: void UDPSocket::OnRecv(SocketEvent_UDP* eventHandler, UByte* pData, int bytesRead, SockAddr* address) nuclear@0: { nuclear@0: eventHandler->UDP_OnRecv(this, pData, bytesRead, address); nuclear@0: } nuclear@0: nuclear@0: int UDPSocket::Send(const void* pData, int bytes, SockAddr* address) nuclear@0: { nuclear@0: // NOTE: This should be OVR_OS_BSD nuclear@0: #ifdef OVR_OS_MAC nuclear@0: int flags = 0; nuclear@0: #else nuclear@0: int flags = MSG_NOSIGNAL; nuclear@0: #endif nuclear@0: nuclear@0: return (int)sendto(TheSocket, (const char*)pData, bytes, flags, (const sockaddr*)&address->Addr6, sizeof(address->Addr6)); nuclear@0: } nuclear@0: nuclear@0: void UDPSocket::Poll(SocketEvent_UDP *eventHandler) nuclear@0: { nuclear@0: struct sockaddr_storage win32_addr; nuclear@0: socklen_t fromlen; nuclear@0: int bytesRead; nuclear@0: nuclear@0: // FIXME: Implement blocking poll wait for UDP nuclear@0: nuclear@0: // While some bytes are read, nuclear@0: while (fromlen = sizeof(win32_addr), // Must set fromlen each time nuclear@0: bytesRead = (int)recvfrom(TheSocket, (char*)RecvBuf, RecvBufSize, 0, (sockaddr*)&win32_addr, &fromlen), nuclear@0: bytesRead > 0) nuclear@0: { nuclear@0: SockAddr address(&win32_addr); // Wrap address nuclear@0: nuclear@0: OnRecv(eventHandler, RecvBuf, bytesRead, &address); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------- nuclear@0: // TCPSocket nuclear@0: nuclear@0: TCPSocket::TCPSocket() nuclear@0: { nuclear@0: IsConnecting = false; nuclear@0: IsListenSocket = false; nuclear@0: } nuclear@0: TCPSocket::TCPSocket(SocketHandle boundHandle, bool isListenSocket) nuclear@0: { nuclear@0: TheSocket = boundHandle; nuclear@0: IsListenSocket = isListenSocket; nuclear@0: IsConnecting = false; nuclear@0: SetSocketOptions(TheSocket); nuclear@0: nuclear@0: // The actual socket is always non-blocking nuclear@0: _Ioctlsocket(TheSocket, 1); nuclear@0: } nuclear@0: nuclear@0: TCPSocket::~TCPSocket() nuclear@0: { nuclear@0: } nuclear@0: nuclear@0: void TCPSocket::OnRecv(SocketEvent_TCP* eventHandler, UByte* pData, int bytesRead) nuclear@0: { nuclear@0: eventHandler->TCP_OnRecv(this, pData, bytesRead); nuclear@0: } nuclear@0: nuclear@0: SocketHandle TCPSocket::Bind(BerkleyBindParameters* pBindParameters) nuclear@0: { nuclear@0: SocketHandle s = BindShared(AF_INET6, SOCK_STREAM, pBindParameters); nuclear@0: if (s < 0) nuclear@0: return s; nuclear@0: nuclear@0: Close(); nuclear@0: nuclear@0: SetBlockingTimeout(pBindParameters->blockingTimeout); nuclear@0: TheSocket = s; nuclear@0: nuclear@0: return TheSocket; nuclear@0: } nuclear@0: nuclear@0: int TCPSocket::Listen() nuclear@0: { nuclear@0: if (IsListenSocket) nuclear@0: { nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: int i = listen(TheSocket, SOMAXCONN); nuclear@0: if (i >= 0) nuclear@0: { nuclear@0: IsListenSocket = true; nuclear@0: } nuclear@0: nuclear@0: return i; nuclear@0: } nuclear@0: nuclear@0: int TCPSocket::Connect(SockAddr* address) nuclear@0: { nuclear@0: int retval; nuclear@0: nuclear@0: retval = connect(TheSocket, (struct sockaddr *) &address->Addr6, sizeof(address->Addr6)); nuclear@0: if (retval < 0) nuclear@0: { nuclear@0: int errsv = errno; nuclear@0: // EINPROGRESS should not be checked on windows but should nuclear@0: // be checked on POSIX platforms. nuclear@0: if (errsv == EWOULDBLOCK || errsv == EINPROGRESS) nuclear@0: { nuclear@0: IsConnecting = true; nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: OVR::LogText( "TCPSocket::Connect failed:Error code - %d\n", errsv ); nuclear@0: } nuclear@0: nuclear@0: return retval; nuclear@0: } nuclear@0: nuclear@0: int TCPSocket::Send(const void* pData, int bytes) nuclear@0: { nuclear@0: if (bytes <= 0) nuclear@0: { nuclear@0: return 0; nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: return (int)send(TheSocket, (const char*)pData, bytes, 0); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //// TCPSocketPollState nuclear@0: nuclear@0: TCPSocketPollState::TCPSocketPollState() nuclear@0: { nuclear@0: FD_ZERO(&readFD); nuclear@0: FD_ZERO(&exceptionFD); nuclear@0: FD_ZERO(&writeFD); nuclear@0: largestDescriptor = INVALID_SOCKET; nuclear@0: } nuclear@0: nuclear@0: bool TCPSocketPollState::IsValid() const nuclear@0: { nuclear@0: return largestDescriptor != INVALID_SOCKET; nuclear@0: } nuclear@0: nuclear@0: void TCPSocketPollState::Add(TCPSocket* tcpSocket) nuclear@0: { nuclear@0: if (!tcpSocket) nuclear@0: { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: SocketHandle handle = tcpSocket->GetSocketHandle(); nuclear@0: nuclear@0: if (handle == INVALID_SOCKET) nuclear@0: { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: if (largestDescriptor == INVALID_SOCKET || nuclear@0: largestDescriptor < handle) nuclear@0: { nuclear@0: largestDescriptor = handle; nuclear@0: } nuclear@0: nuclear@0: FD_SET(handle, &readFD); nuclear@0: FD_SET(handle, &exceptionFD); nuclear@0: nuclear@0: if (tcpSocket->IsConnecting) nuclear@0: { nuclear@0: FD_SET(handle, &writeFD); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: bool TCPSocketPollState::Poll(long usec, long seconds) nuclear@0: { nuclear@0: timeval tv; nuclear@0: tv.tv_sec = seconds; nuclear@0: tv.tv_usec = (int)usec; nuclear@0: nuclear@0: return select(largestDescriptor + 1, &readFD, &writeFD, &exceptionFD, &tv) > 0; nuclear@0: } nuclear@0: nuclear@0: void TCPSocketPollState::HandleEvent(TCPSocket* tcpSocket, SocketEvent_TCP* eventHandler) nuclear@0: { nuclear@0: if (!tcpSocket || !eventHandler) nuclear@0: { nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: SocketHandle handle = tcpSocket->GetSocketHandle(); nuclear@0: nuclear@0: if (tcpSocket->IsConnecting && FD_ISSET(handle, &writeFD)) nuclear@0: { nuclear@0: tcpSocket->IsConnecting = false; nuclear@0: eventHandler->TCP_OnConnected(tcpSocket); nuclear@0: } nuclear@0: nuclear@0: if (FD_ISSET(handle, &readFD)) nuclear@0: { nuclear@0: if (!tcpSocket->IsListenSocket) nuclear@0: { nuclear@0: static const int BUFF_SIZE = 8096; nuclear@0: char data[BUFF_SIZE]; nuclear@0: nuclear@0: int bytesRead = (int)recv(handle, data, BUFF_SIZE, 0); nuclear@0: if (bytesRead > 0) nuclear@0: { nuclear@0: tcpSocket->OnRecv(eventHandler, (UByte*)data, bytesRead); nuclear@0: } nuclear@0: else // Disconnection event: nuclear@0: { nuclear@0: tcpSocket->IsConnecting = false; nuclear@0: eventHandler->TCP_OnClosed(tcpSocket); nuclear@0: } nuclear@0: } nuclear@0: else nuclear@0: { nuclear@0: struct sockaddr_storage sockAddr; nuclear@0: socklen_t sockAddrSize = sizeof(sockAddr); nuclear@0: nuclear@0: SocketHandle newSock = accept(handle, (sockaddr*)&sockAddr, (socklen_t*)&sockAddrSize); nuclear@0: if (newSock > 0) nuclear@0: { nuclear@0: SockAddr sa(&sockAddr); nuclear@0: eventHandler->TCP_OnAccept(tcpSocket, &sa, newSock); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if (FD_ISSET(handle, &exceptionFD)) nuclear@0: { nuclear@0: tcpSocket->IsConnecting = false; nuclear@0: eventHandler->TCP_OnClosed(tcpSocket); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: }} // namespace OVR::Net