feat(websocket): add ping/pong mechanism to maintain connection

- Add proxy timeouts in nginx.conf to handle long connections
- Implement ping/pong handling in server.js WebSocket listener
- Refactor App.js to class component with lifecycle methods and ping interval for WebSocket keep-alive
This commit is contained in:
sebseb7
2025-09-02 08:45:18 +00:00
parent 9cfbe4c0f7
commit a8ca291555
3 changed files with 58 additions and 27 deletions

View File

@@ -21,4 +21,6 @@
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
proxy_send_timeout 86400;
}

View File

@@ -33,7 +33,13 @@ wss.on('connection', (ws) => {
console.log('Client connected');
ws.on('message', (message) => {
console.log('Received:', Buffer.from(message).toString());
const msg = Buffer.from(message).toString();
if (msg === '{"type":"ping"}') {
// Respond to ping to keep connection alive
ws.send(JSON.stringify({ type: 'pong' }));
} else {
console.log('Received:', msg);
}
});
ws.on('error', (error) => {

View File

@@ -1,20 +1,26 @@
import React, { useState, useEffect } from 'react';
import React from 'react';
function App() {
const [counter, setCounter] = useState(0);
const [ws, setWs] = useState(null);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
ws: null,
};
this.pingInterval = null;
}
useEffect(() => {
componentDidMount() {
// Fetch initial counter value
fetch('/api/counter')
.then(response => response.json())
.then(data => setCounter(data.value))
.then(data => this.setState({ counter: data.value }))
.catch(error => console.error('Error fetching counter:', error));
// Connect to WebSocket - use dynamic URL based on current location
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const websocket = new WebSocket(`${protocol}//${window.location.host}/counter-ws`);
setWs(websocket);
this.setState({ ws: websocket });
websocket.onopen = () => {
console.log('WebSocket connected');
@@ -27,7 +33,10 @@ function App() {
websocket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'counter-update') {
setCounter(data.value);
this.setState({ counter: data.value });
} else if (data.type === 'pong') {
// Pong received, connection is alive
console.log('Pong received');
}
};
@@ -35,36 +44,50 @@ function App() {
console.log('WebSocket disconnected', 'Code:', event.code, 'Reason:', event.reason);
};
return () => {
websocket.close();
};
}, []);
// Send ping every 30 seconds to keep connection alive
this.pingInterval = setInterval(() => {
if (websocket.readyState === WebSocket.OPEN) {
websocket.send(JSON.stringify({ type: 'ping' }));
}
}, 30000);
}
const incrementCounter = () => {
componentWillUnmount() {
if (this.pingInterval) {
clearInterval(this.pingInterval);
}
if (this.state.ws) {
this.state.ws.close();
}
}
incrementCounter = () => {
fetch('/api/counter/increment', {
method: 'POST',
})
.then(response => response.json())
.then(data => {
setCounter(data.value);
this.setState({ counter: data.value });
// Broadcast update via WebSocket
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'counter-update', value: data.value }));
if (this.state.ws && this.state.ws.readyState === WebSocket.OPEN) {
this.state.ws.send(JSON.stringify({ type: 'counter-update', value: data.value }));
}
})
.catch(error => console.error('Error incrementing counter:', error));
};
return (
<div style={{ textAlign: 'center', marginTop: '50px' }}>
<h1>Shared Counter Demo</h1>
<h2>Counter: {counter}</h2>
<button onClick={incrementCounter} style={{ fontSize: '20px', padding: '10px 20px' }}>
Increment
</button>
<p>Open this page in multiple tabs to see real-time syncing!</p>
</div>
);
render() {
return (
<div style={{ textAlign: 'center', marginTop: '50px' }}>
<h1>Shared Counter Demo</h1>
<h2>Counter: {this.state.counter}</h2>
<button onClick={this.incrementCounter} style={{ fontSize: '20px', padding: '10px 20px' }}>
Increment
</button>
<p>Open this page in multiple tabs to see real-time syncing!</p>
</div>
);
}
}
export default App;