Merge branch 'main/rendor-staging' into fixes
[ryzomcore.git] / nel / src / net / unified_network.cpp
blobd624fcff0d25d3f1be2251d53e2e0ae3e434b9ba
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "stdnet.h"
21 #include "nel/misc/entity_id.h" // for createMessage()
22 #include "nel/misc/variable.h"
23 #include "nel/misc/thread.h"
24 #include "nel/misc/mutex.h"
26 #include "nel/net/unified_network.h"
27 #include "nel/net/module_common.h"
28 #include "nel/net/naming_client.h"
30 #ifdef NL_OS_UNIX
31 #include <sched.h>
32 #endif
34 using namespace std;
35 using namespace NLMISC;
38 namespace NLNET {
40 static size_t ThreadCreator = 0;
42 static const uintptr_t AppIdDeadConnection = 0xDEAD;
44 uint32 TotalCallbackCalled = 0;
46 uint32 TimeInCallback =0;
48 #if defined(NL_OS_UNIX) || defined(NL_OS_WINDOWS)
49 /// Yield method
50 CVariable<uint32> UseYieldMethod("nel", "UseYieldMethod", "0=select 1=usleep 2=nanosleep 3=sched_yield 4=none", 0, 0, true );
51 #endif
53 /// Reduce sending lag
54 CVariable<bool> FlushSendsBeforeSleep("nel", "FlushSendsBeforeSleep", "If true, send buffers will be flushed before sleep, not in next update", true, 0, true );
56 /// Network congestion monitoring
57 CVariable<uint> L5TotalBytesInLowLevelSendQueues("nel", "L5TotalBytesInLowLevelSendQueues", "Number of bytes pending in send queues (postponed by non-blocking send()) for network congestion monitoring. N/A if FlushSendsBeforeSleep disabled)", 0, 0, true );
59 /// Receiving size limit
60 CVariablePtr<uint32> DefaultMaxExpectedBlockSize("nel", "DefaultMaxExpectedBlockSize", "If receiving more than this value in bytes, the connection will be dropped", &CBufNetBase::DefaultMaxExpectedBlockSize, true );
62 /// Sending size limit
63 CVariablePtr<uint32> DefaultMaxSentBlockSize("nel", "DefaultMaxSentBlockSize", "If sending more than this value in bytes, the program may be stopped", &CBufNetBase::DefaultMaxSentBlockSize, true );
65 #define AUTOCHECK_DISPLAY nlwarning
66 //#define AUTOCHECK_DISPLAY CUnifiedNetwork::getInstance()->displayInternalTables (), nlerror
68 const TServiceId TServiceId::InvalidId = TServiceId(std::numeric_limits<uint16>::max());
71 // Callbacks from NAMING SERVICE
74 // when a service registers
75 void uNetRegistrationBroadcast(const string &name, TServiceId sid, const vector<CInetAddress> &addr)
77 nldebug ("HNETL5: + naming %s-%u '%s'", name.c_str(), sid.get(), vectorCInetAddressToString(addr).c_str ());
79 CUnifiedNetwork *uni= CUnifiedNetwork::getInstance();
81 if (uni->_SId == sid)
83 // it's me! don't add me!!!
84 return;
87 // add the unified connection
89 if(sid.get() >= uni->_IdCnx.size ())
90 uni->_IdCnx.resize (sid.get()+1);
92 if (uni->_IdCnx[sid.get()].State == CUnifiedNetwork::CUnifiedConnection::NotUsed)
94 uni->_IdCnx[sid.get()] = CUnifiedNetwork::CUnifiedConnection(name, sid, false);
95 uni->_UsedConnection.push_back (sid);
98 if (!uni->_IdCnx[sid.get()].ExtAddress.empty ())
99 AUTOCHECK_DISPLAY ("HNETL5: %s-%u already inserted in the table with '%s'", name.c_str(), sid.get(), vectorCInetAddressToString (uni->_IdCnx[sid.get()].ExtAddress).c_str ());
101 // set the list of external addresses
103 nlassert (!addr.empty());
105 uni->_IdCnx[sid.get()].ExtAddress = addr;
107 // associate nid with ext address
108 uni->_IdCnx[sid.get()].setupNetworkAssociation (uni->_NetworkAssociations, uni->_DefaultNetwork);
111 // when a service unregisters
112 void uNetUnregistrationBroadcast(const string &name, TServiceId sid, const vector<CInetAddress> &addr)
114 nldebug ("HNETL5: - naming %s-%u '%s'", name.c_str(), sid.get(), vectorCInetAddressToString (addr).c_str ());
116 // get the service connection
117 CUnifiedNetwork *uni = CUnifiedNetwork::getInstance();
119 CUnifiedNetwork::CUnifiedConnection *uc = uni->getUnifiedConnection (sid);
120 if (uc == 0) return; // should never happen, the getUnifiedConnection() will generate a AUTOCHECK_DISPLAY
122 // call the user callback
124 uni->callServiceDownCallback(uc->ServiceName, uc->ServiceId);
126 if(!uc->Connections.empty ())
128 // set all connection to dead, now, all messages received on this socket will be ignored and closed
129 for (uint i = 0; i < uc->Connections.size (); ++i)
131 if (uc->Connections[i].valid())
132 uc->Connections[i].setAppId (AppIdDeadConnection);
136 // It's the first connection that added the _NamedCnx so if there's no connection, no need to
137 // remove entry in _NamedCnx
140 uni->removeNamedCnx (uc->ServiceName, uc->ServiceId);
143 // remove the _UsedConnection
144 bool found = false;
145 for (vector<TServiceId>::iterator it = uni->_UsedConnection.begin (); it != uni->_UsedConnection.end(); it++)
147 if (*it == uc->ServiceId)
149 found = true;
150 uni->_UsedConnection.erase (it);
151 break;
154 if (!found)
155 AUTOCHECK_DISPLAY ("HNETL5: can't find the sid %hu in the _UsedConnection", uc->ServiceId.get());
157 // reset the unified connection
158 uc->reset ();
163 // Callbacks from connection/disconnection services
166 void uncbConnection(TSockId from, void * /* arg */)
168 nlinfo ("HNETL5: + connec '%s'", from->asString().c_str());
170 from->setAppId (AppIdDeadConnection);
173 void uncbDisconnection(TSockId from, void * /* arg */)
175 if(from->appId () == AppIdDeadConnection)
177 nlinfo ("HNETL5: - connec '%s'", from->asString().c_str());
179 else
181 CUnifiedNetwork *uni = CUnifiedNetwork::getInstance();
182 TServiceId sid(uint16(from->appId()));
183 CUnifiedNetwork::CUnifiedConnection *uc = uni->getUnifiedConnection (sid);
184 if (uc == 0)
186 nlinfo ("HNETL5: - connec '%s' sid %hu", from->asString().c_str(), sid.get());
188 else
190 nlinfo ("HNETL5: - connec '%s' %s-%hu", from->asString().c_str(), uc->ServiceName.c_str (), sid.get());
192 if (uc->IsExternal)
194 if (!uc->AutoRetry)
196 // If it s a external service with no auto retry, remove the connection
198 // call the user callback
199 uni->callServiceDownCallback(uc->ServiceName, uc->ServiceId);
201 uni->removeNamedCnx (uc->ServiceName, uc->ServiceId);
203 uni->_ConnectionToReset.push_back(uc->ServiceId);
205 else
207 // call the user callback
208 uni->callServiceDownCallback(uc->ServiceName, uc->ServiceId);
211 else
213 // reset the connection
214 uint i;
215 for (i = 0; i < uc->Connections.size (); i++)
217 if (uc->Connections[i].valid() && uc->Connections[i].CbNetBase->getSockId(uc->Connections[i].HostId) == from)
219 if (uc->Connections[i].IsServerConnection)
221 // we have to remove the stuffs now because HostId will not be accessible later
222 uc->Connections[i].reset();
224 else
226 // if it s a client, we can't delete now because the callback client is currently in use
227 // only disconnect
228 if(uc->Connections[i].CbNetBase->connected ())
230 uc->Connections[i].CbNetBase->disconnect (uc->Connections[i].HostId);
233 break;
236 if (i == uc->Connections.size ())
238 AUTOCHECK_DISPLAY ("HNETL5: received a disconnection from a service but the connection is not in my list!");
243 from->setAppId (AppIdDeadConnection);
248 // Callback from identification services
251 void uncbServiceIdentification(CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
253 string inSName;
254 TServiceId inSid;
256 if (from->appId () != AppIdDeadConnection)
257 AUTOCHECK_DISPLAY ("HNETL5: received a connect ident from an unknown connection 0x%" NL_I64 "X", from->appId ());
259 // recover the service name and id
260 msgin.serial(inSName);
261 msgin.serial(inSid);
262 uint8 pos;
263 msgin.serial (pos);
264 bool isExternal;
265 msgin.serial (isExternal);
267 nlinfo ("HNETL5: + connect ident '%s' %s-%hu pos %hu ext %d", from->asString().c_str(), inSName.c_str(), inSid.get(), (uint16)pos, (uint8)isExternal);
269 if(isExternal)
271 nlassert (pos == 0);
274 if (inSid.get() == 0)
276 if (isExternal)
278 inSid = CUnifiedNetwork::getInstance ()->_ExtSId;
279 CUnifiedNetwork::getInstance ()->_ExtSId.set(CUnifiedNetwork::getInstance ()->_ExtSId.get()+1);
280 // the following was an nlwarning but this is in fact desired behavior and not an error so we have changed to nlinfo
281 nlinfo ("HNETL5: Received a connection from a service with a SId 0, we give him the SId %d", inSid.get());
283 else
285 nlwarning ("HNETL5: Received a connection from a service with a SId 0 and wasn't external, disconnecting it");
286 netbase.disconnect();
287 return;
291 from->setAppId(inSid.get());
293 // add a new connection to the list
294 CUnifiedNetwork *uni= CUnifiedNetwork::getInstance();
296 if(inSid.get() >= uni->_IdCnx.size ())
298 uni->_IdCnx.resize (inSid.get()+1);
301 switch(uni->_IdCnx[inSid.get()].State)
303 case CUnifiedNetwork::CUnifiedConnection::NotUsed: // add the new unified connection
304 uni->_IdCnx[inSid.get()] = CUnifiedNetwork::CUnifiedConnection(inSName, inSid, isExternal);
305 uni->_UsedConnection.push_back (inSid);
306 break;
307 default:
308 break;
311 if (uni->_IdCnx[inSid.get()].IsExternal != isExternal)
313 AUTOCHECK_DISPLAY ("HNETL5: Receive a connection that is not totally external %d %d", uni->_IdCnx[inSid.get()].IsExternal, isExternal);
314 return;
317 bool FirstConnection = (uni->_IdCnx[inSid.get()].Connections.size () == 0);
319 // add the connection to the already inserted unified connection
320 if (pos >= uni->_IdCnx[inSid.get()].Connections.size ())
322 uni->_IdCnx[inSid.get()].Connections.resize(pos+1);
324 uni->_IdCnx[inSid.get()].Connections[pos] = CUnifiedNetwork::CUnifiedConnection::TConnection(&netbase, from);
326 // If the connection is external, we'll never receive the ExtAddress by the naming service, so add it manually
327 if (isExternal)
329 uni->_IdCnx[inSid.get()].ExtAddress.push_back (netbase.hostAddress (from));
330 uni->_IdCnx[inSid.get()].setupNetworkAssociation (uni->_NetworkAssociations, uni->_DefaultNetwork);
333 // send the callback to the user with the first connection
334 if (FirstConnection)
336 // insert the name in the map to be able to send message with the name
337 uni->addNamedCnx (inSName, inSid);
339 uni->callServiceUpCallback (inSName, inSid);
343 // the callbacks wrapper
344 void uncbMsgProcessing(CMessage &msgin, TSockId from, CCallbackNetBase &/* netbase */)
346 if (from->appId() == AppIdDeadConnection)
348 AUTOCHECK_DISPLAY ("HNETL5: Receive a message from a dead connection");
349 return;
352 CUnifiedNetwork *uni = CUnifiedNetwork::getInstance();
353 TServiceId sid(uint16(from->appId()));
354 CUnifiedNetwork::TMsgMappedCallback::iterator itcb;
356 itcb = uni->_Callbacks.find(msgin.getName());
357 if (itcb == uni->_Callbacks.end())
359 // the callback doesn't exist
360 nlwarning ("HNETL5: Can't find callback '%s' called by service %hu", msgin.getName().c_str(), sid.get());
362 else
364 CUnifiedNetwork::CUnifiedConnection *uc = uni->getUnifiedConnection (sid);
366 if (uc == 0)
368 nlwarning ("HNETL5: Received a message from a service %hu that is not ready (bad appid? 0x%" NL_I64 "X)", sid.get(), from->appId ());
369 return;
371 if((*itcb).second == 0)
373 nlwarning ("HNETL5: Received message %s from a service %hu but the associated callback is NULL", msgin.getName ().c_str(), sid.get());
374 return;
378 static map<string, CHTimer> timers;
379 map<string, CHTimer>::iterator it;
382 H_AUTO(L5UCHTimerOverhead);
383 string callbackName = "USRCB_" + msgin.getName();
384 it = timers.find(callbackName);
385 if(it == timers.end())
387 it = timers.insert(make_pair(callbackName, CHTimer(NULL))).first;
388 (*it).second.setName((*it).first.c_str());
393 H_AUTO(L5UserCallback);
395 TTime before = CTime::getLocalTime();
397 (*it).second.before();
398 // const std::string &cbName = itcb->first;
399 (*itcb).second (msgin, uc->ServiceName, sid);
400 (*it).second.after();
402 TTime after = CTime::getLocalTime();
404 // sum the time used to do callback
405 TimeInCallback += uint32((after-before));
409 uc->TotalCallbackCalled++;
410 TotalCallbackCalled++;
415 TCallbackItem unServerCbArray[] =
417 { "UN_SIDENT", uncbServiceIdentification }
422 // Alive check thread
425 class CAliveCheck : public NLMISC::IRunnable
427 public:
428 CAliveCheck() : ExitRequired(false) { }
430 virtual void run();
431 virtual ~CAliveCheck() { }
433 volatile bool ExitRequired;
435 static CAliveCheck* Thread;
437 struct CCheckAddress
439 CCheckAddress() : ConnectionId(0xDEAD), NeedCheck(false), AddressValid(false) { }
440 CInetAddress Address;
441 std::string ServiceName;
442 TServiceId ServiceId;
443 uint ConnectionId;
444 uint ConnectionIndex;
445 volatile bool NeedCheck;
446 volatile bool AddressValid;
449 CCheckAddress CheckList[128];
451 void checkService(CInetAddress address, uint connectionId, uint connectionIndex, const std::string &service, TServiceId id);
455 CAliveCheck* CAliveCheck::Thread = NULL;
456 IThread* AliveThread = NULL;
459 void CAliveCheck::run()
461 // setup thread
462 Thread = this;
464 TTime t = CTime::getLocalTime();
466 while (!ExitRequired)
468 if (CTime::getLocalTime() - t < 10000)
470 nlSleep(100);
471 continue;
474 uint i;
475 for (i=0; i<sizeof(CheckList)/sizeof(CheckList[0]); ++i)
477 if (CheckList[i].NeedCheck && !CheckList[i].AddressValid)
481 CCallbackClient cbc;
482 cbc.connect(CheckList[i].Address);
483 // success (no exception)
484 CheckList[i].AddressValid = true;
485 cbc.disconnect();
487 catch (const ESocketConnectionFailed &e)
489 #if FINAL_VERSION
490 nlinfo ("HNETL5: can't connect to %s-%hu now (%s)", CheckList[i].ServiceName.c_str(), CheckList[i].ServiceId.get(), e.what ());
491 #else
492 nlwarning ("HNETL5: can't connect to %s-%hu now (%s)", CheckList[i].ServiceName.c_str(), CheckList[i].ServiceId.get(), e.what ());
493 #endif
498 t = CTime::getLocalTime();
501 ExitRequired = false;
504 void CAliveCheck::checkService(CInetAddress address, uint connectionId, uint connectionIndex, const std::string &service, TServiceId id)
506 uint i;
507 for (i=0; i<sizeof(CheckList)/sizeof(CheckList[0]); ++i)
509 if (CheckList[i].NeedCheck)
510 continue;
512 CheckList[i].Address = address;
513 CheckList[i].ConnectionId = connectionId;
514 CheckList[i].ConnectionIndex = connectionIndex;
515 CheckList[i].ServiceName = service;
516 CheckList[i].ServiceId = id;
518 CheckList[i].AddressValid = false;
519 CheckList[i].NeedCheck = true;
521 return;
530 bool CUnifiedNetwork::init(const CInetAddress *addr, CCallbackNetBase::TRecordingState rec,
531 const string &shortName, uint16 port, TServiceId &sid)
533 // the commands can now be invoked
534 registerCommandsHandler();
536 if (_Initialised)
538 AUTOCHECK_DISPLAY ("HNETL5: Unified network layer already initialized");
539 return true;
542 ThreadCreator = NLMISC::getThreadId();
544 vector<CInetAddress> laddr = CInetAddress::localAddresses();
546 _RecordingState = rec;
547 _Name = shortName;
548 _SId = sid;
550 if (addr != 0)
551 _NamingServiceAddr = *addr;
553 // if the address isn't 0, uses the naming service
554 if (_NamingServiceAddr.isValid ())
556 // connect the callback to know when a new service comes in or goes down
557 CNamingClient::setRegistrationBroadcastCallback(uNetRegistrationBroadcast);
558 CNamingClient::setUnregistrationBroadcastCallback(uNetUnregistrationBroadcast);
560 // connect to the naming service (may generate a ESocketConnectionFailed exception)
561 CNamingClient::connect(_NamingServiceAddr, _RecordingState, laddr);
563 if (port == 0)
564 port = CNamingClient::queryServicePort ();
567 #if defined(NL_OS_UNIX)
568 /// Init the main pipe to select() on data available
569 if ( ::pipe( _MainDataAvailablePipe ) != 0 )
570 nlwarning( "Unable to create main D.A. pipe" );
571 //nldebug( "Pipe: created" );
572 #elif defined(NL_OS_WINDOWS)
573 _MainDataAvailableHandle = CreateEventW(NULL, FALSE, FALSE, NULL);
574 if (!_MainDataAvailableHandle)
575 nlwarning("Unable to create main D.A. event");
576 #endif
578 // setup the server callback only if server port != 0, otherwise there's no server callback
579 _ServerPort = port;
581 if(_ServerPort != 0)
583 nlassert (_CbServer == 0);
584 _CbServer = new CCallbackServer( CCallbackNetBase::Off, "", true, false ); // don't init one pipe per connection
585 #if defined(NL_OS_UNIX)
586 _CbServer->setExternalPipeForDataAvailable( _MainDataAvailablePipe ); // the main pipe is shared for all connections
587 //nldebug( "Pipe: set (server %p)", _CbServer );
588 #elif defined(NL_OS_WINDOWS)
589 _CbServer->setExternalPipeForDataAvailable(_MainDataAvailableHandle);
590 #endif
591 bool retry = false;
594 retry = false;
597 _CbServer->init(port);
599 catch (const ESocket &)
601 nlwarning("Failed to init the listen socket on port %u, is the service already running ?", port);
602 // wait a little before retrying
603 nlSleep(5000);
605 retry = true;
607 } while(retry);
609 _CbServer->addCallbackArray(unServerCbArray, 1); // the service ident callback
610 _CbServer->setDefaultCallback(uncbMsgProcessing); // the default callback wrapper
611 _CbServer->setConnectionCallback(uncbConnection, NULL);
612 _CbServer->setDisconnectionCallback(uncbDisconnection, NULL);
614 else
616 nlinfo ("HNETL5: ServerPort is 0 so I don't create a CCallbackServer");
619 if (CNamingClient::connected())
621 // register the service
622 for (uint i = 0; i < laddr.size(); i++)
623 laddr[i].setPort(_ServerPort);
625 if (_SId.get() == 0)
627 if ( ! CNamingClient::registerService(_Name, laddr, _SId) )
629 nlinfo ("HNETL5: Registration denied");
630 return false;
633 else
635 CNamingClient::registerServiceWithSId(_Name, laddr, _SId);
638 sid = _SId;
640 nlinfo ("HNETL5: Server '%s' added, registered and listen to port %hu", _Name.c_str (), _ServerPort);
643 AliveThread = IThread::create(new CAliveCheck(), 1024*4);
644 AliveThread->start();
646 _Initialised = true;
647 return true;
650 void CUnifiedNetwork::connect()
652 nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::connect() whereas it is not initialised yet"));
654 if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
656 if (CNamingClient::connected())
658 // get the services list
659 const list<CNamingClient::CServiceEntry> &services = CNamingClient::getRegisteredServices();
661 // connects to the registered services
662 list<CNamingClient::CServiceEntry>::const_iterator its;
664 // don't connect to itself
665 for (its = services.begin(); its != services.end(); ++its)
667 if (_SId != (*its).SId)
669 // add service with name, address, ident, not external, service id, and not autoretry (obsolete)
670 // we put the last true because the name callback should already inserted it by uNetRegistrationBroadcast()
671 addService((*its).Name, (*its).Addr, true, false, (*its).SId, false, true);
673 else
675 // don't process services received after mine because they'll connect to me
676 break;
682 void CUnifiedNetwork::release(bool mustFlushSendQueues, const std::vector<std::string>& namesOfOnlyServiceToFlushSending)
684 if (!_Initialised)
685 return;
687 // the commands can't be invoked
688 unregisterCommandsHandler();
690 // terminate the auto reconnection thread
691 if (CAliveCheck::Thread)
693 CAliveCheck::Thread->ExitRequired = true;
694 AliveThread->wait();
695 delete CAliveCheck::Thread;
696 delete AliveThread;
699 if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
701 // Ensure all outgoing data are sent before disconnecting, if requested
702 if ( mustFlushSendQueues )
704 nlinfo( "HNETL5: Flushing sending queues..." );
705 float totalBytes = 0;
706 uint bytesRemaining, i=0;
707 while ( (bytesRemaining = tryFlushAllQueues( namesOfOnlyServiceToFlushSending )) != 0 )
709 if ( i == 0 )
710 totalBytes = (float)bytesRemaining;
711 if ( i % 20 == 0 )
713 nldebug( "%.1f%% of %.3f KB flushed so far", // display without HNETL5 to bypass filters!
714 ((float)(bytesRemaining-totalBytes))/totalBytes, totalBytes / 1024.0f );
717 ++i;
719 nlSleep( 100 );
721 nldebug( "HNETL5: Flush done in %u steps", i+1 );
724 // disconnect all clients
725 if(_CbServer)
727 _CbServer->disconnect(InvalidSockId);
728 delete _CbServer;
729 _CbServer = 0;
732 // disconnect all connections to servers
733 for (uint i = 0; i<_IdCnx.size(); ++i)
735 if (_IdCnx[i].State != CUnifiedNetwork::CUnifiedConnection::NotUsed)
737 for(uint j = 0 ; j < _IdCnx[i].Connections.size (); j++)
739 if (_IdCnx[i].Connections[j].valid() && !_IdCnx[i].Connections[j].IsServerConnection)
741 if (_IdCnx[i].Connections[j].CbNetBase->connected ())
742 _IdCnx[i].Connections[j].CbNetBase->disconnect();
744 delete _IdCnx[i].Connections[j].CbNetBase;
747 _IdCnx[i].Connections.clear ();
751 // clear all other data
752 _IdCnx.clear();
753 _UsedConnection.clear ();
754 _NamedCnx.clear();
755 _UpCallbacks.clear();
756 _DownCallbacks.clear();
757 _Callbacks.clear();
759 // disconnect the connection with the naming service
760 if (CNamingClient::connected ())
761 CNamingClient::disconnect ();
763 #if defined(NL_OS_UNIX)
764 ::close( _MainDataAvailablePipe[PipeRead] );
765 ::close( _MainDataAvailablePipe[PipeWrite] );
766 #elif defined(NL_OS_WINDOWS)
767 if (_MainDataAvailableHandle)
769 CloseHandle(_MainDataAvailableHandle);
770 _MainDataAvailableHandle = NULL;
772 #endif
775 void CUnifiedNetwork::addService(const string &name, const CInetAddress &addr, bool sendId, bool external, TServiceId sid, bool autoRetry, bool shouldBeAlreayInserted)
777 vector <CInetAddress> addrs;
778 addrs.push_back (addr);
779 addService (name, addrs, sendId, external, sid, autoRetry, shouldBeAlreayInserted);
782 void CUnifiedNetwork::addService(const string &name, const vector<CInetAddress> &addr, bool sendId, bool external, TServiceId sid, bool autoRetry, bool shouldBeAlreayInserted)
784 nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::addService() whereas it is not initialised yet"));
786 if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
788 if (external)
790 sid = _ExtSId;
791 _ExtSId.set(_ExtSId.get()+1);
794 nlinfo("HNETL5: addService %s-%hu '%s'", name.c_str(), sid.get(), vectorCInetAddressToString(addr).c_str());
796 if (external && addr.size () != 1)
798 AUTOCHECK_DISPLAY ("HNETL5: Can't add external service with more than one connection");
801 // add the entry in the unified connection table
803 if (sid.get() >= _IdCnx.size())
804 _IdCnx.resize(sid.get()+1);
806 CUnifiedConnection *uc = &_IdCnx[sid.get()];
808 // at this point it s possible that the service already added in the _IdCnx by the uNetRegistrationBroadcast()
810 if (shouldBeAlreayInserted && _IdCnx[sid.get()].State == CUnifiedNetwork::CUnifiedConnection::NotUsed) AUTOCHECK_DISPLAY ("HNETL5: the unified connection should already set by the naming reg broadcast and is not (%hu)", sid.get());
811 if (!shouldBeAlreayInserted && _IdCnx[sid.get()].State == CUnifiedNetwork::CUnifiedConnection::Ready) AUTOCHECK_DISPLAY ("HNETL5: the unified connection should not already set but is (%hu)", sid.get());
813 if (_IdCnx[sid.get()].State == CUnifiedNetwork::CUnifiedConnection::NotUsed)
815 *uc = CUnifiedConnection(name, sid, external);
816 _UsedConnection.push_back (sid);
818 else
820 // If the entry already set, check that all is correct
821 if (name != uc->ServiceName) AUTOCHECK_DISPLAY ("HNETL5: name are different in addService %s %s", name.c_str (), uc->ServiceName.c_str ());
822 if (sid != uc->ServiceId) AUTOCHECK_DISPLAY ("HNETL5: sid are different in addService %hu %hu", sid.get(), uc->ServiceId.get());
823 if (addr != uc->ExtAddress) AUTOCHECK_DISPLAY ("HNETL5: external addr are different in addService '%s' '%s'", vectorCInetAddressToString(addr).c_str(), vectorCInetAddressToString(uc->ExtAddress).c_str ());
825 uc->AutoRetry = autoRetry;
826 uc->SendId = sendId;
827 uc->ExtAddress = addr;
828 nlassert (!addr.empty());
830 // associate nid with ext address
831 uc->setupNetworkAssociation (_NetworkAssociations, _DefaultNetwork);
833 // connect to all connection
834 bool connectSuccess;
836 if (uc->Connections.size () < addr.size ())
838 uc->Connections.resize (addr.size ());
841 vector<CInetAddress> laddr = CInetAddress::localAddresses();
843 for (uint i = 0; i < addr.size(); i++)
845 // first we have to look if we have a network that can established the connection
846 uint j = 0;
848 // it's loopback ip address, it's ok
849 if (!addr[i].isLoopbackIPAddress())
851 for (j = 0; j < laddr.size (); j++)
853 if (laddr[j].internalNetAddress () == addr[i].internalNetAddress ())
855 // it's ok, we can try
856 break;
860 // If we don't found a valid network, we'll try with the first one.
861 // It's happen, for example, when you try to connect to a service that is not in the network but use IP translation
862 if (j == laddr.size ())
864 nlwarning ("HNETL5: I can't access '%s' because I haven't a net card on this network, we'll use the first network", addr[i].asString ().c_str ());
865 j = 0;
869 // Create a new connection with the service, setup callback and connect
870 CCallbackClient *cbc = new CCallbackClient( CCallbackNetBase::Off, "", true, false ); // don't init one pipe per connection
871 #if defined(NL_OS_UNIX)
872 cbc->setExternalPipeForDataAvailable( _MainDataAvailablePipe ); // the main pipe is shared for all connections
873 //nldebug( "Pipe: set (client %p)", cbc );
874 #elif defined(NL_OS_WINDOWS)
875 cbc->setExternalPipeForDataAvailable(_MainDataAvailableHandle);
876 #endif
877 cbc->setDisconnectionCallback(uncbDisconnection, NULL);
878 cbc->setDefaultCallback(uncbMsgProcessing);
879 cbc->getSockId()->setAppId(sid.get());
883 cbc->connect(addr[i]);
884 connectSuccess = true;
886 catch (const ESocketConnectionFailed &e)
888 nlwarning ("HNETL5: can't connect to %s (sid %u) now (%s) '%s'", name.c_str(), sid.get(), e.what (), addr[i].asString ().c_str());
889 connectSuccess = false;
892 if (!connectSuccess && !autoRetry)
894 nlwarning ("HNETL5: Can't add service because no retry and can't connect");
895 delete cbc;
897 else
899 uc->Connections[i] = CUnifiedNetwork::CUnifiedConnection::TConnection(cbc);
902 if (connectSuccess && sendId)
904 // send identification to the service
905 CMessage msg("UN_SIDENT");
906 msg.serial(_Name);
907 TServiceId ssid = _SId;
908 if (uc->IsExternal)
910 // in the case that the service is external, we can't send our sid because the external service can
911 // have other connection with the same sid (for example, LS can have 2 WS with same sid => sid = 0 and leave
912 // the other side to find a good number
913 ssid.set(0);
915 msg.serial(ssid); // serializes a 16 bits service id
916 uint8 pos = uint8(j);
917 msg.serial(pos); // send the position in the connection table
918 msg.serial (uc->IsExternal);
919 cbc->send (msg);
923 if (addr.size () != uc->Connections.size())
925 nlwarning ("HNETL5: Can't connect to all connections to the service %d/%d", addr.size (), uc->Connections.size());
928 bool cntok = false;
929 for (uint j = 0; j < uc->Connections.size(); j++)
931 if (uc->Connections[j].CbNetBase != NULL)
933 if (uc->Connections[j].CbNetBase->connected ())
935 cntok = true;
936 break;
941 if (cntok)
943 // add the name only if at least one connection is ok
944 addNamedCnx (name, sid);
946 callServiceUpCallback (name, sid); // global callback ("*") will be called even for external service
949 nldebug ("HNETL5: addService was successful");
955 #define TIME_BLOCK(tick, instr) \
957 TTicks _time_block_before = CTime::getPerformanceTime(); \
958 instr ; \
959 TTicks _time_block_after = CTime::getPerformanceTime(); \
960 tick += (_time_block_after - _before); \
966 void CUnifiedNetwork::update(TTime timeout)
968 H_AUTO(CUnifiedNetworkUpdate);
970 H_BEFORE(UNMisc1);
971 nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::update() whereas it is not initialised yet"));
973 if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
975 bool enableRetry; // true every 5 seconds to reconnect if necessary
977 // Compute the real timeout based on the next update timeout
978 TTime t0 = CTime::getLocalTime ();
979 if (timeout > 0)
981 if (_NextUpdateTime == 0)
983 _NextUpdateTime = t0 + timeout;
985 else
987 TTime err = t0 - _NextUpdateTime;
988 _NextUpdateTime += timeout;
990 // if we are too late, resync to the next value
991 while (err > timeout)
993 err -= timeout;
994 _NextUpdateTime += timeout;
997 timeout -= err;
998 if (timeout < 0) timeout = 0;
1001 TTime remainingTime = timeout;
1003 // check if we need to retry to connect to the client
1004 enableRetry = (t0-_LastRetry > 5000);
1005 if (enableRetry)
1006 _LastRetry = t0;
1008 H_AFTER(UNMisc1);
1010 H_BEFORE(UNNamingCheck);
1011 // Try to reconnect to the naming service if connection lost
1012 if (_NamingServiceAddr.isValid ())
1014 if (CNamingClient::connected ())
1016 CNamingClient::update ();
1018 else if (enableRetry)
1020 H_AUTO(L5NSReconnect);
1023 vector<CInetAddress> laddr = CInetAddress::localAddresses();
1024 CNamingClient::connect (_NamingServiceAddr, _RecordingState, laddr);
1025 // re-register the service
1026 for (uint i = 0; i < laddr.size(); i++)
1027 laddr[i].setPort(_ServerPort);
1028 CNamingClient::resendRegisteration (_Name, laddr, _SId);
1030 catch (const ESocketConnectionFailed &)
1032 nlwarning ("HNETL5: Could not connect to the Naming Service (%s). Retrying in a few seconds...", _NamingServiceAddr.asString().c_str());
1036 H_AFTER(UNNamingCheck);
1038 H_BEFORE(UNUpdateCnx);
1039 for(;;)
1041 H_AUTO(L5OneLoopUpdate);
1043 if (CAliveCheck::Thread)
1045 uint i;
1046 for (i=0; i<sizeof(CAliveCheck::Thread->CheckList)/sizeof(CAliveCheck::Thread->CheckList[0]); ++i)
1048 CAliveCheck::CCheckAddress &address = CAliveCheck::Thread->CheckList[i];
1049 if (address.NeedCheck && address.AddressValid)
1051 CUnifiedConnection &uc = _IdCnx[address.ConnectionId];
1052 if (uc.ServiceName == address.ServiceName &&
1053 uc.ServiceId == address.ServiceId &&
1054 uc.ValidRequested)
1056 uc.ValidRequested = false;
1057 uc.ValidExternal = true;
1060 address.NeedCheck = false;
1061 address.AddressValid = false;
1066 // update all server connections
1067 if (_CbServer)
1069 _CbServer->update2((sint32)remainingTime, 0);
1072 // update all client connections
1073 for (uint k = 0; k<_UsedConnection.size(); ++k)
1075 H_AUTO(UNBrowseConnections);
1076 nlassert (_IdCnx[_UsedConnection[k].get()].State == CUnifiedNetwork::CUnifiedConnection::Ready);
1077 for (uint j = 0; j < _IdCnx[_UsedConnection[k].get()].Connections.size (); j++)
1079 // WARNING : don't take a reference in the outside loop because
1080 // _IdCnx can be resized by execution of a callback
1081 CUnifiedConnection &uc = _IdCnx[_UsedConnection[k].get()];
1082 nlassert(_IdCnx[_UsedConnection[k].get()].Connections.size() > j);
1083 CUnifiedConnection::TConnection &conn = _IdCnx[_UsedConnection[k].get()].Connections[j];
1084 H_AUTO(UNBrowseSubConnections);
1085 if (!conn.valid())
1086 continue;
1088 if (conn.IsServerConnection)
1089 continue;
1091 nlassert(uc.Connections.size() > j);
1092 if (conn.CbNetBase->connected ())
1094 nlassert(uc.Connections.size() > j);
1095 conn.CbNetBase->update2((sint32)remainingTime, 0);
1097 else if (enableRetry && uc.AutoRetry)
1099 if (uc.ValidExternal)
1101 uc.ValidExternal = false;
1102 uc.ValidRequested = false;
1103 autoReconnect( uc, j );
1105 else if (!uc.ValidRequested && CAliveCheck::Thread)
1107 uc.ValidRequested = true;
1108 CAliveCheck::Thread->checkService(uc.ExtAddress[j], _UsedConnection[k].get(), j, uc.ServiceName, uc.ServiceId);
1114 // reset closed client connection
1115 for (uint i=0; i<_ConnectionToReset.size(); ++i)
1117 // remove the _UsedConnection
1118 bool found = false;
1119 for (vector<TServiceId>::iterator it = _UsedConnection.begin (); it != _UsedConnection.end(); it++)
1121 if (*it == _IdCnx[_ConnectionToReset[i].get()].ServiceId)
1123 found = true;
1124 _UsedConnection.erase (it);
1125 break;
1128 if (!found)
1129 AUTOCHECK_DISPLAY ("HNETL5: can't find the sid %hu in the _UsedConnection", _IdCnx[_ConnectionToReset[i].get()].ServiceId.get());
1130 _IdCnx[_ConnectionToReset[i].get()].reset();
1132 _ConnectionToReset.clear();
1134 enableRetry = false;
1136 if ( FlushSendsBeforeSleep.get() )
1138 // Flush all connections
1139 L5TotalBytesInLowLevelSendQueues = tryFlushAllQueues();
1142 // t0 --------- previousTime -------------------------- t0 + timeout
1143 // prevRemainingTime
1145 // t0 -------------- currentTime ---------------------- t0 + timeout
1146 // remainingTime
1147 TTime prevRemainingTime = remainingTime;
1148 TTime currentTime = CTime::getLocalTime();
1149 remainingTime = t0 + timeout - currentTime;
1151 // If it's the end (or if the Unix system time was changed forwards), don't sleep (currentTime > t0 + timeout)
1152 if ( remainingTime <= 0 )
1153 break;
1155 // If the Unix system time was changed backwards, don't wait more than requested and don't provide an erroneous time to the sleep function that would fail (currentTime < previousTime)
1156 if ( remainingTime > prevRemainingTime )
1158 // Restart at previousTime
1159 nldebug( "Backward time sync detected (at least -%" NL_I64 "d ms)", remainingTime - prevRemainingTime );
1160 remainingTime = prevRemainingTime;
1161 t0 = currentTime - (timeout - remainingTime);
1164 #if defined(NL_OS_UNIX)
1165 // Sleep until the time expires or we receive a message
1166 H_BEFORE(L5UpdateSleep);
1167 switch ( UseYieldMethod.get() )
1169 case 0: sleepUntilDataAvailable( remainingTime ); break; // accurate sleep with select()
1170 case 1: ::usleep(1000); break; // 20 ms
1171 case 2: nlSleep(1); break; // 10 ms (by nanosleep, but 20 ms measured on kernel 2.4.20)
1172 case 3: ::sched_yield(); break; // makes all slow (at least on kernel 2.4.20) !
1173 default: break; // don't sleep at all, makes all slow!
1175 H_AFTER(L5UpdateSleep);
1176 #elif defined(NL_OS_WINDOWS)
1177 // Sleep until the time expires or we receive a message
1178 H_BEFORE(L5UpdateSleep);
1179 switch (UseYieldMethod.get())
1181 case 0: sleepUntilDataAvailable(remainingTime); break; // accurate sleep
1182 case 1: nlSleep(1); break;
1183 case 2: nlSleep(1); break;
1184 case 3: SwitchToThread(); break;
1185 default: break; // don't sleep at all, makes all slow!
1187 H_AFTER(L5UpdateSleep);
1188 #else
1189 // Enable windows multithreading before rescanning all connections
1190 H_TIME(L5UpdateSleep, nlSleep(1);); // 0 (yield) would be too harmful to other applications
1191 #endif
1193 H_AFTER(UNUpdateCnx);
1195 H_TIME(UNAutoCheck, autoCheck(););
1200 * Auto-reconnect
1202 void CUnifiedNetwork::autoReconnect( CUnifiedConnection &uc, uint connectionIndex )
1204 H_AUTO(L5AutoReconnect);
1207 CCallbackClient *cbc = (CCallbackClient *)uc.Connections[connectionIndex].CbNetBase;
1208 cbc->connect(uc.ExtAddress[connectionIndex]);
1209 uc.Connections[connectionIndex].CbNetBase->getSockId()->setAppId(uc.ServiceId.get());
1210 nlinfo ("HNETL5: reconnection to %s-%hu success", uc.ServiceName.c_str(), uc.ServiceId.get());
1212 // add the name only if at least one connection is ok
1213 if (!haveNamedCnx (uc.ServiceName, uc.ServiceId))
1214 addNamedCnx (uc.ServiceName, uc.ServiceId);
1216 // resend the identification is necessary
1217 if (uc.SendId)
1219 // send identification to the service
1220 CMessage msg("UN_SIDENT");
1221 msg.serial(_Name);
1223 TServiceId ssid = _SId;
1224 if (uc.IsExternal)
1226 // in the case that the service is external, we can't send our sid because the external service can
1227 // have other connection with the same sid (for example, LS can have 2 WS with same sid => sid = 0 and leave
1228 // the other side to find a good number
1229 ssid.set(0);
1231 msg.serial(ssid); // serializes a 16 bits service id
1232 uint8 pos = uint8(connectionIndex);
1233 msg.serial(pos); // send the position in the connection table
1234 msg.serial (uc.IsExternal);
1235 uc.Connections[connectionIndex].CbNetBase->send (msg, uc.Connections[connectionIndex].HostId);
1238 // call the user callback
1239 callServiceUpCallback (uc.ServiceName, uc.ServiceId);
1241 catch (const ESocketConnectionFailed &e)
1243 #if FINAL_VERSION
1244 nlinfo ("HNETL5: can't connect to %s-%hu now (%s)", uc.ServiceName.c_str(), uc.ServiceId.get(), e.what ());
1245 #else
1246 nlwarning ("HNETL5: can't connect to %s-%hu now (%s)", uc.ServiceName.c_str(), uc.ServiceId.get(), e.what ());
1247 #endif
1251 #if defined(NL_OS_UNIX)
1252 void CUnifiedNetwork::sleepUntilDataAvailable( TTime msecMax )
1254 // Prevent looping infinitely if an erroneous time was provided
1255 if ( msecMax > 999 ) // limit not told in Linux man but here: http://docs.hp.com/en/B9106-90009/select.2.html
1256 msecMax = 999;
1258 // Prepare for select()
1259 fd_set readers;
1260 FD_ZERO( &readers );
1261 FD_SET( _MainDataAvailablePipe[PipeRead], &readers );
1262 SOCKET descmax = _MainDataAvailablePipe[PipeRead] + 1;
1264 // Select
1265 timeval tv;
1266 tv.tv_sec = 0;
1267 tv.tv_usec = msecMax * 1000;
1268 //nldebug( "Select %u ms", (uint)msecMax );
1269 //TTime before = CTime::getLocalTime();
1270 int res = ::select( descmax+1, &readers, NULL, NULL, &tv );
1271 if ( res == -1 )
1272 nlwarning( "HNETL5: Select failed in sleepUntilDataAvailable");
1273 //nldebug( "Slept %u ms", (uint)(CTime::getLocalTime()-before) );
1275 #elif defined(NL_OS_WINDOWS)
1276 void CUnifiedNetwork::sleepUntilDataAvailable(TTime msecMax)
1278 if (msecMax > 999)
1279 msecMax = 999;
1281 nlassert(_MainDataAvailableHandle);
1282 WaitForSingleObject(_MainDataAvailableHandle, msecMax);
1284 #endif
1288 bool CUnifiedNetwork::isConnectionConnected(TServiceId sid) const
1290 // a Connected connection is a connection that is Ready but that is not yet connected (serverUp will be called latter via L5).
1291 return sid.get() < _IdCnx.size()
1292 && _IdCnx[sid.get()].State == CUnifiedConnection::Ready
1293 && !_IdCnx[sid.get()].Connections.empty();
1298 uint8 CUnifiedNetwork::findConnectionId (TServiceId sid, uint8 nid)
1300 if (_IdCnx[sid.get()].Connections.size () == 0)
1302 nlwarning ("HNETL5: Can't send message to %s because no connection are available", _IdCnx[sid.get()].ServiceName.c_str ());
1303 return 0xFF;
1306 // by default, connection id will be the default one
1307 uint8 connectionId = _IdCnx[sid.get()].DefaultNetwork;
1309 if (nid == 0xFF)
1311 // default network
1312 //nldebug ("HNETL5: nid %hu, will use the default connection %hu", (uint16)nid, (uint16)connectionId);
1314 else if (nid >= _IdCnx[sid.get()].NetworkConnectionAssociations.size())
1316 nlwarning ("HNETL5: No net association for nid %hu, use the default connection %hu", (uint16)nid, (uint16)connectionId);
1318 else
1320 if (_IdCnx[sid.get()].NetworkConnectionAssociations[nid] >= _IdCnx[sid.get()].Connections.size ())
1322 nlwarning ("HNETL5: Can't send message to %s because nid %d point on a bad connection (%d and only have %d cnx), use default connection", _IdCnx[sid.get()].ServiceName.c_str (), nid, connectionId, _IdCnx[sid.get()].Connections.size ());
1324 else
1326 connectionId = _IdCnx[sid.get()].NetworkConnectionAssociations[nid];
1330 if (connectionId >= _IdCnx[sid.get()].Connections.size() || !_IdCnx[sid.get()].Connections[connectionId].valid() || !_IdCnx[sid.get()].Connections[connectionId].CbNetBase->connected())
1332 if (nid != 0xFF)
1334 // not a default network. There's a problem with the selected connectionID, so try to find a valid one
1335 nlwarning ("HNETL5: Can't find selected connection id %hu to send message to %s because connection is not valid or connected, find a valid connection id", (uint16)connectionId, _IdCnx[sid.get()].ServiceName.c_str ());
1338 for (connectionId = 0; connectionId < _IdCnx[sid.get()].Connections.size(); connectionId++)
1340 if (_IdCnx[sid.get()].Connections[connectionId].valid() && _IdCnx[sid.get()].Connections[connectionId].CbNetBase->connected())
1342 // we found one at last, use this one
1343 //nldebug ("HNETL5: Ok, we found a valid connectionid, use %hu", (uint16)connectionId);
1344 if (nid < _IdCnx[sid.get()].NetworkConnectionAssociations.size())
1346 _IdCnx[sid.get()].NetworkConnectionAssociations[nid] = connectionId; // we set the preferred networkConnectionAssociation
1348 else
1350 if (nid == 0xFF)
1352 _IdCnx[sid.get()].DefaultNetwork = connectionId;
1356 nlwarning ("HNETL5: selected connection id %hu from network %hu to send message to %s", (uint16)connectionId, (uint16)nid, _IdCnx[sid.get()].ServiceName.c_str ());
1357 break;
1361 if (connectionId == _IdCnx[sid.get()].Connections.size())
1363 nlwarning ("HNETL5: Can't send message to %s because default connection is not exist, valid or connected", _IdCnx[sid.get()].ServiceName.c_str ());
1364 return 0xFF;
1367 return connectionId;
1375 uint CUnifiedNetwork::send(const string &serviceName, const CMessage &msgout, bool warnIfNotFound, uint8 nid)
1377 nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::send(const string&, const CMessage&) whereas it is not initialised yet"));
1379 if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
1381 TNameMappedConnection::const_iterator it;
1382 pair<TNameMappedConnection::const_iterator,TNameMappedConnection::const_iterator> range;
1383 range = _NamedCnx.equal_range(serviceName);
1385 uint found = 0;
1386 if (range.first != _NamedCnx.end())
1388 for (it=range.first; it!=range.second; ++it)
1390 TServiceId sid = it->second;
1391 if (sid.get() >= _IdCnx.size () || _IdCnx[sid.get()].State != CUnifiedNetwork::CUnifiedConnection::Ready)
1393 // It often happen when the service is down (connection broke and the naming not already say that it s down)
1394 // In this case, just warn
1395 nlwarning ("HNETL5: Can't send %s to the service '%s' because it was in the _NamedCnx but not in _IdCnx (means that the service is down)", msgout.getName().c_str(), serviceName.c_str ());
1396 return false;
1399 ++found;
1401 uint8 connectionId = findConnectionId (sid, nid);
1402 if (connectionId == 0xff) // failed
1404 nlwarning ("HNETL5: Can't send %s message to %hu because no connection available", msgout.getName().c_str(), sid.get());
1405 continue;
1408 _IdCnx[sid.get()].Connections[connectionId].CbNetBase->send (msgout, _IdCnx[sid.get()].Connections[connectionId].HostId);
1412 if (!found && warnIfNotFound)
1413 nlwarning ("HNETL5: can't find service %s to send message %s", serviceName.c_str(), msgout.getName().c_str());
1415 return found;
1418 bool CUnifiedNetwork::send(TServiceId sid, const CMessage &msgout, uint8 nid)
1420 nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::send(TServiceId, const CMessage&) whereas it is not initialised yet"));
1422 if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
1424 if (sid.get() >= _IdCnx.size () || _IdCnx[sid.get()].State != CUnifiedNetwork::CUnifiedConnection::Ready)
1426 // Happens when trying to send a message to an unknown service id
1427 nlwarning ("HNETL5: Can't send %s to the service '%hu' because not in _IdCnx", msgout.getName().c_str(), sid.get());
1428 return false;
1431 uint8 connectionId = findConnectionId (sid, nid);
1432 if (connectionId == 0xff) // failed
1434 nlwarning ("HNETL5: Can't send %s to the service '%hu' because no connection available", msgout.getName().c_str(), sid.get());
1435 return false;
1438 _IdCnx[sid.get()].Connections[connectionId].CbNetBase->send (msgout, _IdCnx[sid.get()].Connections[connectionId].HostId);
1439 return true;
1442 void CUnifiedNetwork::sendAll(const CMessage &msgout, uint8 nid)
1444 nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::send(const CMessage&) whereas it is not initialised yet"));
1446 if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
1448 for (TServiceId::size_type i=0; i<_IdCnx.size(); ++i)
1450 if (_IdCnx[i].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1452 uint8 connectionId = findConnectionId (TServiceId(i), nid);
1453 if (connectionId == 0xff) // failed
1455 nlwarning ("HNETL5: Can't send message to %u because no connection available", i);
1456 continue;
1459 _IdCnx[i].Connections[connectionId].CbNetBase->send (msgout, _IdCnx[i].Connections[connectionId].HostId);
1465 /* Flush all the sending queues, and report the number of bytes still pending.
1466 * To ensure all data are sent before stopping a service, you may want to repeat
1467 * calling this method evenly until it returns 0.
1468 * \param namesOfOnlyServiceToFlushSending If not empty, only the send queues to the
1469 * services specified (by short name) will be flushed.
1471 uint CUnifiedNetwork::tryFlushAllQueues(const std::vector<std::string>& namesOfOnlyServiceToFlushSending)
1473 H_AUTO(L5FlushAll);
1474 uint bytesRemaining = 0;
1475 for (uint k = 0; k<_UsedConnection.size(); ++k)
1477 H_AUTO(UNFABrowseConnections);
1478 CUnifiedConnection &uc = _IdCnx[_UsedConnection[k].get()];
1480 // Skip the connection if it is not found in the 'only' list (except if the list is empty)
1481 if ( (! namesOfOnlyServiceToFlushSending.empty()) &&
1482 (std::find( namesOfOnlyServiceToFlushSending.begin(), namesOfOnlyServiceToFlushSending.end(), uc.ServiceName ) == namesOfOnlyServiceToFlushSending.end()) )
1483 continue;
1485 nlassert (uc.State == CUnifiedNetwork::CUnifiedConnection::Ready);
1486 for (uint j = 0; j < uc.Connections.size (); j++)
1488 H_AUTO(UNFABrowseSubConnections);
1489 if (!uc.Connections[j].valid())
1490 continue;
1492 if (uc.Connections[j].CbNetBase->connected ())
1494 uint bytesRemainingLocal;
1495 uc.Connections[j].CbNetBase->flush(uc.Connections[j].HostId, &bytesRemainingLocal);
1496 bytesRemaining += bytesRemainingLocal;
1500 return bytesRemaining;
1508 void CUnifiedNetwork::addCallbackArray (const TUnifiedCallbackItem *callbackarray, sint arraysize)
1510 uint i;
1512 for (i=0; i<(uint)arraysize; ++i)
1513 _Callbacks.insert(make_pair(string(callbackarray[i].Key),callbackarray[i].Callback));
1517 void CUnifiedNetwork::setServiceUpCallback (const string &serviceName, TUnifiedNetCallback cb, void *arg, bool back)
1519 nlassert (cb != NULL);
1520 if (serviceName == "*")
1522 if (back)
1523 _UpUniCallback.push_back (make_pair(cb, arg));
1524 else
1525 _UpUniCallback.insert (_UpUniCallback.begin(), make_pair(cb, arg));
1527 else
1529 if (back)
1530 _UpCallbacks[serviceName].push_back (make_pair(cb, arg));
1531 else
1532 _UpCallbacks[serviceName].insert (_UpCallbacks[serviceName].begin(), make_pair(cb, arg));
1536 void CUnifiedNetwork::removeServiceUpCallback (const string &serviceName, TUnifiedNetCallback cb, void *arg)
1538 if (serviceName == "*")
1540 uint i;
1541 for (i=0; i<_UpUniCallback.size(); ++i)
1543 if (_UpUniCallback[i].first == cb && _UpUniCallback[i].second == arg)
1545 // we found it
1546 _UpUniCallback.erase(_UpUniCallback.begin()+i);
1547 break;
1550 if (i == _UpUniCallback.size())
1552 nlwarning("HNETL5 : can't remove service up callback, not found");
1555 else
1557 if (_UpCallbacks.find(serviceName) != _UpCallbacks.end())
1559 std::list<TCallbackArgItem> &list = _UpCallbacks[serviceName];
1560 std::list<TCallbackArgItem>::iterator first(list.begin()), last(list.end());
1561 for (; first != last; ++first)
1563 if (first->first == cb && first->second == arg)
1565 list.erase(first);
1566 break;
1570 if (first == last)
1572 nlwarning("HNETL5 : can't remove service up callback, not found");
1575 if (list.empty())
1577 // no more callback for this service
1578 _UpCallbacks.erase(serviceName);
1581 else
1583 nlwarning("HNETL5 : can't remove service up callback, not found");
1588 void CUnifiedNetwork::setServiceDownCallback (const string &serviceName, TUnifiedNetCallback cb, void *arg, bool back)
1590 nlassert (cb != NULL);
1591 if (serviceName == "*")
1593 if (back)
1594 _DownUniCallback.push_back (make_pair(cb, arg));
1595 else
1596 _DownUniCallback.insert (_DownUniCallback.begin(), make_pair(cb, arg));
1598 else
1600 if (back)
1601 _DownCallbacks[serviceName].push_back (make_pair(cb, arg));
1602 else
1603 _DownCallbacks[serviceName].insert (_DownCallbacks[serviceName].begin(), make_pair(cb, arg));
1607 void CUnifiedNetwork::removeServiceDownCallback (const string &serviceName, TUnifiedNetCallback cb, void *arg)
1609 if (serviceName == "*")
1611 uint i;
1612 for (i=0; i<_DownUniCallback.size(); ++i)
1614 if (_DownUniCallback[i].first == cb && _DownUniCallback[i].second == arg)
1616 // we found it
1617 _DownUniCallback.erase(_DownUniCallback.begin()+i);
1618 break;
1621 if (i == _DownUniCallback.size())
1623 nlwarning("HNETL5 : can't remove service down callback, not found");
1626 else
1628 if (_DownCallbacks.find(serviceName) != _DownCallbacks.end())
1630 std::list<TCallbackArgItem> &list = _DownCallbacks[serviceName];
1631 std::list<TCallbackArgItem>::iterator first(list.begin()), last(list.end());
1632 for (; first != last; ++first)
1634 if (first->first == cb && first->second == arg)
1636 list.erase(first);
1637 break;
1641 if (first == last)
1643 nlwarning("HNETL5 : can't remove service down callback, not found");
1646 if (list.empty())
1648 // no more callback for this service
1649 _DownCallbacks.erase(serviceName);
1652 else
1654 nlwarning("HNETL5 : can't remove service down callback, not found");
1662 uint64 CUnifiedNetwork::getBytesSent ()
1664 uint64 sent = 0;
1665 uint j;
1667 for (vector<TServiceId>::iterator it = _UsedConnection.begin (); it != _UsedConnection.end(); it++)
1669 if (_IdCnx[it->get()].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1670 for (j=0; j<_IdCnx[it->get()].Connections.size (); ++j)
1671 if(_IdCnx[it->get()].Connections[j].valid () && !_IdCnx[it->get()].Connections[j].IsServerConnection)
1672 sent += _IdCnx[it->get()].Connections[j].CbNetBase->getBytesSent();
1675 if(_CbServer)
1676 sent += _CbServer->getBytesSent();
1677 return sent;
1680 uint64 CUnifiedNetwork::getBytesReceived ()
1682 uint64 received = 0;
1683 uint j;
1685 for (vector<TServiceId>::iterator it = _UsedConnection.begin (); it != _UsedConnection.end(); it++)
1687 if (_IdCnx[it->get()].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1688 for (j=0; j<_IdCnx[it->get()].Connections.size (); ++j)
1689 if(_IdCnx[it->get()].Connections[j].valid () && !_IdCnx[it->get()].Connections[j].IsServerConnection)
1690 received += _IdCnx[it->get()].Connections[j].CbNetBase->getBytesReceived();
1693 if (_CbServer)
1694 received += _CbServer->getBytesReceived();
1695 return received;
1698 uint64 CUnifiedNetwork::getSendQueueSize ()
1700 uint64 sent = 0;
1701 uint j;
1703 for (vector<TServiceId>::iterator it = _UsedConnection.begin (); it != _UsedConnection.end(); it++)
1705 if (_IdCnx[it->get()].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1706 for (j=0; j<_IdCnx[it->get()].Connections.size (); ++j)
1707 if(_IdCnx[it->get()].Connections[j].valid () && !_IdCnx[it->get()].Connections[j].IsServerConnection)
1708 sent += _IdCnx[it->get()].Connections[j].CbNetBase->getSendQueueSize();
1711 if (_CbServer)
1712 sent += _CbServer->getSendQueueSize();
1713 return sent;
1716 uint64 CUnifiedNetwork::getReceiveQueueSize ()
1718 uint64 received = 0;
1719 uint j;
1721 for (vector<TServiceId>::iterator it = _UsedConnection.begin (); it != _UsedConnection.end(); it++)
1723 if (_IdCnx[it->get()].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1724 for (j=0; j<_IdCnx[it->get()].Connections.size (); ++j)
1725 if(_IdCnx[it->get()].Connections[j].valid () && !_IdCnx[it->get()].Connections[j].IsServerConnection)
1726 received += _IdCnx[it->get()].Connections[j].CbNetBase->getReceiveQueueSize();
1729 if (_CbServer)
1730 received += _CbServer->getReceiveQueueSize();
1731 return received;
1734 CCallbackNetBase *CUnifiedNetwork::getNetBase(const std::string &name, TSockId &host, uint8 nid)
1736 nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::getNetBase() whereas it is not initialised yet"));
1738 if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
1740 sint count = (sint)_NamedCnx.count(name);
1742 if (count <= 0)
1744 nlwarning ("HNETL5: couldn't access the service %s", name.c_str());
1745 host = InvalidSockId;
1746 return NULL;
1748 else if (count > 1)
1750 nlwarning ("HNETL5: %d services %s to getNetBase, returns the first valid", count, name.c_str());
1753 TNameMappedConnection::const_iterator itnmc = _NamedCnx.find(name);
1755 uint8 connectionId = findConnectionId ((*itnmc).second, nid);
1756 if (connectionId == 0xff) // failed
1758 nlwarning ("HNETL5: Can't getNetBase %s because no connection available", name.c_str());
1759 host = InvalidSockId;
1760 return NULL;
1763 host = _IdCnx[itnmc->second.get()].Connections[connectionId].HostId;
1764 return _IdCnx[itnmc->second.get()].Connections[connectionId].CbNetBase;
1767 CCallbackNetBase *CUnifiedNetwork::getNetBase(TServiceId sid, TSockId &host, uint8 nid)
1769 nlassertex(_Initialised == true, ("Try to CUnifiedNetwork::getNetBase() whereas it is not initialised yet"));
1771 if (ThreadCreator != NLMISC::getThreadId()) nlwarning ("HNETL5: Multithread access but this class is not thread safe thread creator = %u thread used = %u", ThreadCreator, NLMISC::getThreadId());
1773 if (sid.get() >= _IdCnx.size () || _IdCnx[sid.get()].State != CUnifiedNetwork::CUnifiedConnection::Ready)
1775 nlwarning ("HNETL5: Can't get net base to the service '%hu' because not in _IdCnx", sid.get());
1776 host = InvalidSockId;
1777 return NULL;
1780 uint8 connectionId = findConnectionId (sid, nid);
1781 if (connectionId == 0xff) // failed
1783 nlwarning ("HNETL5: Can't getNetBase %hu because no connection available", sid.get());
1784 host = InvalidSockId;
1785 return NULL;
1788 host = _IdCnx[sid.get()].Connections[connectionId].HostId;
1789 return _IdCnx[sid.get()].Connections[connectionId].CbNetBase;
1792 TUnifiedMsgCallback CUnifiedNetwork::findCallback (const std::string &callbackName)
1794 TMsgMappedCallback::iterator itcb = _Callbacks.find(callbackName);
1795 if (itcb == _Callbacks.end())
1796 return NULL;
1797 else
1798 return (*itcb).second;
1801 bool CUnifiedNetwork::isServiceLocal (const std::string &serviceName)
1803 // it s me, of course we are local
1804 if (serviceName == _Name)
1805 return true;
1807 pair<TNameMappedConnection::const_iterator,TNameMappedConnection::const_iterator> range;
1808 range = _NamedCnx.equal_range(serviceName);
1810 if (range.first != _NamedCnx.end())
1812 TServiceId sid = (*(range.first)).second;
1813 return isServiceLocal (sid);
1816 return false;
1819 bool CUnifiedNetwork::isServiceLocal (TServiceId sid)
1821 // it s me, of course we are local
1822 if (sid == _SId)
1823 return true;
1825 if (sid.get() >= _IdCnx.size () || _IdCnx[sid.get()].State != CUnifiedNetwork::CUnifiedConnection::Ready)
1827 return false;
1830 vector<CInetAddress> laddr = CInetAddress::localAddresses();
1832 for (uint i = 0; i < laddr.size(); i++)
1834 for (uint j = 0; j < _IdCnx[sid.get()].ExtAddress.size(); j++)
1836 if (_IdCnx[sid.get()].ExtAddress[j].isLoopbackIPAddress ())
1837 return true;
1839 if (_IdCnx[sid.get()].ExtAddress[j].internalIPAddress () == laddr[i].internalIPAddress ())
1840 return true;
1843 return false;
1848 * Return the name of the specified service, or "" if not found
1850 std::string CUnifiedNetwork::getServiceName(TServiceId sid)
1852 string s;
1853 CUnifiedConnection *c = getUnifiedConnection(sid, false);
1854 if (c)
1855 s = c->ServiceName;
1856 return s;
1861 * Return a string identifying the service, using the format "NAME-sid" (or "sid" only if not found)
1863 std::string CUnifiedNetwork::getServiceUnifiedName(TServiceId sid)
1865 string s;
1866 CUnifiedConnection *c = getUnifiedConnection(sid, false);
1867 if (c)
1868 s = c->ServiceName + "-";
1869 s += toString(sid.get());
1870 return s;
1878 //CUnifiedNetwork *CUnifiedNetwork::_Instance = NULL;
1879 NLMISC_SAFE_SINGLETON_IMPL(CUnifiedNetwork);
1881 bool CUnifiedNetwork::isUsed ()
1883 return (_Instance != NULL);
1890 CUnifiedNetwork::CUnifiedConnection *CUnifiedNetwork::getUnifiedConnection (TServiceId sid, bool warn)
1892 if (sid.get() < _IdCnx.size () && _IdCnx[sid.get()].State == CUnifiedConnection::Ready)
1894 if (sid != _IdCnx[sid.get()].ServiceId)
1896 AUTOCHECK_DISPLAY ("HNETL5: Sid index %hu is not the same that in the entry %hu", sid.get(), _IdCnx[sid.get()].ServiceId.get());
1897 return NULL;
1899 return &_IdCnx[sid.get()];
1901 else
1903 if ( warn )
1904 nlwarning ("HNETL5: Try to get a bad unified connection (sid %hu is not in the table)", sid.get());
1905 return NULL;
1909 void CUnifiedNetwork::autoCheck()
1911 H_AUTO(L5UpdateAutoCheck);
1912 uint i, j;
1914 for (i = 0; i < _IdCnx.size (); i++)
1916 if (_IdCnx[i].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1918 _IdCnx[i].AutoCheck = 1;
1920 else
1922 _IdCnx[i].AutoCheck = 0;
1926 TNameMappedConnection::iterator itn;
1927 for (itn = _NamedCnx.begin(); itn != _NamedCnx.end(); ++itn)
1929 if ((*itn).first != _IdCnx[itn->second.get()].ServiceName)
1930 AUTOCHECK_DISPLAY ("HLNET5: problem with name synchro between _NameCnx '%s' and _IdCnx '%s' '%hd'",
1931 (*itn).first.c_str(),
1932 _IdCnx[itn->second.get()].ServiceName.c_str (),
1933 itn->second.get());
1934 if (_IdCnx[itn->second.get()].AutoCheck == 0)
1935 AUTOCHECK_DISPLAY ("HLNET5: problem with name synchro between _NameCnx '%s' and _IdCnx '%s' '%hd'",
1936 (*itn).first.c_str(),
1937 _IdCnx[itn->second.get()].ServiceName.c_str (),
1938 itn->second.get());
1939 if (_IdCnx[itn->second.get()].AutoCheck > 1)
1940 AUTOCHECK_DISPLAY ("HLNET5: problem with name synchro between _NameCnx '%s' and _IdCnx '%s' '%hd' more than one entry is named with the same name",
1941 (*itn).first.c_str(),
1942 _IdCnx[itn->second.get()].ServiceName.c_str (),
1943 itn->second.get());
1944 _IdCnx[itn->second.get()].AutoCheck++;
1947 for (i = 0; i < _UsedConnection.size (); i++)
1949 if (_IdCnx[_UsedConnection[i].get()].State != CUnifiedNetwork::CUnifiedConnection::Ready)
1950 AUTOCHECK_DISPLAY ("HLNET5: problem with the _UsedConnection syncro sid %d is not used in _IdCnx", _UsedConnection[i].get());
1953 for (i = 0; i < _IdCnx.size (); i++)
1955 if (_IdCnx[i].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1957 for (j = 0; j < _UsedConnection.size (); j++)
1959 if (_UsedConnection[j].get() == i) break;
1961 if (j == _UsedConnection.size ()) AUTOCHECK_DISPLAY ("HLNET5: problem with the _UsedConnection syncro sid %d is not in _UsedConnection", i);
1965 for (i = 0; i < _IdCnx.size (); i++)
1967 if (_IdCnx[i].State == CUnifiedNetwork::CUnifiedConnection::NotUsed)
1969 if (_IdCnx[i].ServiceName != "DEAD") AUTOCHECK_DISPLAY ("HLNET5: sid %d name should be DEAD and is '%s'", i, _IdCnx[i].ServiceName.c_str ());
1970 if (_IdCnx[i].ServiceId.get() != 0xDEAD) AUTOCHECK_DISPLAY ("HLNET5: sid %d sid should be 0xDEAD and is 0x%X", i, _IdCnx[i].ServiceId.get());
1971 if (!_IdCnx[i].Connections.empty ()) AUTOCHECK_DISPLAY ("HLNET5: sid %d connection size should be 0 and is %d", i, _IdCnx[i].Connections.size ());
1972 if (!_IdCnx[i].ExtAddress.empty ()) AUTOCHECK_DISPLAY ("HLNET5: sid %d ext addr size should be 0 and is %d", i, _IdCnx[i].ExtAddress.size ());
1973 if (_IdCnx[i].AutoCheck != 0) AUTOCHECK_DISPLAY ("HLNET5: sid %d prob with syncro with _NamedCnx", i);
1975 else if (_IdCnx[i].State == CUnifiedNetwork::CUnifiedConnection::Ready)
1977 if (_IdCnx[i].ServiceId.get() != i) AUTOCHECK_DISPLAY ("HNETL5: Bad syncro sid index sid entry for %d %d", i, _IdCnx[i].ServiceId.get());
1979 if (_IdCnx[i].ServiceName == "DEAD") AUTOCHECK_DISPLAY ("HLNET5: sid %d name should not be DEAD and is '%s'", i, _IdCnx[i].ServiceName.c_str ());
1980 if (_IdCnx[i].ServiceId.get() == 0xDEAD) AUTOCHECK_DISPLAY ("HLNET5: sid %d sid should not be 0xDEAD and is 0x%X", i, _IdCnx[i].ServiceId.get());
1981 if (!_IdCnx[i].ExtAddress.empty () && _IdCnx[i].Connections.size () > _IdCnx[i].ExtAddress.size()) AUTOCHECK_DISPLAY ("HLNET5: sid %d ext addr size should not be 0 and is %d", i, _IdCnx[i].ExtAddress.size ());
1983 if (_IdCnx[i].AutoRetry == true && _IdCnx[i].Connections.size () > 1) AUTOCHECK_DISPLAY ("HLNET5: sid %d auto retry with more than one connection %d", i, _IdCnx[i].Connections.size ());
1984 if (_IdCnx[i].AutoRetry == true && _IdCnx[i].IsExternal == false) AUTOCHECK_DISPLAY ("HLNET5: sid %d auto retry with internal connection", i);
1985 if (_IdCnx[i].AutoRetry == true && _IdCnx[i].Connections[0].valid() == false) AUTOCHECK_DISPLAY ("HLNET5: sid %d auto retry with invalid connection", i);
1987 for (j = 0; j < _IdCnx[i].Connections.size (); j++)
1989 if (_IdCnx[i].Connections[j].valid() && !_IdCnx[i].Connections[j].IsServerConnection && _IdCnx[i].Connections[j].CbNetBase->connected () && _IdCnx[i].Connections[j].getAppId() != i) AUTOCHECK_DISPLAY ("HLNET5: sid %d bad appid %" NL_I64 "X", i, _IdCnx[i].Connections[j].getAppId());
1992 for (j = 0; j < _IdCnx[i].NetworkConnectionAssociations.size (); j++)
1994 if (_IdCnx[i].NetworkConnectionAssociations[j] != 0)
1996 if (_NetworkAssociations[j] != _IdCnx[i].ExtAddress[_IdCnx[i].NetworkConnectionAssociations[j]].internalNetAddress ()) AUTOCHECK_DISPLAY ("HLNET5: sid %d nid %d have address 0x%08x and is not the good connection net 0x%08x", i, j, _NetworkAssociations[j], _IdCnx[i].ExtAddress[_IdCnx[i].NetworkConnectionAssociations[j]].internalNetAddress ());
2004 void CUnifiedNetwork::displayInternalTables (NLMISC::CLog *log)
2006 uint i, j;
2007 log->displayNL ("%d Named Connections:", _NamedCnx.size ());
2008 for (TNameMappedConnection::iterator it = _NamedCnx.begin(); it != _NamedCnx.end (); it++)
2010 log->displayNL ("> '%s' -> %hu", (*it).first.c_str(), it->second.get());
2013 uint nbused = 0;
2014 for (i = 0; i < _IdCnx.size (); i++)
2016 if(_IdCnx[i].State != CUnifiedNetwork::CUnifiedConnection::NotUsed)
2017 nbused++;
2020 log->displayNL ("%u/%u Unified Connections:", nbused, _IdCnx.size ());
2021 for (i = 0; i < _IdCnx.size (); i++)
2023 if(_IdCnx[i].State != CUnifiedNetwork::CUnifiedConnection::NotUsed)
2025 _IdCnx[i].display (false, log);
2026 for (j = 0; j < _IdCnx[i].NetworkConnectionAssociations.size (); j++)
2028 log->displayNL (" * nid %d -> cnxn %hu", j, (uint16)_IdCnx[i].NetworkConnectionAssociations[j]);
2033 log->displayNL ("%u Used Unified Connections:", _UsedConnection.size());
2034 for (i = 0; i < _UsedConnection.size (); i++)
2036 log->displayNL ("> %hu", _UsedConnection[i].get());
2039 log->displayNL ("%u Network Associations:", _NetworkAssociations.size());
2040 for (i = 0; i < _NetworkAssociations.size (); i++)
2042 log->displayNL ("> 0x%08x -> '%s'", _NetworkAssociations[i], internalIPAddressToString (_NetworkAssociations[i]).c_str ());
2046 bool CUnifiedNetwork::haveNamedCnx (const std::string &name, TServiceId sid)
2048 CUnifiedNetwork::TNameMappedConnection::iterator it;
2049 pair<CUnifiedNetwork::TNameMappedConnection::iterator,CUnifiedNetwork::TNameMappedConnection::iterator> range;
2050 range = _NamedCnx.equal_range(name);
2052 if (range.first != range.second)
2054 for (it=range.first; it!=range.second && (*it).second!=sid; ++it)
2057 return (it != range.second);
2059 return false;
2062 void CUnifiedNetwork::addNamedCnx (const std::string &name, TServiceId sid)
2064 // check if not already inserted
2065 CUnifiedNetwork::TNameMappedConnection::iterator it;
2066 pair<CUnifiedNetwork::TNameMappedConnection::iterator,CUnifiedNetwork::TNameMappedConnection::iterator> range;
2067 range = _NamedCnx.equal_range(name);
2069 if (range.first != range.second)
2071 for (it=range.first; it!=range.second && (*it).second!=sid; ++it)
2074 if (it != range.second)
2076 AUTOCHECK_DISPLAY ("HNETL5: Try to add 2 times the same connection %s-%hu", name.c_str(), sid.get());
2077 return;
2081 // insert the name in the map to be able to send message with the name
2082 _NamedCnx.insert(make_pair(name, sid));
2085 void CUnifiedNetwork::removeNamedCnx (const std::string &name, TServiceId sid)
2087 // get all map nodes of that service name
2088 CUnifiedNetwork::TNameMappedConnection::iterator it;
2089 pair<CUnifiedNetwork::TNameMappedConnection::iterator,CUnifiedNetwork::TNameMappedConnection::iterator> range;
2090 range = _NamedCnx.equal_range(name);
2092 // assume not empty
2093 if (range.first == range.second)
2095 AUTOCHECK_DISPLAY ("HNETL5: The unified connection %s-%hu wasn't on the _NamedCnx", name.c_str(), sid.get());
2096 return;
2099 // select good service id
2100 for (it=range.first; it!=range.second && (*it).second!=sid; ++it)
2103 // assume id exists
2104 if (it == range.second)
2106 AUTOCHECK_DISPLAY ("HNETL5: The unified connection %s-%hu wasn't on the _NamedCnx", name.c_str(), sid.get());
2107 return;
2110 // remove service for map
2111 _NamedCnx.erase(it);
2114 void CUnifiedNetwork::addNetworkAssociation (const string &networkName, uint8 nid)
2116 if (nid >= _NetworkAssociations.size ())
2117 _NetworkAssociations.resize (nid+1, 0xFF);
2119 _NetworkAssociations[nid] = stringToInternalIPAddress (networkName);
2120 nlinfo ("HNETL5: Associate network '%s' 0x%08x '%s' to nid %hu", networkName.c_str(), _NetworkAssociations[nid], internalIPAddressToString (_NetworkAssociations[nid]).c_str(), (uint16)nid);
2123 void CUnifiedNetwork::callServiceUpCallback (const std::string &serviceName, TServiceId sid, bool callGlobalCallback)
2125 std::pair<std::string, TServiceId> pss = std::make_pair(serviceName, sid);
2126 if (_NotifiedUpCallbacks.find(pss) != _NotifiedUpCallbacks.end())
2128 nlwarning("HNETL5: Attempt to call service UP callback twice for '%s', ignored!", serviceName.c_str());
2129 return;
2131 _NotifiedUpCallbacks.insert(pss);
2133 // now we warn the user
2134 CUnifiedNetwork::TNameMappedCallback::iterator it = _UpCallbacks.find(serviceName);
2135 if (it != _UpCallbacks.end())
2137 // call it
2138 for (list<TCallbackArgItem>::iterator it2 = (*it).second.begin(); it2 != (*it).second.end(); it2++)
2140 TUnifiedNetCallback cb = (*it2).first;
2141 if (cb)
2142 cb(serviceName, sid, (*it2).second);
2143 else
2144 nlwarning ("HNETL5: User set an empty callback for '%s' service up", serviceName.c_str());
2148 if(callGlobalCallback)
2150 for (uint c = 0; c < _UpUniCallback.size (); c++)
2152 if (_UpUniCallback[c].first != NULL)
2153 _UpUniCallback[c].first (serviceName, sid, _UpUniCallback[c].second);
2154 else
2155 nlwarning ("HNETL5: User set an empty callback for '*' service up");
2160 void CUnifiedNetwork::callServiceDownCallback (const std::string &serviceName, TServiceId sid, bool callGlobalCallback)
2162 std::pair<std::string, TServiceId> pss = std::make_pair(serviceName, sid);
2163 if (_NotifiedUpCallbacks.find(pss) == _NotifiedUpCallbacks.end())
2165 nlwarning("HNETL5: Attempt to call service DOWN callback twice for '%s', ignored!", serviceName.c_str());
2166 return;
2168 _NotifiedUpCallbacks.erase(pss);
2170 // now we warn the user
2171 CUnifiedNetwork::TNameMappedCallback::iterator it = _DownCallbacks.find(serviceName);
2172 if (it != _DownCallbacks.end())
2174 // call it
2175 for (list<TCallbackArgItem>::iterator it2 = (*it).second.begin(); it2 != (*it).second.end(); it2++)
2177 TUnifiedNetCallback cb = (*it2).first;
2178 if (cb)
2179 cb(serviceName, sid, (*it2).second);
2180 else
2181 nlwarning ("HNETL5: User set an empty callback for '%s' service down", serviceName.c_str());
2185 if(callGlobalCallback)
2187 for (uint c = 0; c < _DownUniCallback.size (); c++)
2189 if (_DownUniCallback[c].first != NULL)
2190 _DownUniCallback[c].first (serviceName, sid, _DownUniCallback[c].second);
2191 else
2192 nlwarning ("HNETL5: User set an empty callback for '*' service down");
2197 void CUnifiedNetwork::CUnifiedConnection::display (bool full, CLog *log)
2199 log->displayNL ("> %s-%hu %s %s %s (%d ExtAddr %d Cnx) TotalCb %d", ServiceName.c_str (), ServiceId.get(), IsExternal?"External":"NotExternal",
2200 AutoRetry?"AutoRetry":"NoAutoRetry", SendId?"SendId":"NoSendId", ExtAddress.size (), Connections.size (), TotalCallbackCalled);
2202 uint maxc = (uint)std::max (ExtAddress.size (), Connections.size ());
2204 for (uint j = 0; j < maxc; j++)
2206 string base;
2207 if(j < ExtAddress.size ())
2209 base += ExtAddress[j].asString ();
2211 else
2213 base += "NotValid";
2216 string ext;
2217 if(j < Connections.size () && Connections[j].valid())
2219 if(Connections[j].IsServerConnection)
2221 ext += "Server ";
2223 else
2225 ext += "Client ";
2227 ext += Connections[j].CbNetBase->getSockId (Connections[j].HostId)->asString ();
2228 ext += " AppId:" + toString(Connections[j].getAppId());
2229 if (Connections[j].CbNetBase->connected ())
2230 ext += " Connected";
2231 else
2232 ext += " NotConnected";
2234 else
2236 ext += "NotValid";
2239 log->displayNL (" - %s %s", base.c_str (), ext.c_str ());
2240 if(full)
2242 log->displayNL (" * ReceiveQueueStat");
2243 Connections[j].CbNetBase->displayReceiveQueueStat(log);
2244 log->displayNL (" * SendQueueStat");
2245 Connections[j].CbNetBase->displaySendQueueStat(log, Connections[j].HostId);
2246 log->displayNL (" * ThreadStat");
2247 Connections[j].CbNetBase->displayThreadStat(log);
2254 // Commands
2257 bool createMessage (CMessage &msgout, const vector<string> &args, CLog &log)
2259 for (uint i = 2; i < args.size (); i+=2)
2261 string type = args[i+0];
2262 string value = args[i+1];
2264 if (type == "s8") { sint8 v; fromString(value, v); msgout.serial (v); }
2265 else if (type == "s16") { sint16 v; fromString(value, v); msgout.serial (v); }
2266 else if (type == "s32") { sint32 v; fromString(value, v); msgout.serial (v); }
2267 else if (type == "s64") { sint64 v; fromString(value, v); msgout.serial (v); }
2268 else if (type == "u8") { uint8 v; fromString(value, v); msgout.serial (v); }
2269 else if (type == "u16") { uint16 v; fromString(value, v); msgout.serial (v); }
2270 else if (type == "u32") { uint32 v; fromString(value, v); msgout.serial (v); }
2271 else if (type == "u64") { uint64 v; fromString(value, v); msgout.serial (v); }
2272 else if (type == "f") { float v; fromString(value, v); msgout.serial (v); }
2273 else if (type == "d") { double v; fromString(value, v); msgout.serial (v); }
2274 else if (type == "b") { bool v; fromString(value, v); msgout.serial (v); }
2275 else if (type == "s") { msgout.serial (value); }
2276 else if (type == "e") { CEntityId e; e.fromString(value.c_str()); msgout.serial(e); }
2277 else { log.displayNL ("type '%s' is not a valid type", type.c_str()); return false; }
2279 return true;
2284 // Commands and Variables
2287 NLMISC_CATEGORISED_VARIABLE(nel, uint32, TotalCallbackCalled, "Total callback called number on layer 5");
2289 NLMISC_CATEGORISED_DYNVARIABLE(nel, uint64, SendQueueSize, "current size in bytes of all send queues")
2291 nlunreferenced(human);
2293 if (get)
2295 if (!CUnifiedNetwork::isUsed ())
2296 *pointer = 0;
2297 else
2298 *pointer = CUnifiedNetwork::getInstance()->getSendQueueSize();
2302 NLMISC_CATEGORISED_DYNVARIABLE(nel, uint64, ReceiveQueueSize, "current size in bytes of all receive queues")
2304 nlunreferenced(human);
2306 if (get)
2308 if (!CUnifiedNetwork::isUsed ())
2309 *pointer = 0;
2310 else
2311 *pointer = CUnifiedNetwork::getInstance()->getReceiveQueueSize();
2316 NLMISC_CATEGORISED_DYNVARIABLE(nel, uint64, ReceivedBytes, "total of bytes received by this service")
2318 nlunreferenced(human);
2320 if (get)
2322 if (!CUnifiedNetwork::isUsed ())
2323 *pointer = 0;
2324 else
2325 *pointer = CUnifiedNetwork::getInstance()->getBytesReceived ();
2329 NLMISC_CATEGORISED_DYNVARIABLE(nel, uint64, SentBytes, "total of bytes sent by this service")
2331 nlunreferenced(human);
2333 if (get)
2335 if (!CUnifiedNetwork::isUsed ())
2336 *pointer = 0;
2337 else
2338 *pointer = CUnifiedNetwork::getInstance()->getBytesSent ();
2344 * Simulate a message that comes from the network.
2346 * for the bool (b type), you must set the value to 1 or 0
2347 * for the string (s type), we don't manage space inside a string
2348 * for stl containers, you have first to put a u32 type that is the size of the container and after all elements
2349 * (ex: if you want to put a vector<uint16> that have 3 elements: u32 3 u16 10 u16 11 u16 12)
2351 * ex: msgin 128 REGISTER u32 10 u32 541 u32 45
2352 * You'll receive a fake message REGISTER that seems to come from the service number 128 with 3 uint32.
2356 NLMISC_CATEGORISED_COMMAND(nel, msgin, "Simulate an input message from another service (ex: msgin 128 REGISTER u32 10 b 1 f 1.5)", "<ServiceName>|<ServiceId> <MessageName> [<ParamType> <Param>]*")
2358 nlunreferenced(rawCommandString);
2359 nlunreferenced(quiet);
2360 nlunreferenced(human);
2362 if(args.size() < 2) return false;
2364 if (!CUnifiedNetwork::isUsed ())
2366 log.displayNL("Can't do that because the service doesn't use CUnifiedNetwork");
2367 return false;
2370 uint16 sId;
2371 fromString(args[0], sId);
2373 TServiceId serviceId(sId);
2374 string serviceName = args[0].c_str();
2375 string messageName = args[1].c_str();
2377 if (serviceId.get() > 255)
2379 log.displayNL ("Service Id %d must be between [1;255]", serviceId.get());
2380 return false;
2383 if ((args.size()-2) % 2 != 0)
2385 log.displayNL ("The number of parameter must be a multiple of 2");
2386 return false;
2389 CMessage msg (messageName);
2391 if (!createMessage (msg, args, log))
2392 return false;
2394 msg.invert ();
2396 TUnifiedMsgCallback cb = CUnifiedNetwork::getInstance()->findCallback (messageName);
2398 if (cb == NULL)
2400 log.displayNL ("Callback for message '%s' is not found", messageName.c_str());
2402 else
2404 cb (msg, serviceName, serviceId);
2407 return true;
2411 * Create a message and send it to the specified service
2413 * for the bool (b type), you must set the value to 1 or 0
2414 * for the string (s type), we don't manage space inside a string
2415 * for stl containers, you have first to put a u32 type that is the size of the container and after all elements
2416 * (ex: if you want to put a vector<uint16> that have 3 elements: u32 3 u16 10 u16 11 u16 12)
2418 * ex: msgout 128 REGISTER u32 10 u32 541 u32 45
2419 * You'll send a real message REGISTER to the service number 128 with 3 uint32.
2423 NLMISC_CATEGORISED_COMMAND(nel, msgout, "Send a message to a specified service (ex: msgout 128 REGISTER u32 10 b 1 f 1.5)", "<ServiceName>|<ServiceId> <MessageName> [<ParamType> <Param>]*")
2425 nlunreferenced(rawCommandString);
2426 nlunreferenced(quiet);
2427 nlunreferenced(human);
2429 if(args.size() < 2) return false;
2431 if (!CUnifiedNetwork::isUsed ())
2433 log.displayNL("Can't do that because the service doesn't use CUnifiedNetwork");
2434 return false;
2437 uint16 nId;
2438 fromString(args[0], nId);
2440 TServiceId serviceId(nId);
2441 string serviceName = args[0].c_str();
2442 string messageName = args[1].c_str();
2444 if (serviceId.get() > 255)
2446 log.displayNL ("Service Id %d must be between [1;255]", serviceId.get());
2447 return false;
2450 if ((args.size()-2) % 2 != 0)
2452 log.displayNL ("The number of parameter must be a multiple of 2");
2453 return false;
2456 CMessage msg (messageName);
2458 if (!createMessage (msg, args, log))
2459 return false;
2461 TSockId host = InvalidSockId;
2462 CCallbackNetBase *cnb = NULL;
2464 if (serviceId.get() != 0)
2465 cnb = CUnifiedNetwork::getInstance()->getNetBase (serviceId, host);
2466 else
2467 cnb = CUnifiedNetwork::getInstance()->getNetBase (serviceName, host);
2469 if (cnb == NULL)
2471 log.displayNL ("'%s' is a bad <ServiceId> or <ServiceName>", args[0].c_str());
2472 return false;
2475 cnb->send (msg, host);
2477 return true;
2480 NLMISC_CATEGORISED_COMMAND(nel, l5QueuesStats, "Displays queues stats of network layer5", "")
2482 nlunreferenced(rawCommandString);
2483 nlunreferenced(quiet);
2484 nlunreferenced(human);
2486 if(args.size() != 0) return false;
2488 if (!CUnifiedNetwork::isUsed ())
2490 log.displayNL("Can't display internal table because layer5 is not used");
2491 return false;
2494 log.displayNL ("%u Unified Connections:", CUnifiedNetwork::getInstance()->_IdCnx.size ());
2495 for (uint i = 0; i < CUnifiedNetwork::getInstance()->_IdCnx.size (); i++)
2497 if(CUnifiedNetwork::getInstance()->_IdCnx[i].State != CUnifiedNetwork::CUnifiedConnection::NotUsed)
2499 CUnifiedNetwork::getInstance()->_IdCnx[i].display (true, &log);
2503 return true;
2507 NLMISC_CATEGORISED_COMMAND(nel, l5InternalTables, "Displays internal table of network layer5", "")
2509 nlunreferenced(rawCommandString);
2510 nlunreferenced(quiet);
2511 nlunreferenced(human);
2513 if(args.size() != 0) return false;
2515 if (!CUnifiedNetwork::isUsed ())
2517 log.displayNL("Can't display internal table because layer5 is not used");
2518 return false;
2521 CUnifiedNetwork::getInstance ()->displayInternalTables(&log);
2523 return true;
2526 NLMISC_CATEGORISED_COMMAND(nel, l5Callback, "Displays all callback registered in layer5", "")
2528 nlunreferenced(rawCommandString);
2529 nlunreferenced(quiet);
2530 nlunreferenced(human);
2532 if(args.size() != 0) return false;
2534 if (!CUnifiedNetwork::isUsed ())
2536 log.displayNL("Can't display internal table because layer5 is not used");
2537 return false;
2540 log.displayNL ("There're %d registered callbacks:", CUnifiedNetwork::getInstance()->_Callbacks.size());
2541 uint i = 0;
2542 for (CUnifiedNetwork::TMsgMappedCallback::iterator it = CUnifiedNetwork::getInstance()->_Callbacks.begin(); it != CUnifiedNetwork::getInstance()->_Callbacks.end(); it++)
2544 log.displayNL (" %d '%s' %s", i++, (*it).first.c_str(), ((*it).second == NULL?"have a NULL address":""));
2547 return true;
2550 NLMISC_CATEGORISED_COMMAND(nel, isServiceLocal, "Says if a service is local or not compare with this service", "<sid>|<service name>")
2552 nlunreferenced(human);
2553 nlunreferenced(quiet);
2554 nlunreferenced(rawCommandString);
2556 if(args.size() != 1) return false;
2558 if (!CUnifiedNetwork::isUsed ())
2560 log.displayNL("Can't do that because the service doesn't use CUnifiedNetwork");
2561 return false;
2564 uint16 nId;
2565 fromString(args[0], nId);
2567 TServiceId sid(nId);
2568 if (sid.get() > 0)
2570 log.displayNL ("Service %s-%hu and sid %s are %son the same computer", CUnifiedNetwork::getInstance ()->_Name.c_str(), CUnifiedNetwork::getInstance ()->_SId.get(), args[0].c_str(), CUnifiedNetwork::getInstance ()->isServiceLocal (sid)?"":"not ");
2572 else
2574 log.displayNL ("Service %s-%hu and %s are %son the same computer", CUnifiedNetwork::getInstance ()->_Name.c_str(), CUnifiedNetwork::getInstance ()->_SId.get(), args[0].c_str(), CUnifiedNetwork::getInstance ()->isServiceLocal (args[0])?"":"not ");
2577 return true;
2580 NLMISC_CLASS_COMMAND_IMPL(CUnifiedNetwork, addService)
2582 nlunreferenced(human);
2583 nlunreferenced(quiet);
2584 nlunreferenced(args);
2586 TParsedCommandLine pcl;
2587 pcl.parseParamList(rawCommandString);
2589 if (pcl.SubParams.size() != 2)
2590 return false;
2592 // syntax is as follow :
2593 // <serviceName> ( address=<address:port> [sid=<serviceId>] [sendId] [external] [autoRetry] )
2595 TParsedCommandLine * serviceInfo = pcl.SubParams[1];
2596 const TParsedCommandLine *address = serviceInfo->getParam("address");
2597 if (address == NULL)
2599 log.displayNL("Can't find param 'address'");
2600 return false;
2603 CInetAddress ia(address->ParamValue);;
2604 if (!ia.isValid())
2606 log.displayNL("Can't parse internet address in '%s'", address->ParamValue.c_str());
2607 return false;
2610 TServiceId serviceId(0);
2611 const TParsedCommandLine *sid = serviceInfo->getParam("sid");
2612 if (sid != NULL)
2614 uint16 nId;
2615 fromString(sid->ParamValue, nId);
2616 serviceId.set(nId);
2619 bool sendId = serviceInfo->getParam("sendId") != NULL;
2620 bool external = serviceInfo->getParam("external") != NULL;
2621 bool autoRetry = serviceInfo->getParam("autoRetry") != NULL;
2623 log.displayNL("Adding service '%s' as sid %u with [sendId = %s], [external = %s], [autoRetry = %s]",
2624 serviceInfo->ParamName.c_str(),
2625 sid,
2626 sendId ? "YES" : "NO",
2627 external ? "YES" : "NO",
2628 autoRetry ? "YES" : "NO"
2631 addService(serviceInfo->ParamName,
2633 sendId,
2634 external,
2635 serviceId,
2636 autoRetry,
2637 false);
2639 return true;
2644 } // NLNET