Node.js利用Net模块实现多人命令行聊天室的方法


Posted in Javascript onDecember 23, 2016

这篇文章介绍的是Node.js利用Net模块实现命令行式的多人聊天室,下面话不多说,来看看详细的介绍吧。

1、net模块基本API

要使用Node.js的net模块实现一个命令行聊天室,就必须先了解NET模块的API使用。NET模块API分为两大类:

Server和Socket类、工厂方法。

Server类如下图所示:

Node.js利用Net模块实现多人命令行聊天室的方法

net.Server类可以用来创建一个TCP或本地服务器,继承了EventEmitter。

Socket类如下:

Node.js利用Net模块实现多人命令行聊天室的方法

net.Socket类一般用创建一个socket客户端或者是net.Server connection事件的参数。

工厂方法如下:

Node.js利用Net模块实现多人命令行聊天室的方法

以上三个图展示了API的使用,其实NET模块的内部原理和C++网络编程差不多的,都是以下步骤。

服务端:

  1. 创建socket套接字
  2. 绑定IP和端口
  3. 启动监听
  4. 等待客户端连接
  5. 与客户端进行通信
  6. 关闭socket

客户端:

  1. 创建socket套接字
  2. 连接server服务器
  3. 与服务器进行通信
  4. 关闭socket

如下图所示:

Node.js利用Net模块实现多人命令行聊天室的方法

2、聊天室的设计和实现

上面学习了NET模块API的使用,接下来便开始实现命令行聊天室,我们不需要做的很复杂,只需实现如下功能即可:

  1. 用户自定义昵称,不可更改
  2. 当有新的用户进入聊天室,或者用户离开聊天室,广播给其他用户
  3. 用户发送信息,需广播给其他用户
  4. 客户端与服务端建立心跳机制
  5. 用户输入'exit'或者'quit'可以退出聊天室

确定功能之后,便开始代码的编写。这里我就不一步步分析,直接上代码了,首先是服务端:

Server:

const net = require('net');
const server = net.createServer();
const clients = {};//保存客户端的连接
var client = null;//当前客户连接
var uid = 0;
server.on('connection',(socket)=>{
 //启动心跳机制
 var isOnline = !0;
 var keepAliveTimer = socket.timer = setInterval(()=>{
  if(!isOnline){
   isOnline = !1;
   client = socket;
   quit(socket.nick);
   return;
  }
  if(socket.writable){
   socket.write('::');
  }else{
   client = socket;
   quit(socket.nick);
  }
 },3000);
 socket.on('end',()=>{
  console.log(`client disconnected.\n\r`);
  socket.destroy();
 });
 socket.on('error',(error)=>{
  console.log(error.message);
 });
 socket.on('data',(chunk)=>{
  client = socket;
  var msg = JSON.parse(chunk.toString());
  if(msg.cmd=='keep'){
   isOnline = !0;
   return;
  }
  dealMsg(msg);
 });
});
server.on('error',(err)=>{
 console.log(err);
});
server.on('listening',()=>{
 console.log(`listening on ${server.address().address}:${server.address().port}\n\r`);
});
server.listen(8060);//启动监听
/**
 * 处理用户信息
 */
function dealMsg(msg){
 const cmd = msg.cmd;
 const funs = {
  'login':login,
  'chat':chat,
  'quit':quit,
  'exit':quit
 };
 if(typeof funs[cmd] !== 'function') return !1;
 funs[cmd](msg);
}
/**
 * 释放连接资源
 */
function freeConn(conn){
 conn.end();
 delete clients[conn.uuid];
 conn.timer&&clearInterval(conn.timer);
}
/**
 * 用户首次进入聊天室
 */
function login(msg){
 var uuid = '';
 uuid = getRndStr(15)+(++uid);//产生用户ID
 client.write(`欢迎你,${msg.nick}:这里总共有${Object.keys(clients).length}个小伙伴在聊天.\r\n`)
 client.nick = msg.nick;
 client.uuid = uuid;
 clients[uuid] = client;
 broadcast(`系统:${msg.nick}进入了聊天室.`);

}
/**
 * 广播消息
 */
function broadcast(msg){
 Object.keys(clients).forEach((uuid)=>{
  if((clients[uuid]!=client)& clients[uuid].writable){
   clients[uuid].write(msg);
  }
 });
}
/**
 * 退出聊天室
 */
function quit(nick){
 var message = `小伙伴${nick}退出了聊天室.`;
 broadcast(message);
 freeConn(client);
}

function chat(msg){
 if(msg.msg.toLowerCase()=='quit'||msg.msg.toLowerCase()=='exit'){
  quit(msg.nick);
  return ;
 }
 var message = `${msg.nick}说:${msg.msg}`;
 broadcast(message);
} 
/**
 * 随机指定长度(len)的字符串
 */
function getRndStr(len=1){
 var rndStr = '';
 for (; rndStr.length < len; rndStr += Math.random().toString(36).substr(2));
 return rndStr.substr(0, len);
}

客户端代码如下:

client:

const net = require('net');
const cout = process.stdout;
const cin = process.stdin;

var client = null;
var nick = '';

cout.write(`请输入昵称:`);
//监听命令行输入
cin.on('data',(chunk)=>{
 if(chunk.toString()!='\r\n'){
  if(client === null){
   nick = (chunk+'').replace(/[\r\n]/ig,"");
   createClient();
  }else{
   msg = (chunk+'').replace(/[\r\n]/ig,"");
   client.write(JSON.stringify({
    cmd: 'chat',
    msg: msg,
    nick: nick
   }));
   //如果输入是exit或quit则断开连接并退出
   if(msg.toLowerCase() == 'exit' || msg.toLowerCase() == 'quit'){
    client.end();
    cin.end();
    return;
   }
   cout.write(`你说:${msg}\n\r`);
  }
 }else{
  cout.write(`请输入昵称:`);
 }
});

