void NetworkServer::BroadcastMessage(unsigned long id, bool reliable, bool inOrder, unsigned long priority, unsigned long contentID, const char *data, size_t numBytes, MessageConnection *exclude) { PolledTimer timer; Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire(); if (timer.MSecsElapsed() >= 50.f) { LOG(LogWaits, "NetworkServer::BroadcastMessage: Accessing the connection list took %f msecs.", timer.MSecsElapsed()); } for(ConnectionMap::iterator iter = clientsLock->begin(); iter != clientsLock->end(); ++iter) { MessageConnection *connection = iter->second; assert(connection); if (connection == exclude || !connection->IsWriteOpen()) continue; NetworkMessage *msg = connection->StartNewMessage(id, numBytes); msg->reliable = reliable; msg->inOrder = inOrder; msg->priority = priority; msg->contentID = contentID; assert(msg->data); assert(msg->Size() == numBytes); memcpy(msg->data, data, numBytes); connection->EndAndQueueMessage(msg); } }
void NetworkServer::ConnectionClosed(MessageConnection *connection) { PolledTimer timer; Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire(); LOG(LogWaits, "NetworkServer::ConnectionClosed: Accessing the connection list took %f msecs.", timer.MSecsElapsed()); for(ConnectionMap::iterator iter = clientsLock->begin(); iter != clientsLock->end(); ++iter) if (iter->second == connection) { if (networkServerListener) networkServerListener->ClientDisconnected(connection); if (connection->GetSocket() && connection->GetSocket()->TransportLayer() == SocketOverTCP) { owner->DeleteSocket(connection->socket); connection->socket = 0; } clientsLock->erase(iter); return; } LOG(LogError, "Unknown MessageConnection passed to NetworkServer::Disconnect!"); }
void Network::DeInit() { LOG(LogVerbose, "Network::DeInit: Closing down."); PolledTimer timer; // Kill all connections. while(connections.size() > 0) { 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.size() > 0) CloseWorkerThread(workerThreads.front()); // Erases the item from workerThreads, so this loop terminates. // Clean up any sockets that might be remaining. while(sockets.size() > 0) { sockets.front().Close(); sockets.pop_front(); } // Deinitialize network subsystem. #ifdef WIN32 WSACleanup(); #endif LOG(LogWaits, "Network::DeInit: Deinitialized kNet Network object, took %f msecs.", timer.MSecsElapsed()); }
void NetworkServer::Process() { CleanupDeadConnections(); for(size_t i = 0; i < listenSockets.size(); ++i) { Socket *listen = listenSockets[i]; if (listen->TransportLayer() == SocketOverTCP) { // Accept the first inbound connection. Socket *client = AcceptConnections(listen); if (client) { if (!client->Connected()) LOG(LogError, "Warning: Accepted an already closed connection!"); LOG(LogInfo, "Client connected from %s.", client->ToString().c_str()); // Build a MessageConnection on top of the raw socket. assert(listen->TransportLayer() == SocketOverTCP); Ptr(MessageConnection) clientConnection = new TCPMessageConnection(owner, this, client, ConnectionOK); assert(owner); owner->AssignConnectionToWorkerThread(clientConnection); if (networkServerListener) networkServerListener->NewConnectionEstablished(clientConnection); { PolledTimer timer; Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire(); (*clientsLock)[clientConnection->RemoteEndPoint()] = clientConnection; LOG(LogWaits, "NetworkServer::Process: Adding new accepted TCP connection to connection list took %f msecs.", timer.MSecsElapsed()); } owner->NewMessageConnectionCreated(clientConnection); } } } // Note that the above loop will only accept one new connection/socket/iteration, so if there are multiple // pending new connections, they will only get accepted at a rate of one per each frame. // Process a new UDP connection attempt. ConnectionAttemptDescriptor *desc = udpConnectionAttempts.Front(); if (desc) { ProcessNewUDPConnectionAttempt(desc->listenSocket, desc->peer, (const char *)desc->data.data, desc->data.size); udpConnectionAttempts.PopFront(); } // Process all new inbound data for each connection handled by this server. ConnectionMap clientMap = *clients.Acquire(); for(ConnectionMap::iterator iter = clientMap.begin(); iter != clientMap.end(); ++iter) iter->second->Process(); }
bool NetworkServer::ProcessNewUDPConnectionAttempt(Socket *listenSocket, const EndPoint &endPoint, const char *data, size_t numBytes) { LOG(LogInfo, "New inbound connection attempt from %s with datagram of size %d.", endPoint.ToString().c_str(), (int)numBytes); if (!acceptNewConnections) { LOG(LogError, "Ignored a new connection attempt since server is set not to accept new connections."); return false; } // Pass the datagram contents to a callback that decides whether this connection is allowed. if (networkServerListener) { bool connectionAccepted = networkServerListener->NewConnectionAttempt(endPoint, data, numBytes); if (!connectionAccepted) { LOG(LogError, "Server listener did not accept the new connection."); return false; } } ///\todo Check IP banlist. ///\todo Check that the maximum number of active concurrent connections is not exceeded. std::string remoteHostName = endPoint.IPToString(); // Accept the connection and create a new UDP socket that communicates to that endpoint. Socket *socket = owner->CreateUDPSlaveSocket(listenSocket, endPoint, remoteHostName.c_str()); if (!socket) { LOG(LogError, "Network::ConnectUDP failed! Cannot accept new UDP connection."); return false; } UDPMessageConnection *udpConnection = new UDPMessageConnection(owner, this, socket, ConnectionOK); Ptr(MessageConnection) connection(udpConnection); { PolledTimer timer; Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire(); (*clientsLock)[endPoint] = connection; LOG(LogWaits, "NetworkServer::ProcessNewUDPConnectionAttempt: Accessing the connection list took %f msecs.", timer.MSecsElapsed()); } // Pass the MessageConnection to the main application so it can hook the inbound packet stream. if (networkServerListener) networkServerListener->NewConnectionEstablished(connection); connection->SendPingRequestMessage(false); owner->AssignConnectionToWorkerThread(connection); owner->NewMessageConnectionCreated(connection); LOG(LogInfo, "Accepted new UDP connection."); return true; }
NetworkServer::ConnectionMap NetworkServer::GetConnections() { PolledTimer timer; Lockable<ConnectionMap>::LockType lock = clients.Acquire(); if (timer.MSecsElapsed() > 50.f) { LOG(LogWaits, "NetworkServer::GetConnections: Accessing the connection list took %f msecs.", timer.MSecsElapsed()); } return *lock; }
void NetworkServer::DisconnectAllClients() { SetAcceptNewConnections(false); PolledTimer timer; Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire(); LOG(LogWaits, "NetworkServer::DisconnectAllClients: Accessing the connection list took %f msecs.", timer.MSecsElapsed()); for(ConnectionMap::iterator iter = clientsLock->begin(); iter != clientsLock->end(); ++iter) iter->second->Disconnect(0); // Do not wait for any client. }
void NetworkServer::ReadUDPSocketData(Socket *listenSocket) // [worker thread] { using namespace std; assert(listenSocket); OverlappedTransferBuffer *recvData = listenSocket->BeginReceive(); if (!recvData) return; // No datagram available, return. if (recvData->bytesContains == 0) { listenSocket->EndReceive(recvData); LOG(LogError, "Received 0 bytes of data in NetworkServer::ReadUDPSocketData!"); return; } EndPoint endPoint = EndPoint::FromSockAddrIn(recvData->from); // This conversion is quite silly, perhaps it could be removed to gain performance? LOG(LogData, "Received a datagram of size %d to socket %s from endPoint %s.", recvData->bytesContains, listenSocket->ToString().c_str(), endPoint.ToString().c_str()); PolledTimer timer; MessageConnection *receiverConnection = 0; { Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire(); if (timer.MSecsElapsed() > 50.f) { LOG(LogWaits, "NetworkServer::ReadUDPSocketData: Accessing the connection list in UDP server receive code took %f msecs.", timer.MSecsElapsed()); } ConnectionMap::iterator iter = clientsLock->find(endPoint); ///\todo HashTable for performance. if (iter != clientsLock->end()) receiverConnection = iter->second; } if (receiverConnection) { // If the datagram came from a known endpoint, pass it to the connection object that handles that endpoint. UDPMessageConnection *udpConnection = dynamic_cast<UDPMessageConnection *>(receiverConnection); if (udpConnection) udpConnection->QueueInboundDatagram(recvData->buffer.buf, recvData->bytesContains); else LOG(LogError, "Critical! UDP socket data received into a TCP socket!"); } else { // The endpoint for this datagram is not known, deserialize it as a new connection attempt packet. EnqueueNewUDPConnectionAttempt(listenSocket, endPoint, recvData->buffer.buf, recvData->bytesContains); } listenSocket->EndReceive(recvData); }
void Thread::CheckHold() { if (threadHoldEvent.Test()) { LOG(LogVerbose, "Thread::CheckHold(): suspending thread. this: %p.", this); PolledTimer timer; while(!ShouldQuit()) { threadHoldEventAcked.Set(); bool success = threadResumeEvent.Wait(1000); if (success) break; } LOG(LogWaits, "Thread::CheckHold: Slept for %f msecs.", timer.MSecsElapsed()); threadHoldEventAcked.Reset(); } }
void NetworkServer::BroadcastMessage(const NetworkMessage &msg, MessageConnection *exclude) { PolledTimer timer; Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire(); if (timer.MSecsElapsed() >= 50.f) { LOG(LogWaits, "NetworkServer::BroadcastMessage: Accessing the connection list took %f msecs.", timer.MSecsElapsed()); } for(ConnectionMap::iterator iter = clientsLock->begin(); iter != clientsLock->end(); ++iter) { MessageConnection *connection = iter->second; if (connection == exclude) continue; SendMessage(msg, *connection); } }
void NetworkServer::Close(int disconnectWaitMilliseconds) { DisconnectAllClients(); ///\todo Re-implement this function to remove the monolithic Sleep here. Instead of this, /// wait for the individual connections to finish. if (GetConnections().size() > 0) { Clock::Sleep(disconnectWaitMilliseconds); LOG(LogVerbose, "NetworkServer::Close: Waited a fixed period of %d msecs for all connections to disconnect.", disconnectWaitMilliseconds); } PolledTimer timer; Lockable<ConnectionMap>::LockType clientsLock = clients.Acquire(); LOG(LogWaits, "NetworkServer::Close: Accessing the connection list took %f msecs.", timer.MSecsElapsed()); for(ConnectionMap::iterator iter = clientsLock->begin(); iter != clientsLock->end(); ++iter) iter->second->Close(0); // Do not wait for any client. }
/// Suspends the thread until 'Resume()' is called. Call this function from the main thread. void Thread::Hold() { if (threadHoldEvent.Test()) return; threadResumeEvent.Reset(); threadHoldEvent.Reset(); threadHoldEventAcked.Reset(); threadHoldEvent.Set(); PolledTimer timer; while(IsRunning()) { bool success = threadHoldEventAcked.Wait(1000); if (success) break; } LOG(LogWaits, "Thread::Hold: Took %f msecs.", timer.MSecsElapsed()); }
void MessageConnection::WaitForMessage(int maxMSecsToWait) // [main thread] { AssertInMainThreadContext(); // If we have a message to process, no need to wait. if (inboundMessageQueue.Size() > 0) return; // Check the status of the connection worker thread. if (connectionState == ConnectionClosed) { if (socket) Close(); return; } // Wait indefinitely until we get a new message, or the connection is torn down. if (maxMSecsToWait == 0) { ///\todo Log out warning if this takes AGES. Or rather, perhaps remove support for this altogether /// to avoid deadlocks. while(inboundMessageQueue.Size() == 0 && GetConnectionState() == ConnectionOK) Clock::Sleep(1); ///\todo Instead of waiting multiple 1msec slices, should wait for proper event. } else { PolledTimer timer; timer.StartMSecs((float)maxMSecsToWait); while(inboundMessageQueue.Size() == 0 && GetConnectionState() == ConnectionOK && !timer.Test()) Clock::Sleep(1); ///\todo Instead of waiting multiple 1msec slices, should wait for proper event. if (timer.MSecsElapsed() >= 1000.f) { LOG(LogWaits, "MessageConnection::WaitForMessage: Waited %f msecs for a new message. ConnectionState: %s. %d messages in queue.", timer.MSecsElapsed(), ConnectionStateToString(GetConnectionState()).c_str(), (int)inboundMessageQueue.Size()); } } }