基于socket.io+express实现多房间聊天


Posted in Javascript onMarch 17, 2016

socket.io简介

Socket.IO是一个开源的WebSocket库,它通过Node.js实现WebSocket服务端,同时也提供客户端JS库。Socket.IO支持以事件为基础的实时双向通讯,它可以工作在任何平台、浏览器或移动设备。

Socket.IO支持4种协议:WebSocket、htmlfile、xhr-polling、jsonp-polling,它会自动根据浏览器选择适合的通讯方式,从而让开发者可以聚焦到功能的实现而不是平台的兼容性,同时Socket.IO具有不错的稳定性和性能。

多房间聊天

socket.io提供rooms和namespace的API

用rooms的API就可以实现多房间聊天了,总结出来无外乎就是:join/leave room 和 say to room

// join和leave
io.on('connection', function(socket){
 socket.join('some room');
 // socket.leave('some room');
});

// say to room
io.to('some room').emit('some event'):
io.in('some room').emit('some event'):

代码 github
新建文件夹chatapp-demo
chatapp-demo/package.json

{
 "name": "chatapp-demo",
 "version": "1.0.0",
 "description": "multi room chat app demo, powered by socket.io",
 "main": "app.js",
 "dependencies": {
  "express": "^4.13.3",
  "hbs": "^3.1.0",
  "path": "^0.11.14",
  "socket.io": "^1.3.6"
 },
 "devDependencies": {},
 "author": "wuyanxin",
 "license": "ISC"
}

执行 npm install

服务端代码

增加文件 chatapp-demo/app.js

var express = require('express');
var path = require('path');
var IO = require('socket.io');
var router = express.Router();

var app = express();
var server = require('http').Server(app);
app.use(express.static(path.join(__dirname, 'public')));
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');

// 创建socket服务
var socketIO = IO(server);
// 房间用户名单
var roomInfo = {};

socketIO.on('connection', function (socket) {
 // 获取请求建立socket连接的url
 // 如: http://localhost:3000/room/room_1, roomID为room_1
 var url = socket.request.headers.referer;
 var splited = url.split('/');
 var roomID = splited[splited.length - 1];  // 获取房间ID
 var user = '';

 socket.on('join', function (userName) {
  user = userName;

  // 将用户昵称加入房间名单中
  if (!roomInfo[roomID]) {
   roomInfo[roomID] = [];
  }
  roomInfo[roomID].push(user);

  socket.join(roomID);  // 加入房间
  // 通知房间内人员
  socketIO.to(roomID).emit('sys', user + '加入了房间', roomInfo[roomID]); 
  console.log(user + '加入了' + roomID);
 });

 socket.on('leave', function () {
  socket.emit('disconnect');
 });

 socket.on('disconnect', function () {
  // 从房间名单中移除
  var index = roomInfo[roomID].indexOf(user);
  if (index !== -1) {
   roomInfo[roomID].splice(index, 1);
  }

  socket.leave(roomID);  // 退出房间
  socketIO.to(roomID).emit('sys', user + '退出了房间', roomInfo[roomID]);
  console.log(user + '退出了' + roomID);
 });

 // 接收用户消息,发送相应的房间
 socket.on('message', function (msg) {
  // 验证如果用户不在房间内则不给发送
  if (roomInfo[roomID].indexOf(user) === -1) { 
   return false;
  }
  socketIO.to(roomID).emit('msg', user, msg);
 });

});

// room page
router.get('/room/:roomID', function (req, res) {
 var roomID = req.params.roomID;

 // 渲染页面数据(见views/room.hbs)
 res.render('room', {
  roomID: roomID,
  users: roomInfo[roomID]
 });
});

app.use('/', router);

server.listen(3000, function () {
 console.log('server listening on port 3000');
});

客户端代码

新增chatapp/views/room.hbs