function addListener(client) {
 client.on('connect', () => {
  cout.write(`已连接到服务器\n\r`);
  client.write(JSON.stringify({
   cmd: 'login',
   msg: 'hello server',
   nick: nick
  }));
 });
 client.on('end', (chunk) => {
  cout.write(`与服务器断开连接.\n\r`);
 });
 client.on('data', (chunk) => {
  //如果是心跳信息则回应keep命令
  if(chunk.toString()=='::'){
   client.write(JSON.stringify({
    cmd: 'keep',
    msg: '',
    nick: nick
   }));
   return ;
  }
  cout.write(`${chunk}\n\r`);
 });
 client.on('error', (err) => {
  cout.write(`an error has occured.\n\r${err}`);
 });
}
/**
 * 创建socket并连接服务器
 */
function createClient(){
 console.log('\033[2J');//清屏操作
 cout.write(`输入'EXIT OR QUIT'退出聊天室.\r\n`);
 client = new net.Socket()
 client.connect({port:8060/*,host:'1.1.1.69'*/});
 addListener(client);
}

执行结果如下如下:

Node.js利用Net模块实现多人命令行聊天室的方法

到此,一个命令行聊天室便做完了。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
深入理解JavaScript系列(31):设计模式之代理模式详解
Mar 03 Javascript
JQuery中Text方法用法实例分析
May 18 Javascript
Easyui form combobox省市区三级联动
Jan 13 Javascript
用jQuery旋转插件jqueryrotate制作转盘抽奖
Feb 10 Javascript
Vue 2.0在IE11中打开项目页面空白的问题解决
Jul 16 Javascript
Vue-Access-Control 前端用户权限控制解决方案
Dec 01 Javascript
vue实现个人信息查看和密码修改功能
May 06 Javascript
使用layui 渲染table数据表格的实例代码
Aug 19 Javascript
angular2 NgModel模块的具体使用方法
Apr 10 Javascript
vue使用自定义指令实现拖拽
Jan 29 Javascript
Vue中component标签解决项目组件化操作
Sep 04 Javascript
JavaScript 原型与原型链详情
Nov 02 Javascript
Bootstrap select多选下拉框实现代码
Dec 23 #Javascript
Bootstrap select实现下拉框多选效果
Dec 23 #Javascript
详解微信小程序开发—你期待的分享功能来了,微信小程序序新增5大功能
Dec 23 #Javascript
JavaScript用构造函数如何获取变量的类型名
Dec 23 #Javascript
JS中with的替代方法与String中的正则方法详解
Dec 23 #Javascript
Bootstrap源码解读排版(1)
Dec 23 #Javascript
简单实现JS倒计时效果
Dec 23 #Javascript
You might like
分页详解 从此分页无忧(PHP+mysql)
2007/11/23 PHP
php字符编码转换之gb2312转为utf8
2013/10/28 PHP
[原创]smarty简单模板变量输出方法
2016/07/09 PHP
javascript中的循环语句for语句深入理解
2014/04/04 Javascript
两种方法实现在HTML页面加载完毕后运行某个js
2014/06/16 Javascript
jQuery实现文本框输入同步的方法
2015/06/20 Javascript
jquery实现模拟百分比进度条渐变效果代码
2015/10/29 Javascript
值得分享的Bootstrap Ace模板实现菜单和Tab页效果
2015/12/30 Javascript
js实现的页面加载完毕之前loading提示效果完整示例【附demo源码下载】
2016/08/02 Javascript
JavaScript中关于iframe滚动条的去除和保留
2016/11/17 Javascript
jQuery居中元素scrollleft计算方法示例
2017/01/16 Javascript
简单谈谈require模块化jquery和angular的问题
2017/06/23 jQuery
promise处理多个相互依赖的异步请求(实例讲解)
2017/08/03 Javascript
详解Angular4 路由设置相关
2017/08/26 Javascript
angular 实现的输入框数字千分位及保留几位小数点功能示例
2018/06/19 Javascript
微信小程序MUI导航栏透明渐变功能示例(通过改变rgba的a值实现)
2019/01/24 Javascript
JavaScript递归函数定义与用法实例分析
2019/01/24 Javascript
关于layui 实现点击按钮添加一行(方法渲染创建的table)
2019/09/29 Javascript
深入探究Python中变量的拷贝和作用域问题
2015/05/05 Python
Python教程之全局变量用法
2016/06/27 Python
python3操作微信itchat实现发送图片
2018/02/24 Python
Django学习笔记之ORM基础教程
2018/03/27 Python
对python中两种列表元素去重函数性能的比较方法
2018/06/29 Python
Pycharm配置远程调试的方法步骤
2018/12/17 Python
pandas基于时间序列的固定时间间隔求均值的方法
2019/07/04 Python
Python新手学习标准库模块命名
2020/05/29 Python
Python如何将装饰器定义为类
2020/07/30 Python
Python中Yield的基本用法
2020/10/18 Python
Python3.9.1中使用split()的处理方法(推荐)
2021/02/07 Python
馥绿德雅美国官方网站:Rene Furterer头皮护理专家
2019/05/01 全球购物
Farah官方网站:男士服装及配件
2019/11/01 全球购物
ruby如何进行集成操作?Ruby能进行多重继承吗?
2013/10/16 面试题
国际商务专业学生个人的自我评价
2013/09/28 职场文书
党员群众路线承诺书
2014/05/20 职场文书
党委工作总结2015
2015/04/27 职场文书
古诗之感恩老师
2019/10/24 职场文书