基于Nodejs利用socket.io实现多人聊天室


Posted in NodeJs onFebruary 22, 2017

socket.io简介

在Html5中存在着这样的一个新特性,引入了websocket,关于websocket的内部实现原理可以看这篇文章,这篇文章讲述了websocket无到有,根据协议,分析数据帧的头,进行构建websocket。虽然代码短,但可以很好地体现websocket的原理。

,这个特性提供了浏览器端和服务器端的基于TCP连接的双向通道。但是并不是所有的浏览器都支持websocket特性,故为了磨平浏览器间的差异,为开发者提供统一的接口,引入了socket.io模块。在不支持websoket的浏览器中,socket.io可以降级为其他的通信方式,比如有AJAX long polling ,JSONP Polling等。
模块安装

新建一个package.json文件,在文件中写入如下内容:

{
 "name": "socketiochatroom",
 "version": "0.0.1",
 "dependencies": {
 "socket.io": "*",
 "express":"*"
 }
}

npm install

执行完这句,node将会从npm处下载socket.io和express模块。

-

服务器端的实现

在文件夹中添加index.js文件,并在文件中写入如下内容:

/**
 * Created by bamboo on 2016/3/31.
 */
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function (req, res) {
 "use strict";
 res.end("<h1>socket server</h1>")
});
/*在线人员*/
var onLineUsers = {};
/* 在线人数*/
var onLineCounts = 0;
/*io监听到存在链接,此时回调一个socket进行socket监听*/
io.on('connection', function (socket) {
 console.log('a user connected');
 /*监听新用户加入*/
 socket.on('login', function (user) {
  "use strict";
  //暂存socket.name 为user.userId;在用户退出时候将会用到
  socket.name = user.userId;
  /*不存在则加入 */
  if (!onLineUsers.hasOwnProperty(user.userId)) {
   //不存在则加入
   onLineUsers[user.userId] = user.userName;
   onLineCounts++;
  }
  /*一个用户新加入,向所有客户端监听login的socket的实例发送响应,响应内容为一个对象*/
  io.emit('login', {onLineUsers: onLineUsers, onLineCounts: onLineCounts, user: user});
  console.log(user.userName, "加入了聊天室");//在服务器控制台中打印么么么用户加入到了聊天室
 });
 /*监听用户退出聊天室*/
 socket.on('disconnect', function () {
  "use strict";
  if (onLineUsers.hasOwnProperty(socket.name)) {
   var user = {userId: socket.name, userName: onLineUsers[socket.name]};
   delete onLineUsers[socket.name];
   onLineCounts--;
   /*向所有客户端广播该用户退出群聊*/
   io.emit('logout', {onLineUsers: onLineUsers, onLineCounts: onLineCounts, user: user});
   console.log(user.userName, "退出群聊");
  }
 })
 /*监听到用户发送了消息,就使用io广播信息,信息被所有客户端接收并显示。注意,如果客户端自己发送的也会接收到这个消息,故在客户端应当存在这的判断,是否收到的消息是自己发送的,故在emit时,应该将用户的id和信息封装成一个对象进行广播*/
 socket.on('message', function (obj) {
  "use strict";
  /*监听到有用户发消息,将该消息广播给所有客户端*/
  io.emit('message', obj);
  console.log(obj.userName, "说了:", obj.content);
 });
});
/*监听3000*/
http.listen(3000, function () {
 "use strict";
 console.log('listening 3000');
});

运行服务器端程序

node index.js

输出

listening 3000

此时在浏览器中打开localhost:3000会得到这样的结果:

基于Nodejs利用socket.io实现多人聊天室

原因是在代码中只对路由进行了如下设置

app.get('/', function (req, res) {
 "use strict";
 res.end("<h1>socket server</h1>")
});

服务器端主要是提供socketio服务,并没有设置路由。

客户端的实现

在客户端建立如下的目录和文件,其中json3.min.js可以从网上下载到。

client

- - - client.js
- - - index.html
- - - json3.min.js
- - - style.css

