SocketIO 识别断开的用户

Posted

技术标签:

【中文标题】SocketIO 识别断开的用户【英文标题】:SocketIO Identify what user disconnected 【发布时间】:2015-09-22 10:09:04 【问题描述】:

我正在制作一个使用 Express、Socket.io 和 Http 服务器的简单 Node.js 游戏。所有用户都存储在服务器上的多维对象中。这就是服务器端代码的工作方式:

var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.use(express.static(__dirname + '/'));

var playerList = ;

createPlayer = function(array,width,height,spdx,spdy,x,y,color,name,id) 
  var player = 
    width:width,
    height:height,
    spdx:spdx,
    spdy:spdy,
    x:x,
    y:y,
    wKeyDown:false,
    aKeyDown:false,
    sKeyDown:false,
    dKeyDown:false,
    color:color,
    name:name,
    id:id
  
  array[id] = player;


io.on('connection', function(socket) 
  socket.on('new player', function(id, name) 
    id = parseInt(id);
    if (!playerList[id]) 
      createPlayer(playerList,25,25,4,4,Math.round(Math.random() * 800),Math.round(Math.random() * 600),randomColor(),name,id);
    

    socket.on('pressW', function(id, keyDown) 
      playerList[id].wKeyDown = keyDown;
    );
    socket.on('pressA', function(id, keyDown) 
      playerList[id].aKeyDown = keyDown;
    );
    socket.on('pressS', function(id, keyDown) 
      playerList[id].sKeyDown = keyDown;
    );
    socket.on('pressD', function(id, keyDown) 
      playerList[id].dKeyDown = keyDown;
    );
  );

  socket.on('disconnect', function() 

  );
;

sendPlayerList = function() 
  //newPlayerList is used to prevent client from seeing other users IDs
  var newPlayerList = ;
  var count = 0;
  for (var q in playerList) 
    player = 
      x:playerList[q].x,
      y:playerList[q].y,
      width:playerList[q].width,
      height:playerList[q].height,
      color:playerList[q].color,
      name:playerList[q].name,
    
    newPlayerList[count] = player;
    count++;
  

  io.emit('edit playerlist', newPlayerList);


SPLInterval = setInterval(sendPlayerList, 1000);

这是连接的客户端代码:

var id;
$('#playbutton').click(function() 
  var name = document.getElementById('name').value;
  id = Math.floor(Date.now() * Math.random());
  socket.emit('new player', id, name);
);

在客户端,在更新循环中,当游戏想要告诉服务器您的输入时,它会像这样发出您的输入:

update = function() 
    ctx.clearRect(0,0,canvas.width,canvas.height);
    if (document.hasFocus()) 
      socket.emit('pressD', id, dKeyDown);
      socket.emit('pressS', id, sKeyDown);
      socket.emit('pressA', id, aKeyDown);
      socket.emit('pressW', id, wKeyDown);
    else
      socket.emit('pressD', id, false);
      socket.emit('pressS', id, false);
      socket.emit('pressA', id, false);
      socket.emit('pressW', id, false);
    
    clientUpdatePlayer();
    updatePlayers();
  


var updateInterval = setInterval(update, 31.25);

更新玩家的功能只是根据服务器发送的玩家列表来绘制玩家。

我的问题是,当用户断开连接时,他们会留在播放器列表中。 我不明白我应该如何解决这个问题。我通过获取他们从客户端发送的 ID 来识别用户,但是当他们断开连接时我无法获取用户的 ID。

还有很多代码,但我试图只包含我认为必要的代码。如果需要,我愿意包含更多代码。

【问题讨论】:

可能重复:***.com/questions/13443503/… 您可以使用上面链接中的事件向服务器发送带有玩家 ID 的断开连接事件,并在收到该事件时将其从列表中删除。 【参考方案1】:
var users = [];
socket.on('newUser', (username) => 
    users.push(
        id: socket.id,
        username: username
    );
);
socket.on('disconnect', () => 
     const presentUser = users.find(user => user.id == socket.id);
     users = users.filter(user => user != presentUser);
);

【讨论】:

请提供更多信息以获取更多详细信息。 使用“socket.id”查找已断开连接的用户 const presentUser = users.find(user => user.id == socket.id);然后使用 users = users.filter(user => user != presentUser); 从在线用户列表中删除此用户 希望你对javascript中的“箭头函数”和“查找”和“过滤”数组方法没问题。 const presentUser = users.find(user => user.id == socket.id); 这里的socket.id是指触发disconnect事件的socket或者客户端或者已经断开的客户端。【参考方案2】:

这对我有用:

在每个新连接或上线的用户上生成一个socket Id,将其添加到用户对象中,并将其添加到所有在线用户的数组中。

const users = [];
io.on('connection', (socket) => 
const socketId = socket.id; 


socket.on('user online', (data) => 

    users.push( ...data, socketId );
    io.emit('view user online', user);
  );

然后在disconnect中,使用forEach循环遍历数组中的每个对象,然后使用for循环遍历对象中的每个键,delete

 socket.on('disconnect', () => 
    users.forEach((user) => 
      if (user.socketId === socket.id) 
        for (const key in user) 
          delete user[key];
        
      
        );

        logger(`A user has disconnected`);
      );
      );
);

