You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 09:18:02