@ -1,19 +1,24 @@
/*
* topo_group_connect . c — а в т о - п о д к л ю ч е н и е к у з л а м г р у п п ы п р и с т а р т е
* topo_group_connect . c — а в т о - п о д к л ю ч е н и е к у з л а м г р у п п ы ( б е с к о н е ч н ы й ц и к л д о у с п е х а )
*
* Phase 1 : о д н о в р е м е н н ы й з а п у с к conn_mgr_open_invite д л я п и р о в с connected = 1.
* Т а й м а у т = TGC_DIRECT_TIMEOUT_MS . П о т а й м а у т у з а к р ы в а е м н е з а в е р ш ё н н ы е handle ' ы
* и п о м е ч а е м connected = 0 в Б Д . Е с л и х о т ь о д и н п о д к л ю ч и л с я → done .
* Т а й м а у т = TGC_DIRECT_TIMEOUT_MS . Е с л и х о т ь о д и н п о д к л ю ч и л с я → done .
*
* Phase 2 : п о с л е д о в а т е л ь н ы й п е р е б о р п и р о в с п р я м ы м и / EIM а д р е с а м и .
* Phase 2 : п о с л е д о в а т е л ь н ы й п е р е б о р ( supernode , з а т е м public / EIM а д р е с а ) .
* П о с л е к а ж д о г о TIMEOUT — п а у з а п е р е д с л е д у ю щ е й п о п ы т к о й .
*
* Phase 3 : п о с л е д о в а т е л ь н ы й п е р е б о р п и р о в с л о к а л ь н ы м и / strict NAT .
* Phase 3 : п о с л е д о в а т е л ь н ы й п е р е б о р л о к а л ь н ы х / strict NAT а д р е с о в .
* П о с л е к а ж д о г о TIMEOUT — п а у з а п е р е д с л е д у ю щ е й п о п ы т к о й .
*
* П о с л е и с ч е р п а н и я Phase 3 → п а у з а → cycle_restart → Phase 1 ( б е с к о н е ч н о ) .
*
* П а у з а : 1 с к о г д а client_activity = = ACTIVE , 30 с к о г д а STANDBY ( Android ф о н ) .
* П р и DOWN ( active_conn_count → 0 ) — н е м е д л е н н ы й cycle_restart .
* П р и п о в т о р н о м topo_group_connect_init — destroy + fresh init .
*
* С ч ё т ч и к active_conn_count — ч и с л о у н и к а л ь н ы х peer - у з л о в с р е а л ь н ы м и с о е д и н е н и я м и .
* UP : и н к р е м е н т т о л ь к о д л я п е р в о г о с о е д и н е н и я к peer_node_id .
* DOWN : д е к р е м е н т т о л ь к о е с л и н е т д р у г и х conn к peer_node_id И н е т indirect - п у т е й в nodeinfo .
*
* restart : п е р е з а п у с к а в т о - п о д к л ю ч е н и я п р и phase = done и active_conn_count = = 0.
* DOWN : д е к р е м е н т т о л ь к о е с л и н е т д р у г и х conn к peer_node_id И н е т indirect - п у т е й .
*/
# include "topo_group_connect.h"
@ -26,6 +31,7 @@
# include "../lib/u_async.h"
# include "../lib/ll_queue.h"
# include "etcp.h"
# include "../chat/chat_event.h"
/* ─── внутренние константы ─── */
# define TGC_ID "topo_group_connect"
@ -34,13 +40,16 @@
# define TGC_PHASE_TWO 1
# define TGC_PHASE_THREE 2
# define TGC_PHASE_DONE 3
# define TGC_PHASE_PAUSE 4
# define TGC_MAX_HANDLES 128
# define TGC_PAUSE_ACTIVE_TB 10000 /* 1s пауза когда client_activity == ACTIVE */
# define TGC_PAUSE_IDLE_TB 300000 /* 30s пауза когда STANDBY (Android фон) */
struct TOPO_GROUP_CONNECT {
struct TOPO_GROUP * group ;
void * phase_timer ;
void * pause_timer ;
uint8_t phase ;
uint8_t active ;
int pending ;
int connected_count ;
int active_conn_count ;
@ -55,20 +64,39 @@ struct TOPO_GROUP_CONNECT {
static void tgc_phase2_try_next ( struct TOPO_GROUP_CONNECT * gc ) ;
static void tgc_phase3_try_next ( struct TOPO_GROUP_CONNECT * gc ) ;
static void tgc_phase1_timeout ( void * arg ) ;
static void tgc_cycle_restart ( struct TOPO_GROUP_CONNECT * gc ) ;
static void tgc_start_pause ( struct TOPO_GROUP_CONNECT * gc , void ( * cb ) ( void * ) , const char * label ) ;
static void tgc_callback ( struct CONN_MGR_HANDLE * h , uint64_t node_id , uint64_t group_id ,
enum conn_mgr_event event , void * arg ) ;
/* ─── нотификация GUI о списке узлов в процессе подключения ─── */
static void tgc_notify_connecting ( struct TOPO_GROUP_CONNECT * gc , const uint64_t * ids , int count ) {
size_t cl = strlen ( gc - > group - > channel_id ) ; if ( cl > 255 ) cl = 255 ;
size_t sz = 1 + cl + 2 + ( size_t ) count * 8 ;
uint8_t * buf = u_malloc ( sz ) ; if ( ! buf ) return ;
uint8_t * p = buf ;
* p + + = ( uint8_t ) cl ; memcpy ( p , gc - > group - > channel_id , cl ) ; p + = cl ;
uint16_t c = ( uint16_t ) count ; memcpy ( p , & c , 2 ) ; p + = 2 ;
for ( int i = 0 ; i < count ; i + + ) { memcpy ( p , & ids [ i ] , 8 ) ; p + = 8 ; }
chat_event_post ( CHAT_EVT_CONNECTING_NODES , buf , ( int ) sz ) ;
u_free ( buf ) ;
DEBUG_DEBUG ( DEBUG_CATEGORY_BGP , " %s: notify_connecting ch=%s count=%d " ,
TGC_ID , gc - > group - > channel_id , count ) ;
}
/* ═══════════════════════════════════════════════════════════════════════
* Ж и з н е н н ы й ц и к л
* ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ */
int topo_group_connect_init ( struct TOPO_GROUP * group ) {
if ( ! group | | group - > group_type ! = TOPO_GROUP_TYPE_CHAT | | ! group - > channel_id [ 0 ] | | group - > connect )
if ( ! group | | group - > group_type ! = TOPO_GROUP_TYPE_CHAT | | ! group - > channel_id [ 0 ] )
return - 1 ;
if ( group - > connect ) topo_group_connect_destroy ( group ) ;
struct TOPO_GROUP_CONNECT * gc = u_calloc ( 1 , sizeof ( * gc ) ) ;
if ( ! gc ) return - 1 ;
gc - > group = group ; gc - > active = 1 ; gc - > tried_super = 0 ;
gc - > group = group ; gc - > tried_super = 0 ;
group - > connect = gc ;
uint64_t * ids = NULL ; int count = 0 ;
@ -89,6 +117,7 @@ int topo_group_connect_init(struct TOPO_GROUP* group) {
conn_mgr_open_invite ( group - > instance , group - > group_id , NULL , ids [ i ] , tgc_callback , gc ,
& gc - > handles [ gc - > handle_count + + ] ) ;
}
tgc_notify_connecting ( gc , ids , gc - > handle_count ) ;
u_free ( ids ) ;
gc - > phase_timer = uasync_set_timeout ( group - > instance - > ua ,
@ -101,6 +130,7 @@ void topo_group_connect_destroy(struct TOPO_GROUP* group) {
if ( ! gc ) return ;
group - > connect = NULL ;
if ( gc - > phase_timer ) { uasync_cancel_timeout ( group - > instance - > ua , gc - > phase_timer ) ; gc - > phase_timer = NULL ; }
if ( gc - > pause_timer ) { uasync_cancel_timeout ( group - > instance - > ua , gc - > pause_timer ) ; gc - > pause_timer = NULL ; }
for ( int i = 0 ; i < gc - > handle_count ; i + + ) conn_mgr_close ( gc - > handles [ i ] ) ;
u_free ( gc - > candidate_ids ) ;
u_free ( gc ) ;
@ -184,9 +214,9 @@ void topo_group_connect_on_down(struct TOPO_GROUP* group, struct ETCP_CONN* conn
if ( db ) topo_node_sqlite_set_connected ( db , group - > channel_id , peer , 0 ) ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: DOWN peer=0x%016llx ch=%s active=%d " ,
TGC_ID , ( unsigned long long ) peer , group - > channel_id , gc - > active_conn_count ) ;
if ( gc - > phase = = TGC_PHASE_DONE & & gc - > active_conn_count = = 0 ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_BGP , " %s: DOWN restart eligible ch=%s → restarting " , TGC_ID , group - > channel_id ) ;
topo_group_connect_restart ( group ) ;
if ( gc - > active_conn_count = = 0 ) {
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: DOWN no connections — cycle restart ch=%s " , TGC_ID , group - > channel_id ) ;
tgc_cycle_restart ( gc ) ;
}
}
@ -220,23 +250,39 @@ static void tgc_callback(struct CONN_MGR_HANDLE* h, uint64_t node_id, uint64_t g
if ( ok ) {
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: Phase2 connected to 0x%016llx — done " , TGC_ID , ( unsigned long long ) node_id ) ;
gc - > phase = TGC_PHASE_DONE ;
tgc_notify_connecting ( gc , NULL , 0 ) ;
} else {
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: Phase2 0x%016llx FAIL — try next " , TGC_ID , ( unsigned long long ) node_id ) ;
tgc_phase2_try_next ( gc ) ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: Phase2 0x%016llx FAIL — pause then try next " , TGC_ID , ( unsigned long long ) node_id ) ;
tgc_start_pause ( gc , ( void ( * ) ( void * ) ) tgc_phase2_try_next , " tgc_p2_try " ) ;
}
break ;
case TGC_PHASE_THREE :
if ( ok ) {
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: Phase3 connected to 0x%016llx — done " , TGC_ID , ( unsigned long long ) node_id ) ;
gc - > phase = TGC_PHASE_DONE ;
tgc_notify_connecting ( gc , NULL , 0 ) ;
} else {
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: Phase3 0x%016llx FAIL — try next " , TGC_ID , ( unsigned long long ) node_id ) ;
tgc_phase3_try_next ( gc ) ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: Phase3 0x%016llx FAIL — pause then try next " , TGC_ID , ( unsigned long long ) node_id ) ;
tgc_start_pause ( gc , ( void ( * ) ( void * ) ) tgc_phase3_try_next , " tgc_p3_try " ) ;
}
break ;
}
}
/* ═══════════════════════════════════════════════════════════════════════
* П а у з а : 1 s ACTIVE / 30 s STANDBY
* ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ */
static void tgc_start_pause ( struct TOPO_GROUP_CONNECT * gc , void ( * cb ) ( void * ) , const char * label ) {
gc - > phase = TGC_PHASE_PAUSE ;
int active = ( gc - > group - > instance - > client_activity = = CLIENT_ACTIVITY_ACTIVE ) ;
int delay_tb = active ? TGC_PAUSE_ACTIVE_TB : TGC_PAUSE_IDLE_TB ;
gc - > pause_timer = uasync_set_timeout ( gc - > group - > instance - > ua , delay_tb , gc , cb , label ) ;
tgc_notify_connecting ( gc , NULL , 0 ) ;
DEBUG_DEBUG ( DEBUG_CATEGORY_BGP , " %s: pause %dms (activity=%s) for ch=%s " ,
TGC_ID , delay_tb / 10 , active ? " ACTIVE " : " STANDBY " , gc - > group - > channel_id ) ;
}
/* ═══════════════════════════════════════════════════════════════════════
* Phase 1 timeout
* ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ */
@ -273,6 +319,7 @@ static void tgc_phase1_timeout(void* arg) {
if ( gc - > connected_count > 0 ) {
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: Phase1 done — %d connected, skipping Phase 2 " , TGC_ID , gc - > connected_count ) ;
gc - > phase = TGC_PHASE_DONE ;
tgc_notify_connecting ( gc , NULL , 0 ) ;
return ;
}
gc - > phase = TGC_PHASE_TWO ;
@ -284,6 +331,7 @@ static void tgc_phase1_timeout(void* arg) {
* ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ */
static void tgc_phase2_try_next ( struct TOPO_GROUP_CONNECT * gc ) {
if ( gc - > phase = = TGC_PHASE_PAUSE ) gc - > phase = TGC_PHASE_TWO ;
if ( gc - > phase ! = TGC_PHASE_TWO ) return ;
while ( 1 ) {
if ( gc - > candidate_count = = 0 ) {
@ -311,6 +359,7 @@ static void tgc_phase2_try_next(struct TOPO_GROUP_CONNECT* gc) {
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: Phase2 trying 0x%016llx (%d/%d) super_round=%d " , TGC_ID ,
( unsigned long long ) nid , gc - > cursor , gc - > candidate_count , gc - > tried_super ) ;
if ( conn_mgr_open_invite ( gc - > group - > instance , gc - > group - > group_id , NULL , nid , tgc_callback , gc , NULL ) < 0 ) continue ;
tgc_notify_connecting ( gc , & nid , 1 ) ;
return ;
}
if ( gc - > candidate_ids ) { u_free ( gc - > candidate_ids ) ; gc - > candidate_ids = NULL ; }
@ -323,6 +372,7 @@ static void tgc_phase2_try_next(struct TOPO_GROUP_CONNECT* gc) {
* ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ */
static void tgc_phase3_try_next ( struct TOPO_GROUP_CONNECT * gc ) {
if ( gc - > phase = = TGC_PHASE_PAUSE ) gc - > phase = TGC_PHASE_THREE ;
if ( gc - > phase ! = TGC_PHASE_THREE ) return ;
if ( gc - > candidate_count = = 0 ) {
sqlite3 * db = gc - > group - > instance - > topo_sqlite_db ;
@ -337,8 +387,48 @@ static void tgc_phase3_try_next(struct TOPO_GROUP_CONNECT* gc) {
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: Phase3 trying 0x%016llx (%d/%d) " , TGC_ID ,
( unsigned long long ) nid , gc - > cursor , gc - > candidate_count ) ;
if ( conn_mgr_open_invite ( gc - > group - > instance , gc - > group - > group_id , NULL , nid , tgc_callback , gc , NULL ) < 0 ) continue ;
tgc_notify_connecting ( gc , & nid , 1 ) ;
return ;
}
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: Phase3 exhausted — no connections, ch=%s " , TGC_ID , gc - > group - > channel_id ) ;
gc - > phase = TGC_PHASE_DONE ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: Phase3 exhausted — pause then cycle restart, ch=%s " , TGC_ID , gc - > group - > channel_id ) ;
tgc_start_pause ( gc , ( void ( * ) ( void * ) ) tgc_cycle_restart , " tgc_p3_restart " ) ;
}
/* ═══════════════════════════════════════════════════════════════════════
* П о л н ы й п е р е з а п у с к ц и к л а с Phase 1 ( б е з destroy / init )
* ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ ═ */
static void tgc_cycle_restart ( struct TOPO_GROUP_CONNECT * gc ) {
gc - > pause_timer = NULL ;
if ( gc - > phase_timer ) { uasync_cancel_timeout ( gc - > group - > instance - > ua , gc - > phase_timer ) ; gc - > phase_timer = NULL ; }
for ( int i = 0 ; i < gc - > handle_count ; i + + ) conn_mgr_close ( gc - > handles [ i ] ) ;
gc - > handle_count = 0 ; gc - > pending = 0 ; gc - > connected_count = 0 ;
gc - > cursor = 0 ; gc - > tried_super = 0 ;
u_free ( gc - > candidate_ids ) ; gc - > candidate_ids = NULL ; gc - > candidate_count = 0 ;
uint64_t * ids = NULL ; int count = 0 ;
sqlite3 * db = gc - > group - > instance - > topo_sqlite_db ;
DEBUG_DEBUG ( DEBUG_CATEGORY_BGP , " %s: cycle_restart ch=%s active=%d " ,
TGC_ID , gc - > group - > channel_id , gc - > active_conn_count ) ;
if ( ! db | | topo_node_sqlite_get_connected_peers ( db , gc - > group - > channel_id , & ids , & count ) ! = 0 | | count = = 0 ) {
if ( ids ) { u_free ( ids ) ; ids = NULL ; }
gc - > phase = TGC_PHASE_TWO ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: cycle_restart ch=%s no connected peers → Phase 2 " , TGC_ID , gc - > group - > channel_id ) ;
tgc_phase2_try_next ( gc ) ;
return ;
}
gc - > phase = TGC_PHASE_ONE ; gc - > pending = count ;
DEBUG_INFO ( DEBUG_CATEGORY_BGP , " %s: cycle_restart Phase 1 launching %d connects ch=%s " ,
TGC_ID , count , gc - > group - > channel_id ) ;
for ( int i = 0 ; i < count & & gc - > handle_count < TGC_MAX_HANDLES ; i + + ) {
DEBUG_DEBUG ( DEBUG_CATEGORY_BGP , " %s: Phase1 connect to 0x%016llx (%d/%d) " , TGC_ID , ( unsigned long long ) ids [ i ] , i + 1 , count ) ;
conn_mgr_open_invite ( gc - > group - > instance , gc - > group - > group_id , NULL , ids [ i ] , tgc_callback , gc ,
& gc - > handles [ gc - > handle_count + + ] ) ;
}
tgc_notify_connecting ( gc , ids , gc - > handle_count ) ;
u_free ( ids ) ;
gc - > phase_timer = uasync_set_timeout ( gc - > group - > instance - > ua ,
TGC_DIRECT_TIMEOUT_MS * 10 , gc , tgc_phase1_timeout , " tgc_phase1 " ) ;
}