fix(socket): improve connection stability with heartbeat and auto-reconnect

- Backend: Add pingInterval (25s) and pingTimeout (60s) for better keep-alive
- Frontend: Increase reconnection attempts to Infinity (keep trying forever)
- Frontend: Add reconnect event handlers to rejoin rooms after reconnection
- Frontend: Check initial connection state when reusing socket instance
This commit is contained in:
Radosław Gierwiało
2025-11-21 21:53:51 +01:00
parent 78280ca8d8
commit 8a369c1fc4
4 changed files with 60 additions and 9 deletions

View File

@@ -21,6 +21,12 @@ function initializeSocket(httpServer) {
origin: process.env.CORS_ORIGIN || 'http://localhost:8080', origin: process.env.CORS_ORIGIN || 'http://localhost:8080',
credentials: true, credentials: true,
}, },
// Ping/pong heartbeat configuration
pingInterval: 25000, // Send ping every 25 seconds
pingTimeout: 60000, // Wait 60 seconds for pong before considering disconnected
// Allow upgrade from polling to websocket
transports: ['polling', 'websocket'],
allowUpgrades: true,
}); });
// Authentication middleware for Socket.IO // Authentication middleware for Socket.IO

View File

@@ -46,15 +46,37 @@ const useEventChat = (slug, userId, event, messagesContainerRef) => {
return; return;
} }
// Socket event listeners // Function to join room (used on connect and reconnect)
socket.on('connect', () => { const joinRoom = () => {
setIsConnected(true); setIsConnected(true);
// Join event room
socket.emit('join_event_room', { slug }); socket.emit('join_event_room', { slug });
};
// Check if already connected (socket instance may already be connected)
if (socket.connected) {
joinRoom();
}
// Socket event listeners
socket.on('connect', joinRoom);
socket.on('disconnect', (reason) => {
setIsConnected(false);
console.log('🔌 Disconnected:', reason);
}); });
socket.on('disconnect', () => { // Handle reconnection - rejoin room automatically
setIsConnected(false); socket.on('reconnect', (attemptNumber) => {
console.log('🔄 Reconnected after', attemptNumber, 'attempts');
// Room will be joined via 'connect' event
});
socket.on('reconnect_attempt', (attemptNumber) => {
console.log('🔄 Reconnection attempt', attemptNumber);
});
socket.on('reconnect_error', (error) => {
console.error('🔄 Reconnection error:', error);
}); });
// Receive message history (initial 20 messages) // Receive message history (initial 20 messages)
@@ -92,8 +114,11 @@ const useEventChat = (slug, userId, event, messagesContainerRef) => {
// Cleanup // Cleanup
return () => { return () => {
socket.emit('leave_event_room'); socket.emit('leave_event_room');
socket.off('connect'); socket.off('connect', joinRoom);
socket.off('disconnect'); socket.off('disconnect');
socket.off('reconnect');
socket.off('reconnect_attempt');
socket.off('reconnect_error');
socket.off('message_history'); socket.off('message_history');
socket.off('event_message'); socket.off('event_message');
socket.off('active_users'); socket.off('active_users');

View File

@@ -64,8 +64,18 @@ const useMatchChat = (match, userId, slug) => {
// Socket event listeners // Socket event listeners
socket.on('connect', joinMatchRoom); socket.on('connect', joinMatchRoom);
socket.on('disconnect', () => { socket.on('disconnect', (reason) => {
setIsConnected(false); setIsConnected(false);
console.log('🔌 Match chat disconnected:', reason);
});
// Handle reconnection
socket.on('reconnect', (attemptNumber) => {
console.log('🔄 Match chat reconnected after', attemptNumber, 'attempts');
});
socket.on('reconnect_attempt', (attemptNumber) => {
console.log('🔄 Match chat reconnection attempt', attemptNumber);
}); });
// Receive messages // Receive messages
@@ -82,6 +92,8 @@ const useMatchChat = (match, userId, slug) => {
return () => { return () => {
socket.off('connect', joinMatchRoom); socket.off('connect', joinMatchRoom);
socket.off('disconnect'); socket.off('disconnect');
socket.off('reconnect');
socket.off('reconnect_attempt');
socket.off('match_message'); socket.off('match_message');
}; };
}, [match, userId]); }, [match, userId]);

View File

@@ -26,9 +26,17 @@ export function connectSocket() {
auth: { auth: {
token, token,
}, },
// Reconnection settings
reconnection: true, reconnection: true,
reconnectionAttempts: 5, reconnectionAttempts: Infinity, // Keep trying forever
reconnectionDelay: 1000, reconnectionDelay: 1000, // Start with 1 second delay
reconnectionDelayMax: 5000, // Max 5 seconds between attempts
randomizationFactor: 0.5, // Add some randomness to prevent thundering herd
// Timeout settings
timeout: 20000, // 20 seconds connection timeout
// Transport settings - prefer websocket but start with polling for reliability
transports: ['polling', 'websocket'],
upgrade: true,
}); });
socket.on('connect', () => { socket.on('connect', () => {