第四章 用户上线 - nswbmw/N-chat GitHub Wiki

现在,我们来给聊天室添加用户上线提醒。
这时我们就要用到 socket.io 了,打开 app.js ,修改 http.createServer(app)server ,并在之前添加如下代码:

var server = http.createServer(app);
var io = require('socket.io').listen(server);
io.sockets.on('connection', function (socket) {
});

修改 views/index.html ,在 <script type="text/javascript" src="javascripts/jquery.cookie.js"></script> 下一行添加:

<script src="/socket.io/socket.io.js"></script>
<script type="text/javascript" src="javascripts/chat.js"></script>

在 public/javascripts 文件夹下新建 chat.js ,内容如下:

$(document).ready(function() {
  var socket = io.connect();
});

至此,我们就可以在 Express 中使用 socket.io 了。

接下来我们添加用户上线提醒,流程图如下:

对应代码修改如下:
打开 chat.js ,在 var socket = io.connect(); 下添加如下代码:

var from = $.cookie('user');//从 cookie 中读取用户名,存于变量 from
var to = 'all';//设置默认接收对象为"所有人"
//发送用户上线信号
socket.emit('online', {user: from});
socket.on('online', function (data) {
  //显示系统消息
  if (data.user != from) {
    var sys = '<div style="color:#f00">系统(' + now() + '):' + '用户 ' + data.user + ' 上线了!</div>';
  } else {
    var sys = '<div style="color:#f00">系统(' + now() + '):你进入了聊天室!</div>';
  }
  $("#contents").append(sys + "<br/>");
  //刷新用户在线列表
  flushUsers(data.users);
  //显示正在对谁说话
  showSayTo();
});

接下来我们完成刷新用户在线列表的函数 flushUsers() ,添加如下代码:

//刷新用户在线列表
function flushUsers(users) {
  //清空之前用户列表,添加 "所有人" 选项并默认为灰色选中效果
  $("#list").empty().append('<li title="双击聊天" alt="all" class="sayingto" onselectstart="return false">所有人</li>');
  //遍历生成用户在线列表
  for (var i in users) {
    $("#list").append('<li alt="' + users[i] + '" title="双击聊天" onselectstart="return false">' + users[i] + '</li>');
  }
  //双击对某人聊天
  $("#list > li").dblclick(function() {
    //如果不是双击的自己的名字
    if ($(this).attr('alt') != from) {
      //设置被双击的用户为说话对象
      to = $(this).attr('alt');
      //清除之前的选中效果
      $("#list > li").removeClass('sayingto');
      //给被双击的用户添加选中效果
      $(this).addClass('sayingto');
      //刷新正在对谁说话
      showSayTo();
    }
  });
}

添加显示正在对谁说话的函数 showSayTo() ,代码如下:

//显示正在对谁说话
function showSayTo() {
  $("#from").html(from);
  $("#to").html(to == "all" ? "所有人" : to);
}

添加获取当前时间的函数 now() ,代码如下:

//获取当前时间
function now() {
  var date = new Date();
  var time = date.getFullYear() + '-' + (date.getMonth() + 1) + '-' + date.getDate() + ' ' + date.getHours() + ':' + (date.getMinutes() < 10 ? ('0' + date.getMinutes()) : date.getMinutes()) + ":" + (date.getSeconds() < 10 ? ('0' + date.getSeconds()) : date.getSeconds());
  return time;
}

最后,修改 app.js ,在 io.sockets.on('connection') 里添加如下代码:

//有人上线
socket.on('online', function (data) {
  //将上线的用户名存储为 socket 对象的属性,以区分每个 socket 对象,方便后面使用
  socket.name = data.user;
  //users 对象中不存在该用户名则插入该用户名
  if (!users[data.user]) {
    users[data.user] = data.user;
  }
  //向所有用户广播该用户上线信息
  io.sockets.emit('online', {users: users, user: data.user});
});

注意:我们把存储用户名的操作放到用户上线的事件里(即 socket.on('online') 里),而不是用户登录时把直接用户名存储到数组。这是因为 “用户上线添加用户名,用户下线删除用户名” 这是一个对称的操作。假如用户登录时把用户名存储到数组,用户下线时从数组中删除用户名,那么当用户刷新聊天室页面或者关闭然后重新打开页面时(都会触发一个下线并上线的动作),users 数组中就会只删除而不再重新添加该用户,即右侧的 “在线用户” 列表也不会显示该用户名了。

至此,我们完成了聊天室用户上线提醒。运行你的 app ,打开两个浏览器试试吧!

⚠️ **GitHub.com Fallback** ⚠️