调整为你想要的方式。

【讨论】:

【参考方案3】:

我们可以使用 socket id 来存储数据作为 playerList 中的引用。每当用户断开连接时,您都可以根据套接字 id 从对象中删除元素

var playerList = ;
io.on("connection", socket => 

if (!Object.values(playerList).includes(playername) && playername != null) 
      var U_data = 
        [socket.id]: playername
      ;
      playerList =  ...playerList, ...U_data ;
    
  socket.on("disconnect", function(e, id) 
    console.log(socket.id);
    delete playerList[socket.id];
    io.emit("broadcast", Object.values(playerList));

  );

【讨论】:

【参考方案4】:

也许我参加聚会迟到了,但我遇到了类似的事情,发现很难,这可能会对某人有所帮助。

检测用户是否断开连接的最佳方法是首先在套接字会话中设置用户名。

在发射时从客户端发送名称

socket.emit("newUser", username);

在服务器上

socket.on('newUser',function (username) 
     // we store the username in the socket session for this client
     socket.username = username;
);

当用户断开连接时,在断开连接事件中发现

socket.on('disconnect', function () 
    var connectionMessage = socket.username + " Disconnected from Socket " + socket.id;
    console.log(connectionMessage);
  );

你可以从那里拿走它。

【讨论】:

点赞!即使 mscdex 发布的答案可行,我认为将用户名存储在套接字对象上并能够随时从那里获取它感觉更加可靠和灵活。【参考方案5】:

您可以将 id 值存储在父范围中,disconnect 事件处理程序可以访问:

io.on('connection', function(socket) 
  var userId;
  socket.on('new player', function(id, name) 
    userId = id = parseInt(id);
    // ...
  );

  socket.on('disconnect', function() 
    delete playerList[userId];
  );
;

【讨论】:

这适用于多个用户吗?那不就是将 userId 变量更改为最近连接的玩家吗? 是的,它适用于多个用户。您已经从 connection 事件处理程序中获得了正确的范围,因此它是同一个套接字,因此是同一个用户。因此,除非您拥有这样一个用户可以在同一个套接字上同时注册多个玩家名称,否则您应该没问题。 为什么它是正确的答案? id=1 的用户 A 连接到服务器并且 userId 现在是 1 之后 id=2 的用户 B 在 userA 和 userId 是 2 之后连接到服务器!现在如果 userA 断开连接回调将从 playerList 中删除 userB! 我认为 userId 会有最近加入的用户的 id。

以上是关于SocketIO 识别断开的用户的主要内容,如果未能解决你的问题,请参考以下文章

使用烧瓶socketio经常断开套接字连接

socket.io、netty-socketio、nginx 每分钟断开连接

如何在nodejs,socketio中检查在线用户?

微信小程序weapp.socketio不稳定,断开不触发断开事件问题

Android Nkzawa SocketIO 断开连接并建立新连接

socket io:如何传递参数来断开套接字事件?