基于Android+Node.js+MySQL实现一对一聊天的WebSocket定向发消息问题
Node.js WebSocket 一对一聊天定向发消息问题解决方案
现有代码已知问题
- 历史消息查询后调用
wsServer.broadcast()触发全量广播,没有只发给当前新连接的客户端 - 查询历史消息的SQL参数
userid、receiver_id未声明,直接使用会抛出未定义错误 - 接收客户端消息后,使用未定义的
message变量解析,导致逻辑报错,定向发送不生效 - 保留了广播逻辑,每次发消息会推给所有非当前连接的客户端,不符合一对一需求
- 未判断接收方连接是否存在,接收方不在线时调用
send()会抛出空指针错误
具体问题解决方案
1. 仅给刚登录的客户端发送历史消息
将历史消息查询回调中的wsServer.broadcast(result)替换为当前连接的send方法即可,同时修正参数声明问题:
wsServer.on('request', (req) => { const connection = req.accept() // 从请求参数提取用户ID,同时赋值给本地变量供SQL查询使用 const userid = connection.userid = req.resourceURL.query.email_sender_id const receiver_id = connection.receiver_id = req.resourceURL.query.email_receiver_id var sql = "SELECT * FROM messages WHERE((email_sender_id=? AND email_receiver_id=?) OR (email_sender_id=? AND email_receiver_id=?))" const values = [userid,receiver_id,receiver_id,userid] connection_sql.query(sql,values, function (err, result) { if (err) throw err; console.log("We got all user messages"); const resultStr = JSON.stringify(result) // 只发给当前新连接的客户端,不要广播 connection.send(resultStr) }); connections.push(connection)
2. 给指定接收方发消息、不在线处理逻辑
接收消息后去掉广播逻辑,增加接收方连接存在性判断即可,接收方不在线时无需额外处理,消息已经落库,下次对方上线拉历史消息即可获取:
connection.on('message', (mes) => { const message_details = JSON.parse(mes.utf8Data) const email_sender_id = message_details.email_sender_id const email_receiver_id = message_details.email_receiver_id const message_data = message_details.message_data const date = new Date().toISOString().slice(0, 19).replace('T', ' '); const sql = "INSERT INTO messages(email_sender_id,email_receiver_id,message_data,sent_time) VALUES (?,?,?,?)" const values = [email_sender_id,email_receiver_id,message_data,date] connection_sql.query(sql,values, function (err, result) { if (err) throw err; console.log("1 record inserted"); }); // 查找接收方的连接 let toUserWebSocket = null connections.forEach(element => { if(element.userid == email_receiver_id) { toUserWebSocket = element } }) // 只有接收方在线(连接存在)才发送消息 if(toUserWebSocket) { // 直接用已经解析的消息对象转字符串发送,不用重复解析 toUserWebSocket.send(JSON.stringify(message_details)) } // 删掉原来的广播逻辑,不需要给其他用户发消息 })
额外优化建议
- 可以把
connections数组改成对象映射,key为用户ID,value为连接对象,查找接收方连接时时间复杂度从O(n)降到O(1),性能更好:
// 原来的const connections = []改成 const userConnectionMap = {} // 新连接存入时 userConnectionMap[userid] = connection // 断开连接时删除 delete userConnectionMap[connection.userid] // 查找接收方连接时直接取 const toUserWebSocket = userConnectionMap[email_receiver_id]
- 注意mysql单连接在长时间运行后可能出现断连问题,建议换成连接池使用。
内容的提问来源于stack exchange,提问作者david
相关产品推荐
相关产品推荐

