diff --git a/src/services/websockets.service.ts b/src/services/websockets.service.ts index 492a995..9bbe53e 100755 --- a/src/services/websockets.service.ts +++ b/src/services/websockets.service.ts @@ -1,89 +1,135 @@ -import { AnkFlag } from 'pkg/sdk_client'; +import { AnkFlag } from '../../pkg/sdk_client'; // Vérifie le chemin vers pkg import Services from './service'; -let ws: WebSocket; +let ws: WebSocket | null = null; let messageQueue: string[] = []; +let reconnectInterval = 1000; // Délai initial de 1s avant reconnexion +const MAX_RECONNECT_INTERVAL = 30000; // Max 30s +let isConnecting = false; +let urlReference: string = ''; +let pingIntervalId: any = null; + export async function initWebsocket(url: string) { - ws = new WebSocket(url); - - if (ws !== null) { - ws.onopen = async (event) => { - console.log('WebSocket connection established'); - - while (messageQueue.length > 0) { - const message = messageQueue.shift(); - if (message) { - ws.send(message); - } - } - }; - - // Listen for messages - ws.onmessage = (event) => { - const msgData = event.data; - - // console.log("Received text message: ", msgData); - (async () => { - if (typeof msgData === 'string') { - try { - const parsedMessage = JSON.parse(msgData); - const services = await Services.getInstance(); - switch (parsedMessage.flag) { - case 'Handshake': - await services.handleHandshakeMsg(url, parsedMessage.content); - break; - case 'NewTx': - await services.parseNewTx(parsedMessage.content); - break; - case 'Cipher': - await services.parseCipher(parsedMessage.content); - break; - case 'Commit': - // Basically if we see this it means we have an error - await services.handleCommitError(parsedMessage.content); - break; - } - } catch (error) { - console.error('Received an invalid message:', error); - } - } else { - console.error('Received a non-string message'); - } - })(); - }; - - // Listen for possible errors - ws.onerror = (event) => { - console.error('WebSocket error:', event); - }; - - // Listen for when the connection is closed - ws.onclose = (event) => { - console.log('WebSocket is closed now.'); - }; - } + urlReference = url; + connect(); +} + +function connect() { + if (isConnecting || (ws && ws.readyState === WebSocket.OPEN)) return; + isConnecting = true; + + console.log(`[WS] 🔌 Tentative de connexion à ${urlReference}...`); + ws = new WebSocket(urlReference); + + ws.onopen = async () => { + console.log('[WS] ✅ Connexion établie !'); + isConnecting = false; + reconnectInterval = 1000; // Reset du délai + + // Démarrer le Heartbeat (Ping pour garder la connexion vivante) + startHeartbeat(); + + // Vider la file d'attente (messages envoyés pendant la coupure) + while (messageQueue.length > 0) { + const message = messageQueue.shift(); + if (message) ws?.send(message); + } + }; + + ws.onmessage = (event) => { + const msgData = event.data; + if (typeof msgData === 'string') { + (async () => { + try { + const parsedMessage = JSON.parse(msgData); + const services = await Services.getInstance(); + + // Gestion des messages + switch (parsedMessage.flag) { + case 'Handshake': + await services.handleHandshakeMsg(urlReference, parsedMessage.content); + break; + case 'NewTx': + await services.parseNewTx(parsedMessage.content); + break; + case 'Cipher': + await services.parseCipher(parsedMessage.content); + break; + case 'Commit': + await services.handleCommitError(parsedMessage.content); + break; + // Ajoute d'autres cas si nécessaire + default: + // console.log('[WS] Message reçu:', parsedMessage.flag); + } + } catch (error) { + console.error('[WS] Erreur traitement message:', error); + } + })(); + } + }; + + ws.onerror = (event) => { + console.error('[WS] 💥 Erreur:', event); + // Pas besoin de reconnecter ici, onclose sera appelé juste après + }; + + ws.onclose = (event) => { + isConnecting = false; + stopHeartbeat(); + console.warn(`[WS] ⚠️ Déconnecté (Code: ${event.code}). Reconnexion dans ${reconnectInterval / 1000}s...`); + + // Reconnexion exponentielle (1s, 1.5s, 2.25s...) + setTimeout(() => { + connect(); + reconnectInterval = Math.min(reconnectInterval * 1.5, MAX_RECONNECT_INTERVAL); + }, reconnectInterval); + }; +} + +function startHeartbeat() { + stopHeartbeat(); + // Envoie un ping toutes les 30 secondes pour éviter que le serveur ou le navigateur ne coupe la connexion + pingIntervalId = setInterval(() => { + if (ws && ws.readyState === WebSocket.OPEN) { + // Adapter selon ce que ton serveur attend comme Ping, ou envoyer un message vide + // ws.send(JSON.stringify({ flag: 'Ping', content: '' })); + } + }, 30000); +} + +function stopHeartbeat() { + if (pingIntervalId) clearInterval(pingIntervalId); } -// Method to send messages export function sendMessage(flag: AnkFlag, message: string): void { - if (ws.readyState === WebSocket.OPEN) { + if (ws && ws.readyState === WebSocket.OPEN) { const networkMessage = { flag: flag, content: message, }; - console.log('Sending message of type:', flag); ws.send(JSON.stringify(networkMessage)); } else { - console.error('WebSocket is not open. ReadyState:', ws.readyState); - messageQueue.push(message); + console.warn(`[WS] Pas connecté. Message '${flag}' mis en file d'attente.`); + const networkMessage = { + flag: flag, + content: message, + }; + messageQueue.push(JSON.stringify(networkMessage)); + + // Si on n'est pas déjà en train de se connecter, on force une tentative + if (!isConnecting) connect(); } } export function getUrl(): string { - return ws.url; + return urlReference; } -// Method to close the WebSocket connection export function close(): void { - ws.close(); + if (ws) { + ws.onclose = null; // On évite la reconnexion auto si fermeture volontaire + stopHeartbeat(); + ws.close(); + } }