<!DOCTYPE html>
<html>
<head lang="en">
 <meta charset="UTF-8">
 <title>{{roomID}}</title>
 <style>
  #msglog, #messageInput {
   border: 1px solid #ccc;
   width: 500px;
   height: 350px;
   overflow-y: auto;
   font-size: 14px;
  }
  #messageInput {
   height: 80px;
  }
  .message {
   line-height: 22px;
  }
  .message .user {
   padding-right: 5px;
   padding-left: 5px;
   color: brown;
  }
  .sysMsg {
   color: #c1bfbf;
   padding-right: 5px;
   padding-left: 5px;
   font-size: 12px;
  }
  #users {
   width: 490px;
   padding: 0 5px 5px;
  }
 </style>
</head>
<body>
 昵称: <span id="userName"></span> <br/>
 房间: {{roomID}} <br/>
 当前在线人数: <span id="count">{{users.length}}</span> <br/>
 在线用户: <div id="users">{{users}}</div>

 <div id="msglog">

 </div>
 <textarea name="message" id="messageInput"></textarea>
 <br/>
 按Enter键发送
 <button id="joinOrLeave">退出房间</button>

 <script src="/socket.io/socket.io.js"></script>
 <script src="/js/jquery.js"></script>
 <script>
  $(function () {
   // ----------设置昵称-------------
   var userName = '';
   while ($('#userName').text().trim() === '') {
    userName = prompt("请设置你的昵称","");
    $('#userName').text(userName);
   }


   // ---------创建连接-----------
   var socket = io();

   // 加入房间
   socket.on('connect', function () {
    socket.emit('join', userName);
   });

   // 监听消息
   socket.on('msg', function (userName, msg) {
    var message = '' +
      '<div class="message">' +
      ' <span class="user">' + userName + ': </span>' +
      ' <span class="msg">' + msg + '</span>' +
      '</div>';
    $('#msglog').append(message);
    // 滚动条保持最下方
    $('#msglog').scrollTop($('#msglog')[0].scrollHeight); 
   });

   // 监听系统消息
   socket.on('sys', function (sysMsg, users) {
    var message = '<div class="sysMsg">' + sysMsg + '</div>';
    $('#msglog').append(message);

    $('#count').text(users.length);
    $('#users').text(users);
   });

   // 发送消息
   $('#messageInput').keydown(function (e) {
    if (e.which === 13) {
     e.preventDefault();
     var msg = $(this).val();
     $(this).val('');

     socket.send(msg);
    }
   });

   // 退出房间
   $('#joinOrLeave').click(function () {
    if ($(this).text() === '退出房间') {
     $(this).text('进入房间');
     socket.emit('leave');
     var msg = '你已经退出了房间,重新发言请点击"进入房间"';
     $('#msglog').append('<div class="sysMsg">'+msg+'</div>');
    } else {
     $(this).text('退出房间');
     socket.emit('join', userName);
    }

   });
  });
 </script>
</body>
</html>

新增 chatapp/public/index.html

<!DOCTYPE html>
<html>
<head lang="en">
 <meta charset="UTF-8">
 <title>demo</title>
</head>
<body>
 欢迎您,骚年

 <div>
  <p>房间列表</p>
  <ul>
   <li id="room_1"><a href="/room/room_1" target="_blank">1号房间</a></li>
   <li id="room_2"><a href="/room/room_2" target="_blank">2号房间</a></li>
   <li id="room_3"><a href="/room/room_3" target="_blank">3号房间</a></li>
   <li id="room_4"><a href="/room/room_4" target="_blank">4号房间</a></li>
   <li id="room_5"><a href="/room/room_5" target="_blank">5号房间</a></li>
   <li id="room_6"><a href="/room/room_6" target="_blank">6号房间</a></li>
   <li id="room_7"><a href="/room/room_7" target="_blank">7号房间</a></li>
   <li id="room_8"><a href="/room/room_8" target="_blank">8号房间</a></li>
   <li id="room_9"><a href="/room/room_9" target="_blank">9号房间</a></li>
   <li id="room_10"><a href="/room/room_10" target="_blank">10号房间</a></li>
  </ul>
 </div>

</body>
</html>

运行效果

基于socket.io+express实现多房间聊天

代码已放在github https://github.com/wuyanxin/chatapp-demo.git

