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中获取未知对象属性的代码
Apr 27 Javascript
基于jquery自己写tab滑动门(通用版)
Oct 30 Javascript
利用js实现在浏览器状态栏显示访问者在本页停留的时间
Dec 29 Javascript
使用javascript为网页增加夜间模式
Jan 26 Javascript
基于jQuery实现点击最后一行实现行自增效果的表格
Jan 12 Javascript
JavaScript中的跨浏览器事件操作的基本方法整理
May 20 Javascript
全面解析Bootstrap中tooltip、popover的使用方法
Jun 13 Javascript
Bootstrap表单控件使用方法详解
Jan 11 Javascript
ES6中Proxy与Reflect实现重载(overload)的方法
Mar 30 Javascript
JavaScript中错误正确处理方式小结你用对了吗
Oct 10 Javascript
Android 自定义view仿微信相机单击拍照长按录视频按钮
Jul 19 Javascript
vue 封装 Adminlte3组件的实现
Mar 18 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
图解上海144收音机
2021/03/02 无线电
玩转虚拟域名◎+ .
2006/10/09 PHP
木翼下载系统中说明的PHP安全配置方法
2007/06/16 PHP
PHP 中魔术常量的实例详解
2017/10/26 PHP
php+js实现的无刷新下载文件功能示例
2019/08/23 PHP
基于prototype的validation.js发布2.3.4新版本,让你彻底脱离表单验证的烦恼
2006/12/06 Javascript
Javascript Math ceil()、floor()、round()三个函数的区别
2010/03/09 Javascript
js切换div css注意的细节
2012/12/10 Javascript
深入document.write()与HTML4.01的非成对标签的详解
2013/05/08 Javascript
Egret引擎开发指南之编译项目
2014/09/03 Javascript
jQuery实现平滑滚动到指定锚点的方法
2015/03/20 Javascript
JS日期对象简单操作(获取当前年份、星期、时间)
2016/10/26 Javascript
BootstrapValidator超详细教程(推荐)
2016/12/07 Javascript
jquery ajaxfileupload异步上传插件使用详解
2017/02/08 Javascript
微信小程序网络请求的封装与填坑之路
2017/04/01 Javascript
基于复选框demo(分享)
2017/09/27 Javascript
详解Nodejs get获取远程服务器接口数据
2019/03/26 NodeJs
如何构建 vue-ssr 项目的方法步骤
2020/08/04 Javascript
[03:45]Newbee战队出征西雅图 决战2016国际邀请赛
2016/08/02 DOTA
python聊天程序实例代码分享
2013/11/18 Python
Python装饰器用法实例总结
2018/02/07 Python
由Python编写的MySQL管理工具代码实例
2019/04/09 Python
python多进程读图提取特征存npy
2019/05/21 Python
Django框架会话技术实例分析【Cookie与Session】
2019/05/24 Python
django云端留言板实例详解
2019/07/22 Python
Django实现将views.py中的数据传递到前端html页面,并展示
2020/03/16 Python
基于Django快速集成Echarts代码示例
2020/12/01 Python
马来西亚户外装备商店:PTT Outdoor
2019/07/13 全球购物
英国设计师泳装、沙滩装和比基尼在线精品店:Beach Cafe
2019/08/28 全球购物
印度电子产品购物网站:Vijay Sales
2021/02/16 全球购物
如何启动时不需输入用户名与密码
2014/05/09 面试题
五十岁生日宴会答谢词
2014/01/15 职场文书
银行给客户的感谢信
2015/01/23 职场文书
2016年度创先争优活动总结
2016/04/05 职场文书
Nginx防盗链与服务优化配置的全过程
2022/01/18 Servers
css3手动实现pc端横向滚动
2022/06/21 HTML / CSS