基于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 相关文章推荐
windows系统下简单nodejs安装及环境配置
Jan 08 NodeJs
用nodejs写的一个简单项目打包工具
May 11 NodeJs
nodejs爬虫抓取数据之编码问题
Jul 03 NodeJs
浅谈Nodejs应用主文件index.js
Aug 28 NodeJs
Jquery通过ajax请求NodeJS返回json数据实例
Nov 08 NodeJs
Nodejs 搭建简单的Web服务器详解及实例
Nov 30 NodeJs
详解nodejs模板引擎制作
Jun 14 NodeJs
nodejs超出最大的调用栈错误问题
Dec 27 NodeJs
详解从NodeJS搭建中间层再谈前后端分离
Nov 13 NodeJs
详解nodejs解压版安装和配置(带有搭建前端项目脚手架)
Dec 06 NodeJs
NodeJs之word文件生成与解析的实现代码
Apr 01 NodeJs
Nodejs 数组的队列以及forEach的应用详解
Feb 25 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 zlib扩展实现页面GZIP压缩输出
2010/06/17 PHP
php递归遍历多维数组的方法
2015/04/18 PHP
php5.3/5.4/5.5/5.6/7常见新增特性汇总整理
2020/02/27 PHP
跨域请求之jQuery的ajax jsonp的使用解惑
2011/10/09 Javascript
一个JavaScript递归实现反转数组字符串的实例
2014/10/14 Javascript
$.extend 的一个小问题
2015/06/18 Javascript
KnockoutJS 3.X API 第四章之click绑定
2016/10/10 Javascript
jQuery中layer分页器的使用
2017/03/13 Javascript
简单快速的实现js计算器功能
2017/08/17 Javascript
基于JavaScript实现表格滚动分页
2017/11/22 Javascript
React Form组件的实现封装杂谈
2018/05/07 Javascript
js+SVG实现动态时钟效果
2018/07/14 Javascript
详解SPA中前端路由基本原理与实现方式
2018/09/12 Javascript
React性能优化系列之减少props改变的实现方法
2019/01/17 Javascript
在 Vue 应用中使用 Netlify 表单功能的方法详解
2019/06/03 Javascript
微信小程序自定义yPicker组件实现省市区三级联动功能
2020/10/29 Javascript
vue video和vue-video-player实现视频铺满教程
2020/10/30 Javascript
在vant中使用时间选择器和popup弹出层的操作
2020/11/04 Javascript
wxpython中利用线程防止假死的实现方法
2014/08/11 Python
python实现俄罗斯方块游戏
2020/03/25 Python
Python语言进阶知识点总结
2019/05/28 Python
Python爬虫库BeautifulSoup获取对象(标签)名,属性,内容,注释
2020/01/25 Python
jupyter notebook清除输出方式
2020/04/10 Python
解决keras使用cov1D函数的输入问题
2020/06/29 Python
解决python和pycharm安装gmpy2 出现ERROR的问题
2020/08/28 Python
Farnell德国:电子元器件供应商
2018/07/10 全球购物
澳大利亚领先的在线礼品网站:Gifts Australia
2020/08/15 全球购物
幼儿园教师获奖感言
2014/03/11 职场文书
节约每一滴水演讲稿
2014/09/09 职场文书
学生党员批评与自我批评
2014/10/15 职场文书
社区四风存在问题及整改措施
2014/10/26 职场文书
企业战略合作意向书
2015/05/08 职场文书
团队拓展训练感想
2015/08/07 职场文书
《最后一头战象》读后感:动物也有感情
2020/01/02 职场文书
如何在Python中妥善使用进度条详解
2022/04/05 Python
二维码条形码生成的JavaScript脚本库
2022/07/07 Javascript