SocketIO 如何识别用户断开连接

10

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

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。

还有很多代码,但我尽量只包含我认为必要的代码。如果需要的话,我可以提供更多代码。


可能重复:https://dev59.com/BmYr5IYBdhLWcg3wuciw - TJC
您可以使用上面链接中的事件向服务器发送带有玩家ID的断开连接事件,并在接收到该事件时将其从列表中删除。 - TJC
5个回答

13

你可以将id的值存储在父级范围中,这样disconnect事件处理程序就可以访问它:


你可以只在父级作用域中存储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];
  });
};

2
这对于多个用户有效吗?那不会只是将userId变量更改为最近连接的玩家吗? - Weastie
是的,它可以适用于多个用户。您已经从“连接”事件处理程序中获得了正确的作用域,因此它是相同的套接字,因此是相同的用户。因此,除非您设置一个用户可以在同一套接字上同时注册多个玩家名称,否则应该没问题。 - mscdex
3
为什么这是正确答案?用户A(ID为1)连接到服务器,并且现在的用户ID是1。之后,用户B(ID为2)在用户A之后连接到服务器,并且现在的用户ID是2。如果用户A断开连接,此回调函数将从playerList中删除用户B! - Mehdi Akbarian Rastaghi
我认为userId将拥有最近加入的用户的ID。 - Vikneshwar

8
也许我来晚了,但我曾遇到类似情况并且通过艰辛的方式找到了解决方法,希望这对某些人有所帮助。
检测用户是否断开连接的最佳方法是首先在socket会话中设置用户名。
从客户端发送名称进行发射。
socket.emit("newUser", username);

在服务器端

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

并且当用户断开连接时,在 "disconnect" 事件中找到它。
socket.on('disconnect', function () {
    var connectionMessage = socket.username + " Disconnected from Socket " + socket.id;
    console.log(connectionMessage);
  });

并且你可以从那里开始。

点赞!即使mscdex发布的答案可行,我认为将用户名存储在socket对象上并随时从那里获取它会更可靠和灵活。 - Drkawashima

3
这对我有用:
每次有新的连接或用户上线时,生成一个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);
  });

然后在断开连接中,使用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`);
      });
      });
});

随心所欲地调整。

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);
});

请提供更多信息以获取进一步的详细信息。 - Farbod Ahmadian
使用“socket.id”查找已断开连接的用户: const presentUser = users.find(user => user.id == socket.id);然后使用以下代码从在线用户列表中删除此用户: users = users.filter(user => user != presentUser); - Anand Kumar Pandey
希望你对JavaScript中的“箭头函数”、“find”和“filter”数组方法感到满意。 - Anand Kumar Pandey
const presentUser = users.find(user => user.id == socket.id);这里的 socket.id 指的是触发了 disconnect 事件或已断开连接的客户端。 - Anand Kumar Pandey

0
我们可以使用套接字 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));

  });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接