我在socket.io上拥有的每个事件处理程序都首先调用io.on连接。 例如-我进行了一次聊天,每次我发送一条消息(将其发送给所有客户端)时,都会调用io.on连接,然后才移至事件处理程序。
服务器文件
const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
let clients = 0;
io.on('connection', function (socket) {
clients++;
console.log('a user connected');
io.sockets.emit('User Connected', clients + ' clients connected');
socket.on('disconnect', function () {
clients--;
console.log('user disconnected');
io.sockets.emit('connected', clients + ' clients connected');
});
socket.on('msg sent', (msg) => {
console.log(msg, "new msg emit");
io.sockets.emit('msg sent', msg);
});
socket.on('user typing', (msg) => {
io.sockets.emit('user typing', msg);
});
});
http.listen(process.env.PORT || 8080, () => {
console.log('server started')
});
客户端聊天文件
import React, {Component} from 'react';
import socketIOClient from 'socket.io-client';
import Button from "react-bootstrap/Button";
class Chat extends Component {
constructor(props) {
super(props);
this.state = {endpoint: "localhost:8080", messages: [], newMsg: '', typing: '', connected: ''}
}
send = () => {
const socket = socketIOClient(this.state.endpoint);
socket.emit('msg sent', this.state.newMsg);
this.setState({typing: ''});
};
sentMsg = (event) => {
const socket = socketIOClient(this.state.endpoint);
socket.emit('user typing', 'User Typing');
this.setState({newMsg: event.target.value});
console.log(this.state.newMsg);
};
componentDidMount() {
const socket = socketIOClient(this.state.endpoint);
socket.on('msg sent', (msg) => {
console.log(msg, 'msg!!!');
this.setState({messages: [...this.state.messages, msg], typing: ''});
});
socket.on('user typing', (msg) => {
this.setState({typing: msg})
});
socket.on('User Connected', (msg) => {
this.setState({connected: msg});
})
};
renderMsg = () => {
if (this.state.messages.length > 0) {
return this.state.messages.map((msg) => {
return (<div className="border border-primary rounded m-2 p-1">{msg}</div>)
})
}
};
render() {
return (
<div className="container-fluid">
<div className="row chat p-2">
<div className="col-2 border-primary border m-1">
{this.state.connected}
</div>
<div className="col-9 border-danger border m-1 ">
{this.renderMsg()}
<br/>
{this.state.typing}
</div>
</div>
<div className="row">
<div className="fixed-bottom text-center">
<input type="text" className="col-6" onChange={this.sentMsg}/>
<Button className="m-1" onClick={() => {
this.send()
}}>Send</Button>
</div>
</div>
</div>
)
}
};
export default Chat;
希望只有在建立新连接时,连接的客户端计数器才会更新并添加1,但是它会在我发送给它的每个事件中都这样做。
答案 0 :(得分:1)
每次添加一条消息时,您的react组件都会重新呈现,这将触发componentDidMount并创建新的套接字。
考虑使用以下方法优化组件:
答案 1 :(得分:1)
最近我们遇到了同样的问题,每次客户端更改状态时,都会创建与ws服务器的新连接,从而导致多个websocket连接。 我们所做的是@Chris Chen的答案中的第三名
我们使用Web-worker进行socket.io ws连接,但是您只能以简单的反应来实现它。这个想法毕竟是一样的。
function WrapContainer(props) {
const { provider, pairString } = props.state;
useEffect(() => {
const worker = new Worker('../../workers/blox_worker.js', {
type: 'module',
});
worker.postMessage({
marketPair: props.state.pairString,
provider: props.state.provider,
});
// Handling of websockets data here which basically event listener from worker
//...
// ..
return () => {
worker.terminate();
};
}, []);
}
您可以用socket.disconnect()替换worker.terminate;
代码中的return
部分意为componentWillUnmount
。
这将确保在销毁组件时,websocket也将被杀死。如果您不杀死websocket,则使用websocket导航回该组件时,它也会创建另一个连接。
因此,如果要与每个用户建立一致的1个websocket连接,添加此功能至关重要。
编辑:
在进一步研究代码时,似乎您每次发送消息时都在重新创建socket.io客户端。客户端应该只是一个实例,因为每次您调用socketIOClient
新的ws连接时,也会创建一个新的ws连接,并且也应该对该实例进行发送。对于您的方案,更有效的用例是具有一个变量,该变量将保存socketio客户端。
import React, {Component} from 'react';
import socketIOClient from 'socket.io-client';
import Button from "react-bootstrap/Button";
class Chat extends Component {
constructor(props) {
super(props);
this.state = {endpoint: "localhost:8080", messages: [], newMsg: '', typing: '', connected: ''}
}
send = () => {
this.socket.emit('msg sent', this.state.newMsg);
this.setState({typing: ''});
}
sentMsg = (event) => {
this.socket.emit('user typing', 'User Typing');
this.setState({newMsg: event.target.value});
console.log(this.state.newMsg);
}
componentDidMount() {
this.socket = socketIOClient(this.state.endpoint);
this.socket.on('msg sent', (msg) => {
console.log(msg, 'msg!!!');
this.setState({messages: [...this.state.messages, msg], typing: ''});
});
this.socket.on('user typing', (msg) => {
this.setState({typing: msg})
});
this.socket.on('User Connected', (msg) => {
this.setState({connected: msg});
})
}
renderMsg = () => {
if (this.state.messages.length > 0) {
return this.state.messages.map((msg) => {
return (<div className="border border-primary rounded m-2 p-1">{msg}</div>);
})
}
};
render() {
return (
<div className="container-fluid">
<div className="row chat p-2">
<div className="col-2 border-primary border m-1">
{this.state.connected}
</div>
<div className="col-9 border-danger border m-1 ">
{this.renderMsg()}
<br/>
{this.state.typing}
</div>
</div>
<div className="row">
<div className="fixed-bottom text-center">
<input type="text" className="col-6" onChange={this.sentMsg}/>
<Button className="m-1" onClick={() => {
this.send()
}}>Send</Button>
</div>
</div>
</div>
)
}
};