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 相关文章推荐
利用JQuery+EasyDrag 实现弹出可拖动的Div,同时向Div传值,然后返回Div选中的值
Oct 24 Javascript
有趣的JavaScript数组长度问题代码说明
Jan 20 Javascript
web基于浏览器的本地存储方法应用
Nov 27 Javascript
JavaScript基础函数整理汇总
Jan 30 Javascript
js实现仿网易点击弹出提示同时背景变暗效果
Aug 13 Javascript
BootStrap Table 后台数据绑定、特殊列处理、排序功能
May 27 Javascript
对于Javascript 执行上下文的全面了解
Sep 05 Javascript
vue2.0结合Element-ui实战案例
Mar 06 Javascript
JavaScript多种页面刷新方法小结
Apr 04 Javascript
node Buffer缓存区常见操作示例
May 04 Javascript
layer.confirm()右边按钮实现href的例子
Sep 27 Javascript
Vue中component标签解决项目组件化操作
Sep 04 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中模拟链表和链表的基本操作示例
2016/02/27 PHP
php轻松实现文件上传功能
2016/03/03 PHP
php curl上传、下载、https登陆实现代码
2017/07/23 PHP
PHP cURL获取微信公众号access_token的实例
2018/04/28 PHP
Javascript !!的作用
2008/12/04 Javascript
JS实现超炫网页烟花动画效果的方法
2015/03/02 Javascript
jQuery ajax时间差导致的变量赋值问题分析
2016/01/22 Javascript
原生JS封装Ajax插件(同域、jsonp跨域)
2016/05/03 Javascript
javascript将中国数字格式转换成欧式数字格式的简单实例
2016/08/02 Javascript
JavaScript队列、优先队列与循环队列
2016/11/14 Javascript
javascript中apply/call和bind的使用
2017/02/15 Javascript
easyui关于validatebox实现多重规则验证的方法(必看)
2017/04/12 Javascript
Three.js入门之hello world以及如何绘制线
2017/09/25 Javascript
监听element-ui table滚动事件的方法
2019/03/26 Javascript
vue-cli在 history模式下的配置详解
2019/11/26 Javascript
Vue页面跳转传递参数及接收方式
2020/09/09 Javascript
JavaScript构造函数原理及实现流程解析
2020/11/19 Javascript
python批量提交沙箱问题实例
2014/10/08 Python
简单的抓取淘宝图片的Python爬虫
2014/12/25 Python
Python实现的简单文件传输服务器和客户端
2015/04/08 Python
Python中的Matplotlib模块入门教程
2015/04/15 Python
在python中利用GDAL对tif文件进行读写的方法
2018/11/29 Python
python把ipynb文件转换成pdf文件过程详解
2019/07/09 Python
Python如何将图像音视频等资源文件隐藏在代码中(小技巧)
2020/02/16 Python
Python+Selenium实现自动化的环境搭建的步骤(图文)
2020/09/01 Python
Python unittest如何生成HTMLTestRunner模块
2020/09/08 Python
来自世界各地的饮料:Flavourly
2019/05/06 全球购物
意大利体育用品和运动服网上商店:Maxi Sport
2019/09/14 全球购物
internal修饰符起什么作用
2013/12/16 面试题
家具促销活动方案
2014/02/16 职场文书
离婚财产分配协议书
2014/10/21 职场文书
太空授课观后感
2015/06/17 职场文书
学校趣味运动会开幕词
2016/03/04 职场文书
三好学生竞选稿范文
2019/08/21 职场文书
Python爬虫中urllib3与urllib的区别是什么
2021/07/21 Python
MySQL事务的隔离级别详情
2022/07/15 MySQL