基于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学习笔记之(Url,QueryString,Path)模块
Jan 13 NodeJs
详解Nodejs基于mongoose模块的增删改查的操作
Dec 21 NodeJs
使用nodejs爬取前程无忧前端技能排行
May 06 NodeJs
nodejs+websocket实时聊天系统改进版
May 18 NodeJs
详解nodejs异步I/O和事件循环
Jun 07 NodeJs
详解nodejs的express如何自动生成项目框架
Jul 12 NodeJs
nodejs(officegen)+vue(axios)在客户端导出word文档的方法
Jul 31 NodeJs
基于Nodejs的Tcp封包和解包的理解
Sep 19 NodeJs
nodejs 递归拷贝、读取目录下所有文件和目录
Jul 18 NodeJs
nodejs使用node-xlsx生成excel的方法示例
Aug 22 NodeJs
使用nodejs实现JSON文件自动转Excel的工具(推荐)
Jun 24 NodeJs
nodejs+koa2 实现模仿springMVC框架
Oct 21 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中判断一个请求是ajax请求还是普通请求的方法
2011/06/28 PHP
php截取字符串函数分享
2015/02/02 PHP
thinkphp中的多表关联查询的实例详解
2017/10/12 PHP
php实现微信企业转账功能
2018/10/02 PHP
php pdo连接数据库操作示例
2019/11/18 PHP
php5.3/5.4/5.5/5.6/7常见新增特性汇总整理
2020/02/27 PHP
php+js实现的拖动滑块验证码验证表单操作示例【附源码下载】
2020/05/27 PHP
php获取小程序码的实现代码(B类接口)
2020/06/13 PHP
jQuery下扩展插件和拓展函数的写法(匿名函数使用的典型例子)
2010/10/20 Javascript
JavaScript全局函数使用简单说明
2011/03/11 Javascript
ASP.NET jQuery 实例5 (显示CheckBoxList成员选中的内容)
2012/01/13 Javascript
javascript中的this详解
2014/12/08 Javascript
JavaScript中的逻辑判断符&amp;&amp;、||与!介绍
2014/12/31 Javascript
jquery+easeing实现仿flash的载入动画
2015/03/10 Javascript
JavaScript中的Number数字类型学习笔记
2016/05/26 Javascript
Node.js Express 框架 POST方法详解
2017/01/23 Javascript
解决npm管理员身份install时出现权限的问题
2018/03/16 Javascript
微信小程序使用swiper组件实现类3D轮播图
2018/08/29 Javascript
微信小程序下拉框功能的实例代码
2018/11/06 Javascript
详解react组件通讯方式(多种)
2020/05/06 Javascript
微信小程序弹窗禁止页面滚动的实现代码
2020/12/30 Javascript
[01:32]完美世界DOTA2联赛10月29日精彩集锦
2020/10/30 DOTA
如何基于Django实现上下文章跳转
2020/09/16 Python
HTML5表格_动力节点Java学院整理
2017/07/11 HTML / CSS
HTML5 自动聚焦(autofocus)属性使用介绍
2013/08/07 HTML / CSS
期末总结的个人自我评价
2013/11/02 职场文书
大学军训自我鉴定
2013/12/15 职场文书
个人买房协议书范本
2014/10/06 职场文书
2014年社区计生工作总结
2014/11/18 职场文书
文明家庭事迹材料
2014/12/20 职场文书
继承权公证书范本
2015/01/23 职场文书
会计试用期工作总结2015
2015/05/28 职场文书
PyQt5 显示超清高分辨率图片的方法
2021/04/11 Python
python基础之类属性和实例属性
2021/10/24 Python
python中tkinter复选框使用操作
2021/11/11 Python
SQL Server携程核心系统无感迁移到MySQL实战
2022/06/01 SQL Server