在index.html中

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta name="format-detection" content="telephone=no"/>
 <meta name="format-detection" content="email=no"/>
 <title>1301群聊</title>
 <link rel="stylesheet" type="text/css" href="./style.css"/>
 <script src="http://realtime.plhwin.com:3000/socket.io/socket.io.js"></script>
 <script src="./json3.min.js"></script>
</head>
<body>
<div id="loginbox">
 <div style="width: 260px;margin: 200px auto;">
  输入你在群聊中的昵称
  <br/>
  <br/>
  <input type="text" style="width:180px;" placeholder="请输入用户名" id="userName" name="userName"/>
  <input type="button" style="width: 50px;" value="提交" onclick="CHAT.userNameSubmit();"/>
 </div>
</div>
<div id="chatbox" style="display: none;">
 <div style="background: #3d3d3d;height: 28px;width: 100%;font-size: 12px">
  <div style="line-height: 28px;color:#fff;">
   <span style="text-align: left;margin-left: 10px;">1301群聊</span>
   <span style="float: right;margin-right: 10px"><span id="showUserName"></span>|
   <a href="javascript:;" onclick="CHAT.logout()" style="color: #fff;">退出</a></span>
  </div>
 </div>
 <div id="doc">
  <div id="chat">
   <div id="message" class="message">
    <div id="onLineCounts"
      style="background: #EFEFF4; font-size: 12px;margin-top: 10px;margin-left: 10px;color: #666;">
    </div>
   </div>
   <div class="input-box">
    <div class="input">
     <input type="text" maxlength="140" placeholder="输入聊天内容 " id="content" name="content" >
    </div>
    <div class="action">
     <button type="button" id="mjr_send" onclick="CHAT.submit();">提交</button>
    </div>
   </div>
  </div>
 </div>
</div>
<script type="text/javascript" src="./client.js"></script>
</body>
</html>

在client.js中

/**
 * Created by bamboo on 2016/3/31.
 */
 /*即时运行函数*/
