NodeJS服务器无法向指定Java客户端发消息,重复登录强制下线异常
解决Node.js服务器无法向旧客户端发送强制下线通知的问题
看起来你遇到的核心问题是旧客户端(ClientA)收不到强制下线消息,而新客户端(ClientB)能正常收到登录成功通知。我帮你拆解下代码里的问题,然后给出具体的修复方案:
问题根源分析
你的现有代码存在几个关键漏洞:
未正确获取并设置登录用户名
代码里直接使用了requestedUsername但从未定义它,而且旧连接的currentUsername字段从未被赋值——这导致你遍历connectionArray时根本找不到重复登录的旧连接,自然无法给它发消息。踢人逻辑的时机错误
你在调用loginService.check()(异步Promise)之前就遍历了connectionArray,但此时新连接还没被加入数组,更重要的是:只有当登录验证成功后,才需要处理重复登录的踢人逻辑,否则如果登录失败,根本不需要触发任何踢人操作。客户端未持续监听服务器消息
你的Java客户端在登录时只读取了一次服务器响应,但服务器后续发送的强制下线消息,客户端并没有持续监听接收,所以即使服务器发了消息,ClientA也收不到。
修正后的Node.js服务器代码
var net = require('net') var connectionArray = [] var server = net.createServer(function (connection) { // 处理连接断开事件,清理无效连接 connection.on('end', () => { const index = connectionArray.indexOf(connection); if (index !== -1) { connectionArray.splice(index, 1); } console.log(`Connection closed for user: ${connection.currentUsername || 'unknown'}`); }); connection.on('data', function (data) { // 去除首尾空白字符,避免命令拆分出错 let commandArray = data.toString().trim().split(" ") if (commandArray[0] === "login") { // 正确获取用户名和密码 const requestedUsername = commandArray[1]; const requestedPassword = commandArray[2]; // 先验证登录信息,成功后再处理重复登录 loginService.check(requestedUsername, requestedPassword).then((response) => { // 1. 查找是否存在相同用户名的已登录连接 let existingConnection = null; for (let conn of connectionArray) { if (conn.currentUsername === requestedUsername) { existingConnection = conn; break; } } // 2. 如果有旧连接,发送强制下线通知并移除 if (existingConnection) { existingConnection.write("You have been forced to log out: duplicate login detected!\n"); // 移除旧连接,避免后续干扰 const index = connectionArray.indexOf(existingConnection); if (index !== -1) { connectionArray.splice(index, 1); } // 可选:直接关闭旧连接,确保客户端感知到断开 existingConnection.end(); } // 3. 设置新连接的用户名并加入数组 connection.currentUsername = requestedUsername; connectionArray.push(connection); // 4. 通知新客户端登录成功 connection.write("Login successful!\n"); }, (rejected) => { connection.write(`Login failed: ${rejected.message}\n`); }) } }) }) // 记得启动服务器,比如监听3000端口 server.listen(3000, () => { console.log('Server listening on port 3000'); });
修正后的Java客户端代码
关键是让客户端持续监听服务器的消息,而不是只在登录时读取一次:
ClientThread.java
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class ClientThread extends Thread { @Override public void run() { try (Socket socket = new Socket("localhost", 3000); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) { // 单独开线程处理用户输入,避免阻塞消息读取 Thread inputHandler = new Thread(() -> { Scanner scanner = new Scanner(System.in); while (true) { String command = scanner.nextLine(); out.println(command); } }); inputHandler.start(); // 持续读取服务器发送的所有消息 String serverMessage; while ((serverMessage = reader.readLine()) != null) { System.out.println("\nServer notification: " + serverMessage); } } catch (IOException e) { System.out.println("Connection to server lost."); } } }
Main.java
import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { ClientThread clientThread = new ClientThread(); clientThread.start(); } }
核心修复点总结
- 正确关联连接与用户名:登录成功后给
connection对象设置currentUsername字段,确保后续能通过用户名找到对应连接。 - 调整踢人逻辑时机:在登录验证成功后再检查重复登录,避免无效操作。
- 清理无效连接:监听连接断开事件,从
connectionArray中移除失效连接,防止数组积累垃圾数据。 - 客户端持续监听:用独立线程处理用户输入,主线程持续读取服务器消息,确保能实时接收强制下线通知。
这样修改后,当ClientB用相同账号登录时,ClientA会立即收到强制下线的消息,同时ClientB成功登录,符合你的需求。
内容的提问来源于stack exchange,提问作者AcidMammoth
相关产品推荐
相关产品推荐

