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 01 Javascript
javascript中用星号表示预录入内容的实现代码
Jan 08 Javascript
基于javascript 闭包基础分享
Jul 10 Javascript
checkbox全选所涉及到的知识点介绍
Dec 31 Javascript
JavaScript函数参数使用带参数名的方式赋值传入的方法
Mar 19 Javascript
整理Javascript流程控制语句学习笔记
Nov 29 Javascript
jquery自定义表单验证插件
Oct 12 Javascript
Vue.js实现无限加载与分页功能开发
Nov 03 Javascript
vue仿淘宝订单状态的tab切换效果
Jun 23 Javascript
jQuery实现鼠标点击处心形漂浮的炫酷效果示例
Apr 12 jQuery
JavaScript面向对象程序设计创建对象的方法分析
Aug 13 Javascript
Vue基于iview table展示图片实现点击放大
Aug 05 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程序员的技术瓶颈分析
2011/07/17 PHP
php获取文件内容最后一行示例
2014/01/09 PHP
PHP实现采集抓取淘宝网单个商品信息
2015/01/08 PHP
laravel + vue实现的数据统计绘图(今天、7天、30天数据)
2018/07/31 PHP
PHP实现单条sql执行多个数据的insert语句方法
2019/10/11 PHP
yii2.0框架多模型操作示例【添加/修改/删除】
2020/04/13 PHP
jQuery 版元素拖拽原型代码
2011/04/25 Javascript
js中生成map对象的方法
2014/01/09 Javascript
JavaScript设计模式之适配器模式介绍
2014/12/28 Javascript
JavaScript统计网站访问次数的实现代码
2015/11/18 Javascript
BootStrap实现树形目录组件代码详解
2016/06/21 Javascript
JS对象深度克隆实例分析
2017/03/16 Javascript
jQuery实现的form转json经典示例
2017/10/10 jQuery
jquery应用实例分享_实现手风琴特效
2018/02/01 jQuery
Angular4 组件通讯方法大全(推荐)
2018/07/12 Javascript
JS中‘hello’与new String(‘hello’)引出的问题详解
2018/08/14 Javascript
微信小程序获取音频时长与实时获取播放进度问题
2018/08/28 Javascript
python共享引用(多个变量引用)示例代码
2013/12/04 Python
Python生成器(Generator)详解
2015/04/13 Python
详解python中的json的基本使用方法
2016/12/21 Python
Python 实现删除某路径下文件及文件夹的实例讲解
2018/04/24 Python
python3 爬取图片的实例代码
2018/11/06 Python
python 通过麦克风录音 生成wav文件的方法
2019/01/09 Python
在Pycharm中将pyinstaller加入External Tools的方法
2019/01/16 Python
Python中print函数简单使用总结
2019/08/05 Python
python 五子棋如何获得鼠标点击坐标
2019/11/04 Python
selenium框架中driver.close()和driver.quit()关闭浏览器
2020/12/08 Python
HTML5调用手机摄像头拍照的实现思路及代码
2014/06/15 HTML / CSS
财务分析个人的自荐书范文
2013/11/24 职场文书
双方协议书
2014/04/22 职场文书
合作协议书格式
2014/08/19 职场文书
国庆促销活动总结
2014/08/29 职场文书
2014年调度员工作总结
2014/11/19 职场文书
小石潭记导游词
2015/02/03 职场文书
总结一下关于在Java8中使用stream流踩过的一些坑
2021/06/24 Java/Android
vue+iview实现手机号分段输入框
2022/03/25 Vue.js