(function () {
 "use strict";
 var d = document,
  w = window,
  dd = d.documentElement,
  db = d.body,
  dc = d.compatMode === "CSS1Compat",
  dx = dc ? dd : db,
  ec = encodeURIComponent,
  p = parseInt;
 w.CHAT = {
  msgObj: d.getElementById("message"),
  screenHeight: w.innerHeight ? w.innerHeight : dx.innerHeight,
  userName: null,
  userId: null,
  socket: null,
  /*滚动条始终在最底部*/
  scrollToBottom: function () {
   w.scrollTo(0, this.msgObj.clientHeight);
  },
  /*此处仅为简单的刷新页面,当然可以做复杂点*/
  logout: function () {
   // this.socket.disconnect();
   w.top.location.reload();
  },
  submit: function () {
   var content = d.getElementById('content').value;
   if (content != '') {
    var obj = {
     userId: this.userId,
     userName: this.userName,
     content: content
    };
    //如在服务器端代码所说,此对象就行想要发送的信息和发送人组合成为对象一起发送。
    this.socket.emit('message', obj);
    d.getElementById('content').value = '';
   }
   return false;
  },
  /**客户端根据时间和随机数生成ID,聊天用户名称可以重复*/
  genUid: function () {
   return new Date().getTime() + "" + Math.floor(Math.random() * 889 + 100);
  },
  /*更新系统信息
  主要是在客户端显示当前在线人数,在线人列表等,当有新用户加入或者旧用户退出群聊的时候做出页面提示。*/
  updateSysMsg: function (o, action) {
   var onLineUsers = o.onLineUsers;
   var onLineCounts = o.onLineCounts;
   var user = o.user;
   //更新在线人数
   var userHtml = '';
   var separator = '';
   for (var key in onLineUsers) {
    if (onLineUsers.hasOwnProperty(key)) {
     userHtml += separator + onLineUsers[key];
     separator = '、';
    }
   }
   //插入在线人数和在线列表
   d.getElementById('onLineCounts').innerHTML = '当前共有' + onLineCounts + "在线列表: " + userHtml;
   //添加系统消息
   var html = '';
   html += '<div class="msg_system">';
   html += user.userName;
   html += (action === "login") ? "加入了群聊" : "退出了群聊";
   html += '</div>';
   var section = d.createElement('section');
   section.className = 'system J-mjrlinkWrap J-cutMsg';
   section.innerHTML = html;
   this.msgObj.appendChild(section);
   this.scrollToBottom();
  },
  /*用户提交用户名后,将loginbox设置为不显示,将chatbox设置为显示*/
  userNameSubmit: function () {
   var userName = d.getElementById('userName').value;
   if (userName != '') {
    d.getElementById('userName').value = '';
    d.getElementById('loginbox').style.display = 'none';
    d.getElementById('chatbox').style.display = 'block';
    this.init(userName);//调用init方法
   }
   return false;
  },
  //用户初始化
  init: function (userName) {
   //随机数生成uid
   this.userId = this.genUid();
   this.userName = userName;
   d.getElementById('showUserName').innerHTML = this.userName;//[newpidian]|[退出]
   this.scrollToBottom();
   //连接socketIO服务器,newpidian的IP地址
   this.socket = io.connect('192.168.3.155:3000');
   //向服务器发送某用户已经登录了
   this.socket.emit('login', {userId: this.userId, userName: this.userName});
   //监听来自服务器的login,即在客户端socket.emit('login ')发送后,客户端就会收到来自服务器的
   // io.emit('login', {onLineUsers: onLineUsers, onLineCounts: onLineCounts, user: user});
   /*监听到有用户login了,更新信息*/
   this.socket.on('login', function (o) {
    //更新系统信息
    CHAT.updateSysMsg(o, 'login');
   });
   /*监听到有用户logout了,更新信息*/
   this.socket.on('logout', function (o) {
    CHAT.updateSysMsg(o, 'logout');
   });
   //var obj = {
   // userId: this.userId,
   // userName: this.userName,
   // content: content
   //};
   /*监听到有用户发送消息了*/
   this.socket.on("message", function (obj) {
    //判断消息是不是自己发送的
    var isMe = (obj.userId === CHAT.userId);
    var contentDiv = '<div>' + obj.content + '</div>';
    var userNameDiv = '<span>' + obj.userName + '</span>';
    var section = d.createElement('section');
    if (isMe) {
     section.className = 'user';
     section.innerHTML = contentDiv + userNameDiv;
    } else {
     section.className = 'service';
     section.innerHTML = userNameDiv + contentDiv;
    }
    CHAT.msgObj.appendChild(section);
    CHAT.scrollToBottom();
   });
  }
 }
 /*控制键键码值(keyCode)
  按键 键码 按键 键码 按键 键码 按键 键码
  BackSpace 8 Esc 27 Right Arrow 39 -_ 189
  Tab 9 Spacebar 32 Dw Arrow 40 .> 190
  Clear 12 Page Up 33 Insert 45 /? 191
  Enter 13 Page Down 34 Delete 46 `~ 192
  Shift 16 End 35 Num Lock 144 [{ 219
  Control 17 Home 36 ;: 186 \| 220
  Alt 18 Left Arrow 37 =+ 187 ]} 221
  Cape Lock 20 Up Arrow 38 ,< 188 '" 222
  * */
 //通过“回车键”提交用户名
 d.getElementById('userName').onkeydown = function (e) {
  console.log(e);
  e = e || event;
  if (e.keyCode === 13) {
   CHAT.userNameSubmit();
  }
 };
 //通过“回车键”提交聊天内容
 d.getElementById('content').onkeydown = function (e) {
  e = e || event;
  if (e.keyCode === 13) {
   CHAT.submit();
  }
 };
})();

style.css

秘密

运行结果

服务器端已经运行,现将客户端也运行起来得到下图:

基于Nodejs利用socket.io实现多人聊天室

添加了new和pidian两个用户,并发送信息和进行退出,得到下面的结果:

基于Nodejs利用socket.io实现多人聊天室

以上所述是小编给大家介绍的基于Nodejs利用socket.io实现多人聊天室,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

