topo_group_connect: infinite auto-connect cycle with pause + connecting nodes notification
- client_activity: desktop/server now ACTIVE by default, only mobile starts STANDBY - topo_group_connect: endless Phase1→2→3→1 cycle until connected - Pause between failed Phase2/3 attempts: 1s active, 30s standby - on_down always triggers cycle_restart when active_conn_count=0 - re-init destroys previous state and starts fresh - CHAT_EVT_CONNECTING_NODES event notifies GUI of current connecting nodes - Android: ConfigProvider default mode='auto', PTT fix, UDP log timer fix
This commit is contained in:
1 parent
5d02a6d419
commit
54ad03185b
10 files changed
+159
-63
No files matched your search
@@ -37,8 +37,9 @@ void chat_event_post(int type, const uint8_t* data, int len) {
|
||||
[20]="CONN_LIST", [21]="CONN_METRICS",
|
||||
[22]="MEMBER_DETAIL", [23]="MEMBER_LIST",
|
||||
[24]="MEMBER_UPDATED", [25]="MEMBER_REMOVED",
|
||||
[26]="BGP_INFO", [27]="CONNECTING_NODES",
|
||||
};
|
||||
const char* n = (type >= 1 && type <= 25) ? names[type] : "?";
|
||||
const char* n = (type >= 1 && type <= 27) ? names[type] : "?";
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CHAT, "chat_event: %s(%d) data=%d bytes", n, type, len);
|
||||
if (data && len > 0) log_dump(DEBUG_LEVEL_DEBUG, DEBUG_CATEGORY_CHAT, " ", data, len);
|
||||
}
|
||||
@@ -45,6 +45,7 @@ extern "C" {
|
||||
#define CHAT_EVT_MEMBER_UPDATED 24 /* [ch_id_len:1][ch_id:var][count:1][member:79B] — обновление одного мембера */
|
||||
#define CHAT_EVT_MEMBER_REMOVED 25 /* [ch_id_len:1][ch_id:var][node_id:8] — мембер удалён из канала */
|
||||
#define CHAT_EVT_BGP_INFO 26 /* текст: "rtt:XXms hop:X via bgp: [XXXX] name1 [XXXX] name2" */
|
||||
#define CHAT_EVT_CONNECTING_NODES 27 /* [ch_id_len:1][ch_id:var][count:2][node_id:8]* */
|
||||
|
||||
typedef void (*chat_event_handler_fn)(int type, const uint8_t* data, int len);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/* ═══════════════════════════════════════════════════════════════════════
|
||||
* Пауза: 1s ACTIVE / 30s 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");
|
||||
}
|
||||
@@ -1,16 +1,21 @@
|
||||
/**
|
||||
* @file topo_group_connect.h
|
||||
* @brief Авто-подключение к узлам группы при старте и отслеживание состояния connected в БД.
|
||||
* @brief Авто-подключение к узлам группы — бесконечный цикл до успеха.
|
||||
*
|
||||
* При старте CHAT-группы:
|
||||
* Phase 1 — одновременный запуск подключений ко всем пирам с connected=1.
|
||||
* При старте CHAT-группы (topo_group_connect_init):
|
||||
* Phase 1 — параллельный запуск ко всем пирам с connected=1 (таймаут 2с).
|
||||
* Если хоть один подключился → done.
|
||||
* Phase 2 — последовательный перебор по пирам с публичными адресами.
|
||||
* До первого успешного подключения.
|
||||
* Phase 2 — последовательный перебор (supernode, затем public/EIM).
|
||||
* После каждого TIMEOUT — пауза перед следующей попыткой.
|
||||
* 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.
|
||||
*
|
||||
* Отслеживание: на UP → peers_<ch>.connected=1, на DOWN (последний conn) → 0.
|
||||
* active_conn_count — количество уникальных peer-узлов с живыми соединениями.
|
||||
* restart — перезапуск авто-подключения (если phase=done и нет подключений).
|
||||
*/
|
||||
#ifndef TOPO_GROUP_CONNECT_H
|
||||
#define TOPO_GROUP_CONNECT_H
|
||||
|
||||
+1
-1
@@ -102,7 +102,7 @@ static int instance_init_common(struct UTUN_INSTANCE* instance, struct UASYNC* u
|
||||
// Set client type
|
||||
instance->client_type = config->global.client_type;
|
||||
instance->keepalive_interval = (uint16_t)(config->global.keepalive_interval > 0 ? config->global.keepalive_interval : 200);
|
||||
instance->client_activity = (instance->client_type == CLIENT_TYPE_SERVER) ? CLIENT_ACTIVITY_ACTIVE : CLIENT_ACTIVITY_STANDBY;
|
||||
instance->client_activity = (instance->client_type == CLIENT_TYPE_MOBILE) ? CLIENT_ACTIVITY_STANDBY : CLIENT_ACTIVITY_ACTIVE;
|
||||
instance->client_activity_timer = NULL;
|
||||
|
||||
// Set my keys
|
||||
|
||||
Reference in new issue
Block a user