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

Node.js TCP服务端密码认证逻辑重复触发问题排查求助

问题排查与修复方案

重复校验问题根因

  • 同一socket重复绑定data事件:userAuth函数内为socket绑定了密码校验用的data事件监听器,认证通过后newConnectionHandler又为同一socket绑定了业务数据处理的data事件监听器。Node.js 事件监听器是叠加生效的,后续客户端发送的所有数据会同时触发两个监听器,密码校验逻辑会把业务数据当作密码二次校验,匹配失败就销毁连接。
  • 连接逻辑仅执行一次:connection事件仅在客户端首次建连时触发一次,已认证列表判断逻辑仅在建连瞬间执行,后续该连接上的所有数据传输不会再触发该判断,原有依赖该逻辑拦截未认证请求的设计无法生效。
  • 列表操作逻辑不匹配:sock数组存储的是ip:port格式的字符串,close事件中查找下标时使用对象属性匹配,永远无法命中对应记录,客户端下线后已认证记录不会被清除。
  • 全局事件重复绑定:server.on('error', errorHandler)定义在connection回调内部,每次新客户端建连都会重复绑定错误事件,触发错误时会重复执行回调。

服务端回发数据说明

不需要调用server.createConnection相关逻辑,该API用于创建TCP客户端主动连接其他服务。你直接使用connection事件回调中拿到的socket对象,调用socket.write(要发送的内容)即可向当前连接的客户端回发数据,原有代码中 socket.write('Welcome \n')就是正确的实现方式。

修复方案

调整要点

  1. 将密码校验的data事件回调改为具名函数,认证通过后主动移除该监听器,避免重复触发
  2. 密码校验时对输入内容做trim处理,避免换行符、空白字符导致的校验失败
  3. 将全局错误事件绑定移到connection回调外部
  4. 修正sock数组的匹配逻辑,确保客户端下线时能正确清除已认证记录

修复后完整代码

const net = require('net'); 
const port = 3001; 
const host = '192.168.0.165'; 
const server = net.createServer()

let authenticatedClients = []; // 重命名更语义化,存储已认证的客户端地址字符串
const validPassword = '123'; // 改为字符串类型,避免类型隐式转换问题

// 全局错误事件移到外部绑定
server.on('error', errorHandler);

server.on('connection', (socket) => {
    const clientAddress = `${socket.remoteAddress}:${socket.remotePort}`;
    console.log(`新客户端接入:${clientAddress}`);
    
    if(authenticatedClients.includes(clientAddress)){
        console.log('客户端已认证,进入通信逻辑')
        initCommunicationHandler(socket, clientAddress)
    } else {
        console.log('客户端未认证,启动密码校验')
        handleAuth(socket, clientAddress)
    }

    function handleAuth(socket, clientAddress){
        socket.write('password : ' )
        // 具名函数定义校验回调,方便后续移除
        const authHandler = (data) => {
            const inputPassword = data.toString().trim();
            if (inputPassword === validPassword) {
                // 认证成功,移除密码校验监听器
                socket.off('data', authHandler);
                authenticatedClients.push(clientAddress);
                console.log(`客户端${clientAddress}认证通过,已认证列表:`, authenticatedClients)
                initCommunicationHandler(socket, clientAddress)
            } else {
                socket.write('Sorry, you cannot access the server \n')
                console.log(`客户端${clientAddress}认证失败,断开连接`)
                socket.write('connection closed')
                socket.destroy()
            }
        }
        socket.on('data', authHandler)
    }

    function initCommunicationHandler(socket, clientAddress){
        socket.write('Welcome \n')
        // 业务数据处理
        socket.on('data', (data) => {
            const content = data.toString().trim();
            console.log(`收到客户端${clientAddress}发送的业务数据:${content}`);
            // 示例:回发响应给客户端
            socket.write(`Server received: ${content}\n`);
        });
        // 连接关闭处理
        socket.once('close', () => { 
            // 修正匹配逻辑,直接匹配地址字符串
            const index = authenticatedClients.findIndex(item => item === clientAddress);
            if (index !== -1) {
                authenticatedClients.splice(index, 1);
            }
            // 广播给其他已认证客户端(如果需要)
            authenticatedClients.forEach(address => {
                console.log(`广播下线通知给:${address}`)
                // 这里如果要广播需要存储所有已认证的socket对象,而不仅仅是地址,当前逻辑仅打印日志
            }); 
            console.log(`客户端${clientAddress}断开连接,当前已认证列表:`, authenticatedClients); 
        }); 
    }
})

function errorHandler(err){
    console.log(`服务端发生错误:${err.message}`);
}

server.listen(port, host, () => {
    console.log(`TCP Server is running on ${host}:${port}`);
});

内容的提问来源于stack exchange,提问作者Ucin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 10:06:04