NodeJs 相关文章推荐
基于nodejs+express(4.x+)实现文件上传功能
Nov 23 NodeJs
nodejs模块nodemailer基本使用-邮件发送示例(支持附件)
Mar 28 NodeJs
解析NodeJS异步I/O的实现
Apr 13 NodeJs
使用nodejs爬取前程无忧前端技能排行
May 06 NodeJs
基于nodejs实现微信支付功能
Dec 20 NodeJs
通过nodejs 服务器读取HTML文件渲染到页面的方法
May 17 NodeJs
详解NodeJs开发微信公众号
May 25 NodeJs
Nodejs调用Dll模块的方法
Sep 17 NodeJs
nodeJS进程管理器pm2的使用
Jan 09 NodeJs
如何让Nodejs支持H5 History模式(connect-history-api-fallback源码分析)
May 30 NodeJs
linux 下以二进制的方式安装 nodejs
Feb 12 NodeJs
浅谈使用nodejs搭建web服务器的过程
Jul 20 NodeJs
NodeJS配置HTTPS服务实例分享
Feb 19 #NodeJs
解决nodejs中使用http请求返回值为html时乱码的问题
Feb 18 #NodeJs
利用nodejs监控文件变化并使用sftp上传到服务器
Feb 18 #NodeJs
详解nodejs中exports和module.exports的区别
Feb 17 #NodeJs
Nodejs+Socket.io实现通讯实例代码
Feb 13 #NodeJs
Nodejs高扩展性的模板引擎 functmpl简介
Feb 13 #NodeJs
Nodejs 发送Post请求功能(发短信验证码例子)
Feb 09 #NodeJs
You might like
PHP动态变静态原理
2006/11/25 PHP
PHP GD 图像处理组件的常用函数总结
2010/04/28 PHP
ThinkPHP3.2框架自定义配置和加载用法示例
2018/06/14 PHP
JavaScript入门教程(9) Document文档对象
2009/01/31 Javascript
javascript面向对象的方式实现的弹出层效果代码
2010/01/28 Javascript
js Math 对象的方法
2013/09/01 Javascript
JS将表单导出成EXCEL的实例代码
2013/11/11 Javascript
javascript:void(0)是什么意思示例介绍
2013/11/17 Javascript
js实现数组去重、判断数组以及对象中的内容是否相同
2013/11/29 Javascript
jquery插件lazyload.js延迟加载图片的使用方法
2014/02/19 Javascript
JS实现仿FLASH效果的竖排导航代码
2015/09/15 Javascript
js阻止浏览器默认行为的简单实例
2016/05/15 Javascript
基于Bootstrap的Metronic框架实现条码和二维码的生成及打印处理操作
2016/08/29 Javascript
AngularJS过滤器filter用法实例分析
2016/11/04 Javascript
Node.js websocket使用socket.io库实现实时聊天室
2017/02/20 Javascript
JavaScript关联数组用法分析【概念、定义、遍历】
2017/03/15 Javascript
详解AngularJS ng-class样式切换
2017/06/27 Javascript
nodejs中实现用户注册路由功能
2019/05/20 NodeJs
你准备好迎接vue3.0了吗
2020/04/28 Javascript
Vue中ref和$refs的介绍以及使用方法示例
2021/01/11 Vue.js
[45:18]2018DOTA2亚洲邀请赛 4.3 突围赛 Optic vs iG 第一场
2018/04/04 DOTA
Python登录并获取CSDN博客所有文章列表代码实例
2017/12/28 Python
浅析Python3中的对象垃圾收集机制
2019/06/06 Python
python找出一个列表中相同元素的多个索引实例
2019/06/11 Python
python图的深度优先和广度优先算法实例分析
2019/10/26 Python
一文解决django 2.2与mysql兼容性问题
2020/07/15 Python
python集合能干吗
2020/07/19 Python
瑞典灯具和照明网上商店:Lamp24.se
2018/03/17 全球购物
新西兰廉价汽车租赁:Snap Rentals
2018/09/14 全球购物
正规的求职信范文分享
2013/12/11 职场文书
拾金不昧的表扬信
2014/01/16 职场文书
战友聚会邀请函
2014/01/18 职场文书
先进德育工作者事迹材料
2014/01/24 职场文书
孕妇病假条怎么写
2015/08/17 职场文书
C站最全Python标准库总结,你想要的都在这里
2021/07/03 Python
Netty分布式客户端处理接入事件handle源码解析
2022/03/25 Java/Android