Client* ClientConnectionSet::Add(LPSOCKADDR_IN addr) { int newclientnum; // Get a random uniq client number Client* testclient; do { newclientnum = psserver->rng->Get(0x8fffff); //make clientnum random testclient = FindAny(newclientnum); } while(testclient != NULL); // Have uniq client number, create the new client Client* client = new Client(); if(!client->Initialize(addr, newclientnum)) { Bug1("Client Init failed?!?\n"); delete client; return NULL; } CS::Threading::RecursiveMutexScopedLock lock(mutex); addrHash.PutUnique(SockAddress(client->GetAddress()), client); hash.Put(client->GetClientNum(), client); return client; }
void ClientsManager::run(void) { //Make sure the thread stays running while (!threadShouldExit())//as long as the thread should stay running { if (indexpointer <= -1) { juce::Thread::yield();//give some more time back to other threads before processing clients again lockProcess->enterRead();//make sure size() is properly readable maxsize = clientslist->size();//Let's see if there is anything to process lockProcess->exitRead();//Reading is done, no need to keep it mutex locked if (maxsize <= 0)//No clients... { juce::Thread::sleep(1);//no clients, wait 1 millisecond } else//We have clients to process! { //possibly initialise codes before client looping starts? laststamp = juce::Time::currentTimeMillis(); //assign indexpointer to last client indexpointer = (maxsize - 1); } } else //There are clients to process as long indexpointer >= 0 { lockProcess->enterWrite();//lock the clientslist so this client pointer doesn't magically dissappear maxsize = clientslist->size();//Let's see if the next client didn't suddenly dissappear if (indexpointer >= maxsize)//The wanted index is not set.. { indexpointer = (maxsize - 1);//take the last used index and use that one instead } //Get client Client* c = clientslist->getUnchecked(indexpointer); int currentindex = indexpointer; //move to next client next iteration or if process is somehow interrupted --indexpointer; //process the client.. if (c->IsConnectionStateExpired(laststamp)) { if (c->CheckIfDisconnected()) { c->MarkForDeletion(); } } if (c->IsMarkedForDeletion()) { c->Disconnect(); if (serverprocessor != NULL)//special exception for server functions { serverprocessor->ClientWasRemoved(c); } #ifdef FEEDBACK_CLIENTSMANAGER_REMOVED_CLIENTS juce::String message("Client `"); const std::string* clientname = c->GetClientName(); if (clientname->length() > 0)//if name is set { message += *clientname; } else { message += CLIENT_UNKNOWN_NAME_TEXT; } message += "` ("; message += *c->GetAddress(); message += ':'; message += *c->GetPort(); message += ") disconnected"; //possibly a switch case is needed here, for windows vs mac vs linux message += '\n';//newline std::cout << message; #endif arraylock.enter(); clientslist->remove(currentindex, true); arraylock.exit(); } else { /* Here, a mechanic could be added to stop execution time of thread after client processing time. If time exceeds the time threshol, restart thread. */ //process incoming data c->ProcessBuffers(); //check if there is data to process if (c->HasBufferedCommunicationObjects()) { juce::OwnedArray<CommunicationObject>* messages = c->GetBufferedCommunicationObjects(); for (int i = (messages->size() - 1); i >= 0; i--) { CommunicationObject* message = messages->getUnchecked(i); messages->remove(i, false); /* std::cout << "TEST: " << message->ToString(c->GetLastClassPresentationProtocol()) << std::endl; */ CommunicationObjectType* devicename = message->ClearDataIndex(0); if (devicename != NULL) { juce::juce_wchar type = devicename->GetType(); if (type == 's' || type == 'S' || type == 'A') { char* devicenamecharpointer = (char*)devicename->GetRawDataCopy(); juce::String devicenamestring(devicenamecharpointer); delete devicenamecharpointer; delete devicename; switch (devicenamestring[0])//this message is meant for a group { case '#': { devicenamestring = devicenamestring.substring(1); if (serverprocessor != NULL)//special exception for server functions { if (devicenamestring.equalsIgnoreCase("server")) { serverprocessor->ProcesssFunction(c, message); continue; } } //a group of clients { if (c->IsFullyIdentified())//only clients that are identified are allowed to do this { const std::string* this_clientname = c->GetClientName(); message->ReplaceDataIndex(0, new CommunicationObjectType(*this_clientname)); for (int u = (maxsize - 1); u >= 0; u--)//and iterate backwards { Client* t = clientslist->getUnchecked(u); if (t != c && t->IsFullyIdentified())//do not self to self and target must be identified { if (!t->MarkedSenderOnly() && t->IsClientPartOfGroup(devicenamestring)) { t->Send(message); } } } } } break;//# } case '?'://meant for every client (send message to every client) { if (c->IsFullyIdentified())//only clients that are identified are allowed to do this { const std::string* this_clientname = c->GetClientName(); message->ReplaceDataIndex(0, new CommunicationObjectType(*this_clientname)); for (int u = (maxsize - 1); u >= 0; u--)//and iterate backwards { Client* t = clientslist->getUnchecked(u); if (!t->MarkedSenderOnly() && t != c)//do not send to self { t->Send(message); } } } break;//? } case '!'://meant for every registered client (send message to every registered client) { if (c->IsFullyIdentified())//only clients that are identified are allowed to do this { const std::string* this_clientname = c->GetClientName(); message->ReplaceDataIndex(0, new CommunicationObjectType(*this_clientname)); for (int u = (maxsize - 1); u >= 0; u--)//and iterate backwards { Client* t = clientslist->getUnchecked(u); if (!t->MarkedSenderOnly() && t != c && t->IsFullyIdentified())//do not send to self { t->Send(message); } } } break;//! } default://compare name { if (c->IsFullyIdentified())//only clients that are identified are allowed to do this { const std::string* this_clientname = c->GetClientName(); message->ReplaceDataIndex(0, new CommunicationObjectType(*this_clientname)); for (int u = (maxsize - 1); u >= 0; u--)//and iterate backwards { Client* t = clientslist->getUnchecked(u); if (!t->MarkedSenderOnly() && t != c && t->IsFullyIdentified())//do not self to self and target must be identified { if (devicenamestring.compareNatural(juce::StringRef(*t->GetClientName()))) { t->Send(message); break; } } } } break; } } } else { delete devicename; } } delete message; } messages->clear(false); delete messages; //messages = NULL; } } lockProcess->exitWrite();//unlock mutex //Give other threads more processing time by yielding after X time //The following algorithm checks whether the amount of clients superseeds or underseeds the load processing //If there are many clients, the yield will happen less often if (++AdaptiveLoad_CurrentBatchSizeClientProcessing >= AdaptiveLoad_BatchSizeClientProcessing) { //after processing X clients, reset counter so yielding won't happen immediately after AdaptiveLoad_CurrentBatchSizeClientProcessing = 0; if (maxsize - AdaptiveLoad_BatchSizeClientProcessing > AdaptiveLoad_HeavyLoadNumClientsThreshhold)//if is bigger { AdaptiveLoad_BatchSizeClientProcessing += AdaptiveLoad_HeavyLoadNumClientsThreshhold; } else if (AdaptiveLoad_BatchSizeClientProcessing - maxsize > AdaptiveLoad_HeavyLoadNumClientsThreshhold) { AdaptiveLoad_BatchSizeClientProcessing -= AdaptiveLoad_HeavyLoadNumClientsThreshhold; if (AdaptiveLoad_BatchSizeClientProcessing < AdaptiveLoad_HeavyLoadNumClientsThreshhold) { AdaptiveLoad_BatchSizeClientProcessing = AdaptiveLoad_HeavyLoadNumClientsThreshhold;//make sure at least some clients are processed } } juce::Thread::yield();//yield once to provide an additional time slice for other threads } } } }