1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
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"
35 using namespace NLMISC
;
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)
50 CVariable
<uint32
> UseYieldMethod("nel", "UseYieldMethod", "0=select 1=usleep 2=nanosleep 3=sched_yield 4=none", 0, 0, true );
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();
83 // it's me! don't add me!!!
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
145 for (vector
<TServiceId
>::iterator it
= uni
->_UsedConnection
.begin (); it
!= uni
->_UsedConnection
.end(); it
++)
147 if (*it
== uc
->ServiceId
)
150 uni
->_UsedConnection
.erase (it
);
155 AUTOCHECK_DISPLAY ("HNETL5: can't find the sid %hu in the _UsedConnection", uc
->ServiceId
.get());
157 // reset the unified connection
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());
181 CUnifiedNetwork
*uni
= CUnifiedNetwork::getInstance();
182 TServiceId
sid(uint16(from
->appId()));
183 CUnifiedNetwork::CUnifiedConnection
*uc
= uni
->getUnifiedConnection (sid
);
186 nlinfo ("HNETL5: - connec '%s' sid %hu", from
->asString().c_str(), sid
.get());
190 nlinfo ("HNETL5: - connec '%s' %s-%hu", from
->asString().c_str(), uc
->ServiceName
.c_str (), sid
.get());
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
);
207 // call the user callback
208 uni
->callServiceDownCallback(uc
->ServiceName
, uc
->ServiceId
);
213 // reset the connection
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();
226 // if it s a client, we can't delete now because the callback client is currently in use
228 if(uc
->Connections
[i
].CbNetBase
->connected ())
230 uc
->Connections
[i
].CbNetBase
->disconnect (uc
->Connections
[i
].HostId
);
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
)
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
);
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
);
274 if (inSid
.get() == 0)
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());
285 nlwarning ("HNETL5: Received a connection from a service with a SId 0 and wasn't external, disconnecting it");
286 netbase
.disconnect();
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
);
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
);
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
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
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");
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());
364 CUnifiedNetwork::CUnifiedConnection
*uc
= uni
->getUnifiedConnection (sid
);
368 nlwarning ("HNETL5: Received a message from a service %hu that is not ready (bad appid? 0x%" NL_I64
"X)", sid
.get(), from
->appId ());
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());
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
428 CAliveCheck() : ExitRequired(false) { }
431 virtual ~CAliveCheck() { }
433 volatile bool ExitRequired
;
435 static CAliveCheck
* Thread
;
439 CCheckAddress() : ConnectionId(0xDEAD), NeedCheck(false), AddressValid(false) { }
440 CInetAddress Address
;
441 std::string ServiceName
;
442 TServiceId ServiceId
;
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()
464 TTime t
= CTime::getLocalTime();
466 while (!ExitRequired
)
468 if (CTime::getLocalTime() - t
< 10000)
475 for (i
=0; i
<sizeof(CheckList
)/sizeof(CheckList
[0]); ++i
)
477 if (CheckList
[i
].NeedCheck
&& !CheckList
[i
].AddressValid
)
482 cbc
.connect(CheckList
[i
].Address
);
483 // success (no exception)
484 CheckList
[i
].AddressValid
= true;
487 catch (const ESocketConnectionFailed
&e
)
490 nlinfo ("HNETL5: can't connect to %s-%hu now (%s)", CheckList
[i
].ServiceName
.c_str(), CheckList
[i
].ServiceId
.get(), e
.what ());
492 nlwarning ("HNETL5: can't connect to %s-%hu now (%s)", CheckList
[i
].ServiceName
.c_str(), CheckList
[i
].ServiceId
.get(), e
.what ());
498 t
= CTime::getLocalTime();
501 ExitRequired
= false;
504 void CAliveCheck::checkService(CInetAddress address
, uint connectionId
, uint connectionIndex
, const std::string
&service
, TServiceId id
)
507 for (i
=0; i
<sizeof(CheckList
)/sizeof(CheckList
[0]); ++i
)
509 if (CheckList
[i
].NeedCheck
)
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;
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();
538 AUTOCHECK_DISPLAY ("HNETL5: Unified network layer already initialized");
542 ThreadCreator
= NLMISC::getThreadId();
544 vector
<CInetAddress
> laddr
= CInetAddress::localAddresses();
546 _RecordingState
= rec
;
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
);
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");
578 // setup the server callback only if server port != 0, otherwise there's no server callback
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
);
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
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
);
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
);
627 if ( ! CNamingClient::registerService(_Name
, laddr
, _SId
) )
629 nlinfo ("HNETL5: Registration denied");
635 CNamingClient::registerServiceWithSId(_Name
, laddr
, _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();
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);
675 // don't process services received after mine because they'll connect to me
682 void CUnifiedNetwork::release(bool mustFlushSendQueues
, const std::vector
<std::string
>& namesOfOnlyServiceToFlushSending
)
687 // the commands can't be invoked
688 unregisterCommandsHandler();
690 // terminate the auto reconnection thread
691 if (CAliveCheck::Thread
)
693 CAliveCheck::Thread
->ExitRequired
= true;
695 delete CAliveCheck::Thread
;
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 )
710 totalBytes
= (float)bytesRemaining
;
713 nldebug( "%.1f%% of %.3f KB flushed so far", // display without HNETL5 to bypass filters!
714 ((float)(bytesRemaining
-totalBytes
))/totalBytes
, totalBytes
/ 1024.0f
);
721 nldebug( "HNETL5: Flush done in %u steps", i
+1 );
724 // disconnect all clients
727 _CbServer
->disconnect(InvalidSockId
);
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
753 _UsedConnection
.clear ();
755 _UpCallbacks
.clear();
756 _DownCallbacks
.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
;
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());
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
);
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
;
827 uc
->ExtAddress
= addr
;
828 nlassert (!addr
.empty());
830 // associate nid with ext address
831 uc
->setupNetworkAssociation (_NetworkAssociations
, _DefaultNetwork
);
833 // connect to all connection
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
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
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 ());
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
);
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");
899 uc
->Connections
[i
] = CUnifiedNetwork::CUnifiedConnection::TConnection(cbc
);
902 if (connectSuccess
&& sendId
)
904 // send identification to the service
905 CMessage
msg("UN_SIDENT");
907 TServiceId ssid
= _SId
;
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
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
);
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());
929 for (uint j
= 0; j
< uc
->Connections
.size(); j
++)
931 if (uc
->Connections
[j
].CbNetBase
!= NULL
)
933 if (uc
->Connections
[j
].CbNetBase
->connected ())
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(); \
959 TTicks _time_block_after = CTime::getPerformanceTime(); \
960 tick += (_time_block_after - _before); \
966 void CUnifiedNetwork::update(TTime timeout
)
968 H_AUTO(CUnifiedNetworkUpdate
);
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 ();
981 if (_NextUpdateTime
== 0)
983 _NextUpdateTime
= t0
+ timeout
;
987 TTime err
= t0
- _NextUpdateTime
;
988 _NextUpdateTime
+= timeout
;
990 // if we are too late, resync to the next value
991 while (err
> timeout
)
994 _NextUpdateTime
+= timeout
;
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);
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
);
1041 H_AUTO(L5OneLoopUpdate
);
1043 if (CAliveCheck::Thread
)
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
&&
1056 uc
.ValidRequested
= false;
1057 uc
.ValidExternal
= true;
1060 address
.NeedCheck
= false;
1061 address
.AddressValid
= false;
1066 // update all server connections
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
);
1088 if (conn
.IsServerConnection
)
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
1119 for (vector
<TServiceId
>::iterator it
= _UsedConnection
.begin (); it
!= _UsedConnection
.end(); it
++)
1121 if (*it
== _IdCnx
[_ConnectionToReset
[i
].get()].ServiceId
)
1124 _UsedConnection
.erase (it
);
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
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 )
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
);
1189 // Enable windows multithreading before rescanning all connections
1190 H_TIME(L5UpdateSleep
, nlSleep(1);); // 0 (yield) would be too harmful to other applications
1193 H_AFTER(UNUpdateCnx
);
1195 H_TIME(UNAutoCheck
, autoCheck(););
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
1219 // send identification to the service
1220 CMessage
msg("UN_SIDENT");
1223 TServiceId ssid
= _SId
;
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
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
)
1244 nlinfo ("HNETL5: can't connect to %s-%hu now (%s)", uc
.ServiceName
.c_str(), uc
.ServiceId
.get(), e
.what ());
1246 nlwarning ("HNETL5: can't connect to %s-%hu now (%s)", uc
.ServiceName
.c_str(), uc
.ServiceId
.get(), e
.what ());
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
1258 // Prepare for select()
1260 FD_ZERO( &readers
);
1261 FD_SET( _MainDataAvailablePipe
[PipeRead
], &readers
);
1262 SOCKET descmax
= _MainDataAvailablePipe
[PipeRead
] + 1;
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
);
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
)
1281 nlassert(_MainDataAvailableHandle
);
1282 WaitForSingleObject(_MainDataAvailableHandle
, msecMax
);
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 ());
1306 // by default, connection id will be the default one
1307 uint8 connectionId
= _IdCnx
[sid
.get()].DefaultNetwork
;
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
);
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 ());
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())
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
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 ());
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 ());
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
);
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 ());
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());
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());
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());
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());
1438 _IdCnx
[sid
.get()].Connections
[connectionId
].CbNetBase
->send (msgout
, _IdCnx
[sid
.get()].Connections
[connectionId
].HostId
);
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
);
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
)
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()) )
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())
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
)
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
== "*")
1523 _UpUniCallback
.push_back (make_pair(cb
, arg
));
1525 _UpUniCallback
.insert (_UpUniCallback
.begin(), make_pair(cb
, arg
));
1530 _UpCallbacks
[serviceName
].push_back (make_pair(cb
, arg
));
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
== "*")
1541 for (i
=0; i
<_UpUniCallback
.size(); ++i
)
1543 if (_UpUniCallback
[i
].first
== cb
&& _UpUniCallback
[i
].second
== arg
)
1546 _UpUniCallback
.erase(_UpUniCallback
.begin()+i
);
1550 if (i
== _UpUniCallback
.size())
1552 nlwarning("HNETL5 : can't remove service up callback, not found");
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
)
1572 nlwarning("HNETL5 : can't remove service up callback, not found");
1577 // no more callback for this service
1578 _UpCallbacks
.erase(serviceName
);
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
== "*")
1594 _DownUniCallback
.push_back (make_pair(cb
, arg
));
1596 _DownUniCallback
.insert (_DownUniCallback
.begin(), make_pair(cb
, arg
));
1601 _DownCallbacks
[serviceName
].push_back (make_pair(cb
, arg
));
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
== "*")
1612 for (i
=0; i
<_DownUniCallback
.size(); ++i
)
1614 if (_DownUniCallback
[i
].first
== cb
&& _DownUniCallback
[i
].second
== arg
)
1617 _DownUniCallback
.erase(_DownUniCallback
.begin()+i
);
1621 if (i
== _DownUniCallback
.size())
1623 nlwarning("HNETL5 : can't remove service down callback, not found");
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
)
1643 nlwarning("HNETL5 : can't remove service down callback, not found");
1648 // no more callback for this service
1649 _DownCallbacks
.erase(serviceName
);
1654 nlwarning("HNETL5 : can't remove service down callback, not found");
1662 uint64
CUnifiedNetwork::getBytesSent ()
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();
1676 sent
+= _CbServer
->getBytesSent();
1680 uint64
CUnifiedNetwork::getBytesReceived ()
1682 uint64 received
= 0;
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();
1694 received
+= _CbServer
->getBytesReceived();
1698 uint64
CUnifiedNetwork::getSendQueueSize ()
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();
1712 sent
+= _CbServer
->getSendQueueSize();
1716 uint64
CUnifiedNetwork::getReceiveQueueSize ()
1718 uint64 received
= 0;
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();
1730 received
+= _CbServer
->getReceiveQueueSize();
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
);
1744 nlwarning ("HNETL5: couldn't access the service %s", name
.c_str());
1745 host
= InvalidSockId
;
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
;
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
;
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
;
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())
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
)
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
);
1819 bool CUnifiedNetwork::isServiceLocal (TServiceId sid
)
1821 // it s me, of course we are local
1825 if (sid
.get() >= _IdCnx
.size () || _IdCnx
[sid
.get()].State
!= CUnifiedNetwork::CUnifiedConnection::Ready
)
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 ())
1839 if (_IdCnx
[sid
.get()].ExtAddress
[j
].internalIPAddress () == laddr
[i
].internalIPAddress ())
1848 * Return the name of the specified service, or "" if not found
1850 std::string
CUnifiedNetwork::getServiceName(TServiceId sid
)
1853 CUnifiedConnection
*c
= getUnifiedConnection(sid
, false);
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
)
1866 CUnifiedConnection
*c
= getUnifiedConnection(sid
, false);
1868 s
= c
->ServiceName
+ "-";
1869 s
+= toString(sid
.get());
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());
1899 return &_IdCnx
[sid
.get()];
1904 nlwarning ("HNETL5: Try to get a bad unified connection (sid %hu is not in the table)", sid
.get());
1909 void CUnifiedNetwork::autoCheck()
1911 H_AUTO(L5UpdateAutoCheck
);
1914 for (i
= 0; i
< _IdCnx
.size (); i
++)
1916 if (_IdCnx
[i
].State
== CUnifiedNetwork::CUnifiedConnection::Ready
)
1918 _IdCnx
[i
].AutoCheck
= 1;
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 (),
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 (),
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 (),
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
)
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());
2014 for (i
= 0; i
< _IdCnx
.size (); i
++)
2016 if(_IdCnx
[i
].State
!= CUnifiedNetwork::CUnifiedConnection::NotUsed
)
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
);
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());
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
);
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());
2099 // select good service id
2100 for (it
=range
.first
; it
!=range
.second
&& (*it
).second
!=sid
; ++it
)
2104 if (it
== range
.second
)
2106 AUTOCHECK_DISPLAY ("HNETL5: The unified connection %s-%hu wasn't on the _NamedCnx", name
.c_str(), sid
.get());
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());
2131 _NotifiedUpCallbacks
.insert(pss
);
2133 // now we warn the user
2134 CUnifiedNetwork::TNameMappedCallback::iterator it
= _UpCallbacks
.find(serviceName
);
2135 if (it
!= _UpCallbacks
.end())
2138 for (list
<TCallbackArgItem
>::iterator it2
= (*it
).second
.begin(); it2
!= (*it
).second
.end(); it2
++)
2140 TUnifiedNetCallback cb
= (*it2
).first
;
2142 cb(serviceName
, sid
, (*it2
).second
);
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
);
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());
2168 _NotifiedUpCallbacks
.erase(pss
);
2170 // now we warn the user
2171 CUnifiedNetwork::TNameMappedCallback::iterator it
= _DownCallbacks
.find(serviceName
);
2172 if (it
!= _DownCallbacks
.end())
2175 for (list
<TCallbackArgItem
>::iterator it2
= (*it
).second
.begin(); it2
!= (*it
).second
.end(); it2
++)
2177 TUnifiedNetCallback cb
= (*it2
).first
;
2179 cb(serviceName
, sid
, (*it2
).second
);
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
);
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
++)
2207 if(j
< ExtAddress
.size ())
2209 base
+= ExtAddress
[j
].asString ();
2217 if(j
< Connections
.size () && Connections
[j
].valid())
2219 if(Connections
[j
].IsServerConnection
)
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";
2232 ext
+= " NotConnected";
2239 log
->displayNL (" - %s %s", base
.c_str (), ext
.c_str ());
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
);
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; }
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
);
2295 if (!CUnifiedNetwork::isUsed ())
2298 *pointer
= CUnifiedNetwork::getInstance()->getSendQueueSize();
2302 NLMISC_CATEGORISED_DYNVARIABLE(nel
, uint64
, ReceiveQueueSize
, "current size in bytes of all receive queues")
2304 nlunreferenced(human
);
2308 if (!CUnifiedNetwork::isUsed ())
2311 *pointer
= CUnifiedNetwork::getInstance()->getReceiveQueueSize();
2316 NLMISC_CATEGORISED_DYNVARIABLE(nel
, uint64
, ReceivedBytes
, "total of bytes received by this service")
2318 nlunreferenced(human
);
2322 if (!CUnifiedNetwork::isUsed ())
2325 *pointer
= CUnifiedNetwork::getInstance()->getBytesReceived ();
2329 NLMISC_CATEGORISED_DYNVARIABLE(nel
, uint64
, SentBytes
, "total of bytes sent by this service")
2331 nlunreferenced(human
);
2335 if (!CUnifiedNetwork::isUsed ())
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");
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());
2383 if ((args
.size()-2) % 2 != 0)
2385 log
.displayNL ("The number of parameter must be a multiple of 2");
2389 CMessage
msg (messageName
);
2391 if (!createMessage (msg
, args
, log
))
2396 TUnifiedMsgCallback cb
= CUnifiedNetwork::getInstance()->findCallback (messageName
);
2400 log
.displayNL ("Callback for message '%s' is not found", messageName
.c_str());
2404 cb (msg
, serviceName
, serviceId
);
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");
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());
2450 if ((args
.size()-2) % 2 != 0)
2452 log
.displayNL ("The number of parameter must be a multiple of 2");
2456 CMessage
msg (messageName
);
2458 if (!createMessage (msg
, args
, log
))
2461 TSockId host
= InvalidSockId
;
2462 CCallbackNetBase
*cnb
= NULL
;
2464 if (serviceId
.get() != 0)
2465 cnb
= CUnifiedNetwork::getInstance()->getNetBase (serviceId
, host
);
2467 cnb
= CUnifiedNetwork::getInstance()->getNetBase (serviceName
, host
);
2471 log
.displayNL ("'%s' is a bad <ServiceId> or <ServiceName>", args
[0].c_str());
2475 cnb
->send (msg
, host
);
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");
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
);
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");
2521 CUnifiedNetwork::getInstance ()->displayInternalTables(&log
);
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");
2540 log
.displayNL ("There're %d registered callbacks:", CUnifiedNetwork::getInstance()->_Callbacks
.size());
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":""));
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");
2565 fromString(args
[0], nId
);
2567 TServiceId
sid(nId
);
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 ");
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 ");
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)
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'");
2603 CInetAddress
ia(address
->ParamValue
);;
2606 log
.displayNL("Can't parse internet address in '%s'", address
->ParamValue
.c_str());
2610 TServiceId
serviceId(0);
2611 const TParsedCommandLine
*sid
= serviceInfo
->getParam("sid");
2615 fromString(sid
->ParamValue
, 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(),
2626 sendId
? "YES" : "NO",
2627 external
? "YES" : "NO",
2628 autoRetry
? "YES" : "NO"
2631 addService(serviceInfo
->ParamName
,