基于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 相关文章推荐
js实现的跟随鼠标移动的时钟效果(中英文日期显示)
Jan 17 Javascript
js禁止document element对象选中文本实现代码
Mar 21 Javascript
Javascript alert消息换行的方法
Aug 07 Javascript
jQuery设置和移除文本框默认值的方法
Mar 09 Javascript
jquery获取img的src值的简单实例
May 17 Javascript
Jquery 整理元素选取、常用方法一览表
Nov 26 Javascript
js 判断数据类型的几种方法
Jan 13 Javascript
jquery.tableSort.js表格排序插件使用方法详解
Aug 12 Javascript
vue2.0使用swiper组件实现轮播效果
Nov 27 Javascript
基于vue实现可搜索下拉框定制组件
Mar 26 Javascript
Vue中使用webpack别名的方法实例详解
Jun 19 Javascript
基于Vue SEO的四种方案(小结)
Jul 01 Javascript
盘点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
PHP文件上传实例详解!!!
2007/01/02 PHP
mysql 字段类型说明
2007/04/27 PHP
php strtotime 函数UNIX时间戳
2009/01/14 PHP
php类中private属性继承问题分析
2012/11/01 PHP
PHP PDOStatement对象bindpram()、bindvalue()和bindcolumn之间的区别
2014/11/20 PHP
Zend Framework常用校验器详解
2016/12/09 PHP
jQuery 隔行换色 支持键盘上下键,按Enter选定值
2009/08/02 Javascript
深入理解JavaScript系列(30):设计模式之外观模式详解
2015/03/03 Javascript
根据user-agent判断蜘蛛代码黑帽跳转代码(js版与php版本)
2015/09/14 Javascript
JavaScript的History API使搜索引擎抓取AJAX内容
2015/12/07 Javascript
jQuery针对input的class属性写了多个值情况下的选择方法
2016/06/03 Javascript
jQuery移动端图片上传组件
2016/06/12 Javascript
Html5+jQuery+CSS制作相册小记录
2016/12/30 Javascript
BootStrap Fileinput初始化时的一些参数
2016/12/30 Javascript
angular ng-repeat数组中的数组实例
2017/02/18 Javascript
JS/jquery实现一个网页内同时调用多个倒计时的方法
2017/04/27 jQuery
vue使用Google地图的实现示例代码
2018/12/19 Javascript
elementUI table表格动态合并的示例代码
2019/05/15 Javascript
Python类定义和类继承详解
2015/05/08 Python
Python通过Pygame绘制移动的矩形实例代码
2018/01/03 Python
对python内置map和six.moves.map的区别详解
2018/12/19 Python
Python多线程原理与用法实例剖析
2019/01/22 Python
python使用参数对嵌套字典进行取值的方法
2019/04/26 Python
selenium+python自动化测试环境搭建步骤
2019/06/03 Python
python 密码学示例——理解哈希(Hash)算法
2020/09/21 Python
美国时尚女装在线:Missguided
2016/12/03 全球购物
三星新西兰官网:Samsung新西兰
2019/03/05 全球购物
维氏瑞士军刀英国网站:Victorinox英国
2019/07/04 全球购物
在校生党员自我评价
2013/09/25 职场文书
网络营销策划方案
2014/06/04 职场文书
售后客服工作职责
2014/06/16 职场文书
综治目标管理责任书
2015/05/11 职场文书
Pytorch中Softmax和LogSoftmax的使用详解
2021/06/05 Python
实体类或对象序列化时,忽略为空属性的操作
2021/06/30 Java/Android
Golang 并发下的问题定位及解决方案
2022/03/16 Golang
在容器中使用nginx搭建上传下载服务器
2022/05/11 Servers