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 相关文章推荐
js刷新框架子页面的七种方法代码
Nov 20 Javascript
比较全面的event对像在IE与FF中的区别 推荐
Sep 21 Javascript
基于jQuery实现的百度导航li拖放排列效果,即时更新数据库
Jul 31 Javascript
JS实现侧悬浮浮动实例代码
Nov 29 Javascript
jQuery简易图片放大特效示例代码
Jun 09 Javascript
浅析2种JavaScript继承方式
Dec 04 Javascript
BootStrap中的表单大全
Sep 07 Javascript
浅谈Node.js轻量级Web框架Express4.x使用指南
May 03 Javascript
gulp安装以及打包合并的方法教程
Nov 19 Javascript
vue路由导航守卫和请求拦截以及基于node的token认证的方法
Apr 07 Javascript
微信小程序收货地址API兼容低版本解决方法
May 18 Javascript
详解 javascript对象创建模式
Oct 30 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
点评山进PR-D3L三波段收音机
2021/03/02 无线电
php使用pdo连接sqlite3的配置示例
2016/05/27 PHP
PHP文件上传操作实例详解
2016/09/27 PHP
PHP 数组操作详解【遍历、指针、函数等】
2020/05/13 PHP
在IE下获取object(ActiveX)的Param的代码
2009/09/15 Javascript
javascript innerText和innerHtml应用
2010/01/28 Javascript
Ajax异步提交表单数据的说明及方法实例
2013/06/22 Javascript
jQueryMobile之Helloworld与页面切换的方法
2015/02/04 Javascript
jQuery实现新消息闪烁标题提示的方法
2015/03/11 Javascript
JavaScript制作淘宝星级评分效果的思路
2020/06/23 Javascript
Web打印解决方案之普通报表打印功能
2016/08/29 Javascript
js阻止冒泡和默认事件(默认行为)详解
2016/10/20 Javascript
Node.js复制文件的方法示例
2016/12/29 Javascript
echarts3 使用总结(绘制各种图表,地图)
2017/01/05 Javascript
js如何获取网页所有图片
2017/05/12 Javascript
基于vue.js快速搭建图书管理平台
2017/10/29 Javascript
jquery动态添加以及遍历option并获取特定样式名称的option方法
2018/01/29 jQuery
ES6 Generator函数的应用实例分析
2019/06/26 Javascript
vue计算属性无法监听到数组内部变化的解决方案
2019/11/06 Javascript
jquery制作的移动端购物车效果完整示例
2020/02/24 jQuery
从0搭建vue-cli4脚手架
2020/06/17 Javascript
[01:52]深扒TI7聊天轮盘语音出处7
2017/05/11 DOTA
python共享引用(多个变量引用)示例代码
2013/12/04 Python
Python聊天室实例程序分享
2016/01/05 Python
利用Python代码实现数据可视化的5种方法详解
2018/03/25 Python
pip安装时ReadTimeoutError的解决方法
2018/06/12 Python
简单了解python的一些位运算技巧
2019/07/13 Python
python实现猜拳小游戏
2020/04/05 Python
基于Python绘制美观动态圆环图、饼图
2020/06/03 Python
浅谈keras使用中val_acc和acc值不同步的思考
2020/06/18 Python
解决python 虚拟环境删除包无法加载的问题
2020/07/13 Python
HTML5地理定位与第三方工具百度地图的应用
2016/11/17 HTML / CSS
医学护理系毕业生求职信
2013/10/01 职场文书
百家讲坛观后感
2015/06/12 职场文书
土木工程生产实习心得体会
2016/01/22 职场文书
web前端之css水平居中代码解析
2021/05/20 HTML / CSS