基于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写的一个简单项目打包工具
May 11 NodeJs
NodeJS学习笔记之Http模块
Jan 13 NodeJs
nodejs爬虫抓取数据乱码问题总结
Jul 03 NodeJs
Nodejs抓取html页面内容(推荐)
Aug 11 NodeJs
Highcharts+NodeJS搭建数据可视化平台示例
Jan 01 NodeJs
nodejs个人博客开发第六步 数据分页
Apr 12 NodeJs
详解nodejs微信公众号开发——6.自定义菜单
Apr 13 NodeJs
用nodejs实现json和jsonp服务的方法
Aug 25 NodeJs
nodejs实现连接mongodb数据库的方法示例
Mar 15 NodeJs
nodejs搭建本地服务器轻松解决跨域问题
Mar 21 NodeJs
nodejs的安装使用与npm的介绍
Sep 11 NodeJs
NodeJS开发人员常见五个错误理解
Oct 14 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/10/09 PHP
无数据库的详细域名查询程序PHP版(2)
2006/10/09 PHP
《PHP编程最快明白》第五讲:php目录、文件操作
2010/11/01 PHP
php计算数组不为空元素个数的方法
2014/01/27 PHP
php计算一个文件大小的方法
2015/03/30 PHP
深入理解JavaScript系列(1) 编写高质量JavaScript代码的基本要点
2012/01/15 Javascript
seajs中模块的解析规则详解和模块使用总结
2014/03/12 Javascript
bootstrap data与jquery .data
2014/07/07 Javascript
Javascript学习指南
2014/12/01 Javascript
javascript手工制作悬浮菜单
2015/02/12 Javascript
javascript运动框架用法实例分析(实现放大与缩小效果)
2016/01/08 Javascript
分步解析JavaScript实现tab选项卡自动切换功能
2016/01/25 Javascript
JavaScript的ExtJS框架中数面板TreePanel的使用实例解析
2016/05/21 Javascript
JavaScript仿聊天室聊天记录
2016/12/27 Javascript
从零学习node.js之利用express搭建简易论坛(七)
2017/02/25 Javascript
vue如何引用其他组件(css和js)
2017/04/13 Javascript
基于bootstrop常用类总结(推荐)
2017/09/11 Javascript
vue学习笔记五:在vue项目里面使用引入公共方法详解
2019/04/04 Javascript
JS函数动态传递参数的方法分析【基于arguments对象】
2019/06/05 Javascript
Vue-cli 移动端布局和动画使用详解
2020/08/10 Javascript
浅谈vant组件Picker 选择器选单选问题
2020/11/04 Javascript
小米5s微信跳一跳小程序python源码
2018/01/08 Python
pytorch 转换矩阵的维数位置方法
2018/12/08 Python
pandas读取csv文件,分隔符参数sep的实例
2018/12/12 Python
python生成带有表格的图片实例
2019/02/03 Python
python获取Pandas列名的几种方法
2019/08/07 Python
pandas 按日期范围筛选数据的实现
2021/02/20 Python
来自全球大都市的高级街头服饰:Pegador
2018/01/03 全球购物
static全局变量与普通的全局变量有什么区别
2014/05/27 面试题
大门门卫岗位职责
2013/11/30 职场文书
军校本科大学生自我评价
2014/01/14 职场文书
西安交大自主招生自荐信
2014/01/27 职场文书
2014领导班子专题民主生活会对照检查材料思想汇报
2014/09/23 职场文书
安全守法证明
2015/06/23 职场文书
话题作文之自信作文
2019/11/15 职场文书
详解Anyscript开发指南绕过typescript类型检查
2022/09/23 Javascript