Javascript 相关文章推荐
InnerHtml和InnerText的区别分析
Mar 13 Javascript
有趣的javascript数组定义方法
Sep 10 Javascript
基于jquery的文字向上跑动类似跑马灯的效果
Sep 22 Javascript
IE6 hack for js 集锦
Sep 23 Javascript
Javascript基础教程之break和continue语句
Jan 18 Javascript
javascript 应用小技巧方法汇总
Jul 05 Javascript
Javascript中apply、call、bind的巧妙使用
Aug 18 Javascript
使用jQuery ajaxupload插件实现无刷新上传文件
Apr 23 jQuery
Angular中响应式表单的三种更新值方法详析
Aug 22 Javascript
vue 系列——vue2-webpack2框架搭建踩坑之路
Dec 22 Javascript
angularJS实现不同视图同步刷新详解
Oct 09 Javascript
jQuery+Ajax+js实现请求json格式数据并渲染到html页面操作示例
Jun 02 jQuery
盘点javascript 正则表达式中 中括号的【坑】
Mar 16 #Javascript
教你用javascript实现随机标签云效果_附代码
Mar 16 #Javascript
浅析AMD CMD CommonJS规范--javascript模块化加载学习心得总结
Mar 16 #Javascript
js纯数字逐一停止显示效果的实现代码
Mar 16 #Javascript
神奇!js+CSS+DIV实现文字颜色渐变效果
Mar 16 #Javascript
js获取时间精确到秒(年月日)
Mar 16 #Javascript
js实现内容显示并使用json传输数据
Mar 16 #Javascript
You might like
深入for,while,foreach遍历时间比较的详解
2013/06/08 PHP
PHP统一页面编码避免乱码问题
2015/04/09 PHP
php如何控制用户对图片的访问 PHP禁止图片盗链
2016/03/25 PHP
如果文字过长,则将过长的部分变成省略号显示
2006/06/26 Javascript
js 键盘记录实现(兼容FireFox和IE)
2010/02/07 Javascript
JS 类型转换常见方法小结
2010/05/31 Javascript
cnblogs 代码高亮显示后的代码复制问题解决实现代码
2011/12/14 Javascript
JavaScript 函数replace深入了解
2013/03/14 Javascript
jquery submit ie6下失效的原因分析及解决方法
2013/11/15 Javascript
js判断当前浏览器类型,判断IE浏览器方法
2014/06/02 Javascript
js实现绿白相间竖向网页百叶窗动画切换效果
2015/03/02 Javascript
jQuery验证插件validation使用指南
2015/04/21 Javascript
AngularJS实现表单元素值绑定操作示例
2017/10/11 Javascript
JS实现的简单表单验证功能示例
2017/10/13 Javascript
Vue中的Vux配置指南
2017/12/08 Javascript
JavaScript选择排序算法原理与实现方法示例
2018/08/06 Javascript
react基本安装与测试示例
2020/04/27 Javascript
layui使用及简单的三级联动实现教程
2020/12/01 Javascript
在Python的Django框架中创建和使用模版
2015/07/15 Python
Python中列表和元组的相关语句和方法讲解
2015/08/20 Python
浅谈Python中重载isinstance继承关系的问题
2018/05/04 Python
django框架自定义用户表操作示例
2018/08/07 Python
Django 登陆验证码和中间件的实现
2018/08/17 Python
python print输出延时,让其立刻输出的方法
2019/01/07 Python
python创造虚拟环境方法总结
2019/03/04 Python
Python3 批量扫描端口的例子
2019/07/25 Python
在django模板中实现超链接配置
2019/08/21 Python
Pycharm如何运行.py文件的方法步骤
2020/03/03 Python
Python 实现打印单词的菱形字符图案
2020/04/12 Python
欧洲最大的品牌水上运动服装和设备在线零售商:Wuituit Outlet
2018/05/05 全球购物
培训科主任岗位职责
2014/08/08 职场文书
2015年社区重阳节活动总结
2015/07/30 职场文书
教师远程研修感悟
2015/11/18 职场文书
告诉你创业计划书的8个实用技巧
2019/07/12 职场文书
人为什么会“幸灾乐祸”?
2019/08/06 职场文书
python Django框架快速入门教程(后台管理)
2021/07/21 Python