基于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的前后端分离的思考与实践(一)全栈式开发
Sep 26 NodeJs
基于NodeJS的前后端分离的思考与实践(三)轻量级的接口配置建模框架
Sep 26 NodeJs
nodejs教程之异步I/O
Nov 21 NodeJs
轻松创建nodejs服务器(1):一个简单nodejs服务器例子
Dec 18 NodeJs
轻松创建nodejs服务器(3):代码模块化
Dec 18 NodeJs
详谈Angular路由与Nodejs路由的区别
Mar 05 NodeJs
nodejs个人博客开发第一步 准备工作
Apr 12 NodeJs
ubuntu编译nodejs所需的软件并安装
Sep 12 NodeJs
nodejs 日志模块winston的使用方法
May 02 NodeJs
nodejs的路径问题的解决
Jun 30 NodeJs
NodeJs 实现简单WebSocket即时通讯的示例代码
Aug 05 NodeJs
nodejs脚本centos开机启动实操方法
Mar 04 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实现比较两个字符串日期大小的方法
2015/05/12 PHP
五个jQuery图片画廊插件 推荐
2011/05/12 Javascript
js为数字添加逗号并格式化数字的代码
2013/08/23 Javascript
js图片预加载示例
2014/04/30 Javascript
jquery 3D 标签云示例代码
2014/06/12 Javascript
jQuery动态背景图片效果实现方法
2015/07/03 Javascript
jquery实现鼠标点击后展开列表内容的导航栏效果
2015/09/14 Javascript
javascript中类的定义方式详解(四种方式)
2015/12/22 Javascript
JS实现简单易用的手机端浮动窗口显示效果
2016/09/07 Javascript
原生JS实现图片轮播效果
2016/12/26 Javascript
jQuery实现切换隐藏与显示同时切换图标功能
2017/10/29 jQuery
利用Console来Debug的10个高级技巧汇总
2018/03/26 Javascript
fastadmin中调用js的方法
2019/05/14 Javascript
小程序实现搜索框功能
2020/03/26 Javascript
js中的this的指向问题详解
2019/08/29 Javascript
Vue函数式组件的应用实例详解
2019/08/30 Javascript
原生js实现随机点餐效果
2019/12/10 Javascript
vue中的双向数据绑定原理与常见操作技巧详解
2020/03/16 Javascript
JavaScript find()方法及返回数据实例
2020/04/30 Javascript
autojs 蚂蚁森林能量自动拾取即给指定好友浇水的实现方法
2020/05/03 Javascript
Python模拟登录12306的方法
2014/12/30 Python
读写json中文ASCII乱码问题的解决方法
2016/11/05 Python
Python使用pandas对数据进行差分运算的方法
2018/12/22 Python
python多环境切换及pyenv使用过程详解
2019/09/27 Python
Flask框架 CSRF 保护实现方法详解
2019/10/30 Python
Python logging日志库空间不足问题解决
2020/09/14 Python
Django websocket原理及功能实现代码
2020/11/14 Python
Flask-SocketIO服务端安装及使用代码示例
2020/11/26 Python
美国时尚女装在线:Missguided
2016/12/03 全球购物
电钳工人个人求职信
2014/05/10 职场文书
弄虚作假心得体会
2014/09/10 职场文书
公司试用期员工自我评价
2014/09/17 职场文书
股东授权委托书
2014/10/15 职场文书
学习十八届四中全会依法治国心得体会
2014/11/03 职场文书
《我是什么》教学反思
2016/02/16 职场文书
Go语言grpc和protobuf
2022/04/13 Golang