void Network::DeInit() { KNET_LOG(LogVerbose, "Network::DeInit: Closing down."); PolledTimer timer; // Kill all connections. while(!connections.empty()) { MessageConnection *connection = *connections.begin(); CloseConnection(connection); // CloseConnection erases connection from the connections list, so this loop terminates. } // Kill the server, if it's running. StopServer(); // Kill all worker threads. while(!workerThreads.empty()) CloseWorkerThread(workerThreads.front()); // Erases the item from workerThreads, so this loop terminates. // Clean up any sockets that might be remaining. while(!sockets.empty()) { sockets.front().Close(); sockets.pop_front(); } // Deinitialize network subsystem. #ifdef WIN32 WSACleanup(); #endif KNET_LOG(LogWaits, "Network::DeInit: Deinitialized kNet Network object, took %f msecs.", timer.MSecsElapsed()); }
Ptr(MessageConnection) Network::Connect(const char *address, unsigned short port, SocketTransportLayer transport, IMessageHandler *messageHandler, Datagram *connectMessage) { Socket *socket = ConnectSocket(address, port, transport); if (!socket) return 0; if (transport == SocketOverUDP) { SendUDPConnectDatagram(*socket, connectMessage); KNET_LOG(LogInfo, "Network::Connect: Sent a UDP Connection Start datagram to to %s.", socket->ToString().c_str()); } else KNET_LOG(LogInfo, "Network::Connect: Connected a TCP socket to %s.", socket->ToString().c_str()); Ptr(MessageConnection) connection; if (transport == SocketOverTCP) connection = new TCPMessageConnection(this, 0, socket, ConnectionOK); else connection = new UDPMessageConnection(this, 0, socket, ConnectionPending); connection->RegisterInboundMessageHandler(messageHandler); AssignConnectionToWorkerThread(connection); connections.insert(connection); return connection; }
void Network::Init() { #ifdef WIN32 // Initialize Winsock int result = WSAStartup(MAKEWORD(2,2), &wsaData); if (result != 0) { KNET_LOG(LogError, "Network::Init: WSAStartup failed: %s!", GetErrorString(result).c_str()); return; } #endif // Fetch the local system host name for later use. The local address is cached here // to avoid multiple queries to namespace providers. char str[256]; int ret = gethostname(str, 256); // For more information, see http://msdn.microsoft.com/en-us/library/ms738527(VS.85).aspx . if (ret == 0) { localHostName = str; KNET_LOG(LogInfo, "Network::Init successful. gethostname returned %s", str); } else { KNET_LOG(LogError, "Network::Init: gethostname failed! Error: %s. Using 'localhost' as the local host name", GetLastErrorString().c_str()); localHostName = "localhost"; } }
void Clock::InitClockData() { if (appStartTime == 0) appStartTime = Tick(); #ifdef _WIN32 if (!QueryPerformanceFrequency(&ddwTimerFrequency)) { KNET_LOG(LogError, "The system doesn't support high-resolution timers!"); ddwTimerFrequency.HighPart = (unsigned long)-1; ddwTimerFrequency.LowPart = (unsigned long)-1; } if (ddwTimerFrequency.HighPart > 0) KNET_LOG(LogError, "Warning: Clock::TicksPerSec will yield invalid timing data!"); if (appStartTime == 0) { #if WINVER >= 0x0600 && !defined(KNET_ENABLE_WINXP_SUPPORT) appStartTime = (tick_t)GetTickCount64(); #else appStartTime = (tick_t)GetTickCount(); #endif } ///\todo Test here that the return values of QueryPerformanceCounter is nondecreasing. #endif }
void Event::Set() { if (IsNull()) { KNET_LOG(LogError, "Event::Set() failed! Tried to set an uninitialized Event!"); return; } if (type != EventWaitSignal) { KNET_LOG(LogError, "Event::Set() failed! Tried to set an event that is of type %d (should have been of type EventWaitSignal)", (int)type); return; } if (fd[1] == -1) { KNET_LOG(LogError, "Event::Set() failed! Tried to set a read-only Event! (This event is probably a Socket read descriptor"); return; } // Read one byte off from the pipe. This will fail or succeed and we don't really care, the important thing // is that Event::Set() will not increase the number of bytes in the pipe. u8 val = 1; read(fd[0], &val, sizeof(val)); // Now that we removed one byte off the pipe (if there was one), we are allowed to add one in. val = 1; // It doesn't really matter what value we write here, but by convention we always write (and expect to read back) a single '1'. int ret = write(fd[1], &val, sizeof(val)); if (ret == -1) { KNET_LOG(LogError, "Event::Set() write() failed: %s(%d)!", strerror(errno), (int)errno); return; } }
void Event::Create(EventWaitType type_) { type = type_; assert(type != EventWaitInvalid); if (type == EventWaitSignal) // For signal events, we need to create a pipe. { if (pipe(fd) == -1) { KNET_LOG(LogError, "Error in Event::Create: %s(%d)!", strerror(errno), errno); return; } int ret = fcntl(fd[0], F_SETFL, O_NONBLOCK); if (ret == -1) { KNET_LOG(LogError, "Event::Create: fcntl failed to set fd[0] in nonblocking mode: %s(%d)", strerror(errno), errno); return; } ret = fcntl(fd[1], F_SETFL, O_NONBLOCK); if (ret == -1) { KNET_LOG(LogError, "Event::Create: fcntl failed to set fd[1] in nonblocking mode: %s(%d)", strerror(errno), errno); return; } } ///\todo Return success or failure. }
void Event::Reset() { if (IsNull()) { KNET_LOG(LogError, "Event::Reset() failed! Tried to reset an uninitialized Event!"); return; } if (type == EventWaitDummy) return; if (type == EventWaitSignal) { // Exhaust the pipe: read bytes off of it until there is nothing to read. This will cause select()ing on the // pipe to not trigger on read-availability. (The code in this class should maintain that the pipe never contains // more than one unread byte, but still better to loop here to be sure) u8 val = 0; int ret = 0; while(ret != -1) { ret = read(fd[0], &val, sizeof(val)); if (ret == -1 && errno != EAGAIN) KNET_LOG(LogError, "Event::Reset() read() failed: %s(%d)!", strerror(errno), (int)errno); } } else KNET_LOG(LogError, "Event::Reset() called on an Event of type %d! (should have been of type EventWaitSignal)", (int)type); ///\todo int to string. }
void PrintLocalIP() { char ac[80]; if (gethostname(ac, sizeof(ac)) == KNET_SOCKET_ERROR) { KNET_LOG(LogError, "Error getting local host name!"); return; } KNET_LOG(LogInfo, "Host name is %s", ac); #ifndef UWP struct hostent *phe = gethostbyname(ac); if (phe == 0) { KNET_LOG(LogError, "Bad host lookup."); return; } for (int i = 0; phe->h_addr_list[i] != 0; ++i) { struct in_addr addr; memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr)); KNET_LOG(LogInfo, "Address %d: %s", i, inet_ntoa(addr)); ///\todo inet_ntoa is deprecated! doesn't handle IPv6! } #endif }
void Thread::Stop() { // Signal that the thread should quit now. threadEnabled = false; if (threadHandle == NULL) { threadHoldEvent.Close(); threadHoldEventAcked.Close(); threadResumeEvent.Close(); delete invoker; invoker = 0; return; } kNet::Clock::Sleep(10); assert(threadHandle != 0); int numTries = 100; while(numTries-- > 0) { DWORD exitCode = 0; BOOL result = GetExitCodeThread(threadHandle, &exitCode); if (result == 0) { KNET_LOG(LogError, "Warning: Received error %d from GetExitCodeThread in Thread::Stop()!", GetLastError()); break; } else if (exitCode != STILL_ACTIVE) { CloseHandle(threadHandle); threadHandle = NULL; break; } kNet::Clock::Sleep(50); } if (threadHandle != NULL) { TerminateThread(threadHandle, (DWORD)-1); // CloseHandle(threadHandle); KNET_LOG(LogError, "Warning: Had to forcibly terminate thread!"); } KNET_LOG(LogInfo, "Thread::Stop() called."); threadHandle = NULL; threadId = 0; delete invoker; invoker = 0; threadHoldEvent.Close(); threadHoldEventAcked.Close(); threadResumeEvent.Close(); }
DWORD WINAPI ThreadEntryPoint(LPVOID lpParameter) { KNET_LOG(LogInfo, "ThreadEntryPoint: Thread started with param 0x%08X.", lpParameter); Thread *thread = reinterpret_cast<Thread*>(lpParameter); if (!thread) { KNET_LOG(LogError, "Invalid thread start parameter 0!"); return (DWORD)-1; } thread->_ThreadRun(); return 0; }
void* ThreadEntryPoint(void* data) { KNET_LOG(LogInfo, "ThreadEntryPoint: Thread started with param 0x%p.", data); Thread *thread = reinterpret_cast<Thread*>(data); if (!thread) { KNET_LOG(LogError, "Invalid thread start parameter 0!"); return 0; } thread->_ThreadRun(); pthread_exit((void*)0); return 0; }
NetworkServer *Network::StartServer(const std::vector<std::pair<unsigned short, SocketTransportLayer> > &listenPorts, INetworkServerListener *serverListener, bool allowAddressReuse) { if (listenPorts.empty()) { KNET_LOG(LogError, "Failed to start server, since you did not provide a list of ports to listen to in Network::StartServer()!"); return 0; } std::vector<Socket *> listenSockets; for(size_t i = 0; i < listenPorts.size(); ++i) { Socket *listenSock = OpenListenSocket(listenPorts[i].first, listenPorts[i].second, allowAddressReuse); if (listenSock) listenSockets.push_back(listenSock); } if (listenSockets.empty()) { KNET_LOG(LogError, "Failed to start server. No ports to listen to!"); return 0; } server = new NetworkServer(this, listenSockets); server->RegisterServerListener(serverListener); AssignServerToWorkerThread(server); KNET_LOG(LogInfo, "Server up and listening on the following ports: "); { std::stringstream ss; ss << "UDP "; for(size_t i = 0; i < listenSockets.size(); ++i) if (listenSockets[i]->TransportLayer() == SocketOverUDP) ss << listenSockets[i]->LocalPort() << " "; KNET_LOG(LogInfo, ss.str().c_str()); } { std::stringstream ss; ss << "TCP "; for(size_t i = 0; i < listenSockets.size(); ++i) if (listenSockets[i]->TransportLayer() == SocketOverTCP) ss << listenSockets[i]->LocalPort() << " "; KNET_LOG(LogInfo, ss.str().c_str()); } return server; }
void SerializedMessageList::ParseMessages(TiXmlElement *root) { #ifdef KNET_USE_TINYXML TiXmlElement *node = root->FirstChildElement("message"); while(node) { SerializedMessageDesc desc; int success = node->QueryIntAttribute("id", (int*)&desc.id); if (success == TIXML_NO_ATTRIBUTE) { KNET_LOG(LogError, "Error parsing message attribute 'id' as int!"); node = node->NextSiblingElement("message"); continue; } success = node->QueryIntAttribute("priority", (int*)&desc.priority); if (success == TIXML_NO_ATTRIBUTE) desc.priority = 0; // If priority not specified, use the default priority of zero - the lowest priority possible. if (node->Attribute("name")) desc.name = node->Attribute("name"); desc.reliable = ParseBool(node->Attribute("reliable")); desc.inOrder = ParseBool(node->Attribute("inOrder")); desc.data = ParseNode(node, 0); // Work a slight convenience - if there is a single struct inside a single struct inside a single struct - jump straight through to the data. messages.push_back(desc); node = node->NextSiblingElement("message"); } #else throw NetException("kNet was built without TinyXml support! SerializedMessageList is not available!"); #endif }
void Thread::Stop() { // Signal that the thread should quit now. threadEnabled = false; if (!thread) { threadHoldEvent.Close(); threadHoldEventAcked.Close(); threadResumeEvent.Close(); delete invoker; invoker = 0; return; } kNet::Clock::Sleep(10); assert(thread); /// \todo Do not block indefinitely while waiting for the thread to terminate pthread_join(thread, 0); thread = 0; KNET_LOG(LogInfo, "Thread::Stop() called."); delete invoker; invoker = 0; threadHoldEvent.Close(); threadHoldEventAcked.Close(); threadResumeEvent.Close(); }
void Event::Set() { if (wsaEvent == NULL) KNET_LOG(LogError, "Event::Set called on a null event!"); else WSASetEvent(wsaEvent); }
void Thread::CheckHold() { if (threadHoldEvent.Test()) { KNET_LOG(LogVerbose, "Thread::CheckHold(): suspending thread. this: %p.", this); PolledTimer timer; while(!ShouldQuit()) { threadHoldEventAcked.Set(); bool success = threadResumeEvent.Wait(1000); if (success) break; } KNET_LOG(LogWaits, "Thread::CheckHold: Slept for %f msecs.", timer.MSecsElapsed()); threadHoldEventAcked.Reset(); } }
void Network::DeleteSocket(Socket *socket) { if (!socket) { KNET_LOG(LogError, "Network::DeleteSocket() called with a null socket pointer!"); return; } for(std::list<Socket>::iterator iter = sockets.begin(); iter != sockets.end(); ++iter) if (&*iter == socket) { socket->Close(); // The Socket pointers MessageConnection objects have are pointers to this list, // so after calling this function with a Socket pointer, the Socket is deleted for good. sockets.erase(iter); KNET_LOG(LogInfo, "Network::DeleteSocket: Closed socket %p.", socket); return; } KNET_LOG(LogError, "Network::DeleteSocket: Tried to free a nonexisting socket %p!", socket); }
void Network::StopServer() { if (!server) return; RemoveServerFromItsWorkerThread(server); ///\todo This is a forceful stop. Perhaps have a benign teardown as well? server = 0; KNET_LOG(LogVerbose, "Network::StopServer: Deinitialized NetworkServer."); }
void Network::PrintHostNameInfo(const char *hostname, const char *port) { addrinfo hints; //-------------------------------- // Setup the hints address info structure // which is passed to the getaddrinfo() function memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; //-------------------------------- // Call getaddrinfo(). If the call succeeds, // the result variable will hold a linked list // of addrinfo structures containing response // information addrinfo *result = NULL; unsigned long dwRetval = (unsigned long)getaddrinfo(hostname, port, &hints, &result); if (dwRetval != 0) { KNET_LOG(LogError, "getaddrinfo failed with error: %d", (int)dwRetval); return; } KNET_LOG(LogInfo, "getaddrinfo returned success"); int i = 1; // Retrieve each address and print out the hex bytes for(addrinfo *ptr = result; ptr != NULL; ptr = ptr->ai_next) { KNET_LOG(LogInfo, "getaddrinfo response %d", i++); PrintAddrInfo(ptr); } freeaddrinfo(result); PrintLocalIP(); }
/// Returns true if the event was set during this time, or false if timout occurred. bool Event::Wait(unsigned long msecs) const { if (IsNull() || type == EventWaitDummy) return false; fd_set fds; timeval tv; tv.tv_sec = msecs / 1000; tv.tv_usec = (msecs - tv.tv_sec * 1000) * 1000; FD_ZERO(&fds); FD_SET(fd[0], &fds); if (type == EventWaitSignal || type == EventWaitRead) // These both use fd[0] and descriptor read-ready as signal, so are processed using the same codepath. { // Wait on a read state. // "The file descriptor is readable if the counter has a value greater than 0." int ret = select(fd[0]+1, &fds, NULL, NULL, &tv); // http://linux.die.net/man/2/select if (ret == -1) { KNET_LOG(LogError, "Event::Wait: select() failed on a pipe: %s(%d)!", strerror(errno), (int)errno); return false; } return ret != 0; } else if (type == EventWaitWrite) { int ret = select(fd[0]+1, NULL, &fds, NULL, &tv); if (ret == -1) { KNET_LOG(LogError, "Event::Wait: select() failed for Event of type EventWaitWrite: %s(%d)!", strerror(errno), (int)errno); return false; } return ret != 0; } else { KNET_LOG(LogError, "Event::Wait called for even of invalid type %d!", (int)type); return false; } }
void Network::CloseConnection(MessageConnection *connection) { KNET_LOG(LogVerbose, "Network::CloseConnection: Closing down connection %p.", connection); if (!connection) return; RemoveConnectionFromItsWorkerThread(connection); DeleteSocket(connection->socket); connection->socket = 0; connection->owner = 0; connection->ownerServer = 0; connections.erase(connection); }
NetworkServer *Network::StartServer(unsigned short port, SocketTransportLayer transport, INetworkServerListener *serverListener, bool allowAddressReuse) { Socket *listenSock = OpenListenSocket(port, transport, allowAddressReuse); if (listenSock == 0) { KNET_LOG(LogError, "Failed to start server. Could not open listen port to %d using %s.", (int)port, transport == SocketOverTCP ? "TCP" : "UDP"); return 0; } std::vector<Socket *> listenSockets; listenSockets.push_back(listenSock); server = new NetworkServer(this, listenSockets); server->RegisterServerListener(serverListener); AssignServerToWorkerThread(server); KNET_LOG(LogInfo, "Server up (%s). Waiting for client to connect.", listenSock->ToString().c_str()); return server; }
void Thread::_ThreadRun() { try { if (!threadEnabled) { KNET_LOG(LogError, "ThreadEntryPoint: Thread immediately requested to quit."); return; } invoker->Invoke(); } catch(NetException &e) { KNET_LOG(LogError, "NetException thrown in thread: %s.", e.what()); } catch(std::exception &e) { KNET_LOG(LogError, "std::exception thrown in thread: %s.", e.what()); } catch(...) { KNET_LOG(LogError, "Unknown exception thrown in thread."); } }
Socket *Network::CreateUDPSlaveSocket(Socket *serverListenSocket, const EndPoint &remoteEndPoint, const char *remoteHostName) { if (!serverListenSocket) { KNET_LOG(LogError, "Network::CreateUDPSlaveSocket called with null serverListenSocket handle!"); return 0; } SOCKET udpSocket = serverListenSocket->GetSocketHandle(); if (udpSocket == INVALID_SOCKET) { KNET_LOG(LogError, "Network::CreateUDPSlaveSocket called with a UDP server socket with invalid internal socket handle!"); return 0; } sockets.push_back(Socket(udpSocket, serverListenSocket->LocalEndPoint(), serverListenSocket->LocalAddress(), remoteEndPoint, remoteHostName, SocketOverUDP, ServerClientSocket, cMaxUDPSendSize)); Socket *socket = &sockets.back(); socket->SetBlocking(false); KNET_LOG(LogInfo, "Network::CreateUDPSlaveSocket: Connected an UDP socket to %s.", socket->ToString().c_str()); return socket; }
void Network::SendUDPConnectDatagram(Socket &socket, Datagram *connectMessage) { const int connectMessageSize = connectMessage ? connectMessage->size : 8; OverlappedTransferBuffer *sendData = socket.BeginSend(connectMessageSize); if (!sendData) { KNET_LOG(LogError, "Network::SendUDPConnectDatagram: socket.BeginSend failed! Cannot send UDP connection datagram!"); return; } sendData->bytesContains = connectMessageSize; if (connectMessage) { ///\todo Craft the proper connection attempt datagram. memcpy(sendData->buffer.buf, connectMessage->data, sendData->buffer.len); KNET_LOG(LogVerbose, "Network::SendUDPConnectDatagram: Sending UDP connect message of size %d.", (int)sendData->buffer.len); } else { ///\todo Craft the proper connection attempt datagram. memset(sendData->buffer.buf, 0, sendData->buffer.len); KNET_LOG(LogVerbose, "Network::SendUDPConnectDatagram: Sending null UDP connect message of size %d.", (int)sendData->buffer.len); } socket.EndSend(sendData); }
void Network::CloseWorkerThread(NetworkWorkerThread *workerThread) { if (!workerThread) return; // We (should) never close a worker thread until we have first removed all the connections and servers it handles. if (workerThread->NumConnections() + workerThread->NumServers() > 0) KNET_LOG(LogError, "Warning: Closing a worker thread %p when it still has %d connections and %d servers to handle.", workerThread, workerThread->NumConnections(), workerThread->NumServers()); for(size_t i = 0; i < workerThreads.size(); ++i) if (workerThreads[i] == workerThread) { // Remove the thread pointer from internal list. std::swap(workerThreads[i], workerThreads[workerThreads.size()-1]); workerThreads.pop_back(); workerThread->StopThread(); KNET_LOG(LogInfo, "Deleted a NetworkWorkerThread. There are now %d worker threads left.", (int)workerThreads.size()); delete workerThread; return; } KNET_LOG(LogError, "Network::CloseWorkerThread: Asked to close worker thread %p, but no such thread is tracked by this Network object! Ignoring the request.", workerThread); }
NetworkWorkerThread *Network::GetOrCreateWorkerThread() { static const int maxConnectionsPerThread = 8; // Find an existing thread with sufficiently low load. for(size_t i = 0; i < workerThreads.size(); ++i) if (workerThreads[i]->NumConnections() + workerThreads[i]->NumServers() < maxConnectionsPerThread) return workerThreads[i]; // No appropriate thread found. Create a new one. NetworkWorkerThread *workerThread = new NetworkWorkerThread(); workerThread->StartThread(); workerThreads.push_back(workerThread); KNET_LOG(LogInfo, "Created a new NetworkWorkerThread. There are now %d worker threads.", (int)workerThreads.size()); return workerThread; }
bool Thread::IsRunning() const { if (threadHandle == NULL) return false; DWORD exitCode = 0; BOOL result = GetExitCodeThread(threadHandle, &exitCode); if (result == 0) { KNET_LOG(LogError, "Warning: Received error %d from GetExitCodeThread in Thread::IsRunning!", GetLastError()); return false; } return exitCode == STILL_ACTIVE; }
void Thread::StartThread() { if (threadHandle != NULL) return; threadHoldEvent = CreateNewEvent(EventWaitSignal); threadHoldEventAcked = CreateNewEvent(EventWaitSignal); threadResumeEvent = CreateNewEvent(EventWaitSignal); threadEnabled = true; threadHandle = CreateThread(NULL, 0, ThreadEntryPoint, this, 0, &threadId); if (threadHandle == NULL) throw NetException("Failed to create thread!"); else KNET_LOG(LogInfo, "Thread::Run(): Thread created."); SetName("kNet Thread"); }
void Clock::Sleep(int milliseconds) { #ifdef WIN8RT #pragma WARNING(Clock::Sleep has not been implemented!) #elif defined(_WIN32) ::Sleep(milliseconds); #elif !defined(__native_client__) && !defined(__EMSCRIPTEN__) && !defined(__APPLE__) // http://linux.die.net/man/2/nanosleep timespec ts; ts.tv_sec = milliseconds / 1000; ts.tv_nsec = (milliseconds - ts.tv_sec * 1000) * 1000 * 1000; int ret = nanosleep(&ts, NULL); if (ret == -1) KNET_LOG(LogError, "nanosleep returned -1! Reason: %s(%d).", strerror(errno), (int)errno); #else #warning Clock::Sleep has not been implemented! #endif }