1、net模块基本API

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

Server类如下图所示:



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

Socket类如下:



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

工厂方法如下:



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

服务端:

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

客户端:

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

如下图所示:

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){
client = socket;
quit(socket.nick);
return;
}
if(socket.writable){
isOnline = !1;
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模块实现一个命令行多人聊天室的更多相关文章

  1. node.js中使用yargs来处理命令行参数

    yargs库能够方便的处理命令行参数. 一.安装 yargs npm install yargs --save 二.读取命令行参数 const yargs = require('yargs'); le ...

  2. Node.js实战5:操作系统与命令行。

    Nodejs有一些内置的方法可以查询操作系统信息: 如: process.arch获取到系统是32位还是64位, process.platform可获取系统的类型. 例程: console.log(p ...

  3. 在nginx服务器里面搭建好node.js本地服务器后,利用Node.js的FS模块,实现简单数据的写入和读取

    先在server.js里面引入: var fs = require('fs');   然后写入  // 往writeme.txt文件 写入一些内容     fs.writeFile('./writem ...

  4. 【Node.js】利用node.js搭建服务器并访问静态网页

    node.js是一门服务端的语言,下面讲讲如何利用node.js提供给我们的api来搭建服务器,并且访问静态网页 项目结构如下 ------------------------------------ ...

  5. 浅析 Node.js 的 vm 模块以及运行不信任代码

    在一些系统中,我们希望给用户提供插入自定义逻辑的能力,除了 RPC 和 REST 之外,运行客户提供的代码也是比较常用的方法,好处是可以极大地减少在网络上的耗时.JavaScript 是一种非常流行而 ...

  6. Node.js的Formidable模块的使用

    今天总结了下Node.js的Formidable模块的使用,下面做一些简要的说明. 1)     创建Formidable.IncomingForm对象 var form = new formidab ...

  7. Node.js 教程 04 - 模块系统

    前言: Node.js的模块系统类似于C/C++的文件引用,可以声明对象,也可以定义类 创建对象. 大家这么理解,就简单了. 定义: 为了让Node.js的文件可以相互调用,Node.js提供了一个简 ...

  8. Node.js的cluster模块——Web后端多进程服务

    众所周知,Node.js是单线程的,一个单独的Node.js进程无法充分利用多核.Node.js从v0.6.0开始,新增cluster模块,让Node.js开发Web服务时,很方便的做到充分利用多核机 ...

  9. Node.js入门:模块机制

    CommonJS规范      早在Netscape诞生不久后,JavaScript就一直在探索本地编程的路,Rhino是其代表产物.无奈那时服务端JavaScript走的路均是参考众多服务器端语言来 ...

随机推荐

  1. 对抗假人 —— 前后端结合的 WAF

    前言 之前介绍了一些前后端结合的中间人攻击方案.由于 Web 程序的特殊性,前端脚本的参与能大幅弥补后端的不足,从而达到传统难以实现的效果. 攻防本为一体,既然能用于攻击,类似的思路同样也可用于防御. ...

  2. .NET Core 首例 Office 开源跨平台组件(NPOI Core)

    前言 最近项目中,需要使用到 Excel 导出,找了一圈发现没有适用于 .NET Core的,不依赖Office和操作系统限制的 Office 组件,于是萌生了把 NPOI 适配并移植到 .NET C ...

  3. 【前端性能】高性能滚动 scroll 及页面渲染优化

    最近在研究页面渲染及web动画的性能问题,以及拜读<CSS SECRET>(CSS揭秘)这本大作. 本文主要想谈谈页面优化之滚动优化. 主要内容包括了为何需要优化滚动事件,滚动与页面渲染的 ...

  4. iframe用法

    <iframe src="http://caiyanli.top/" height="500"  width="500" frameb ...

  5. jsp中出现onclick函数提示Cannot return from outside a function or method

    在使用Myeclipse10部署完项目后,原先不出错的项目,会有红色的叉叉,JSP页面会提示onclick函数错误 Cannot return from outside a function or m ...

  6. Js 原型和原型链

    Js中通过原型和原型链实现了继承 Js对象属性的访问,首先会查找自身是否拥有这个属性 如果查到,则返回属性值,如果找不到,就会遍历原型链,一层一层的查找,如果找到就会返回属性值 直到遍历完Object ...

  7. Win10提示没有权限使用网络资源问题解决

    借鉴链接:http://www.cr173.com/html/67361_1.html Win10提示没有权限使用网络资源解决方法 1.打开控制面板; 2.在所有控制面板项中找到凭据管理器; 3.添加 ...

  8. iOS从零开始学习直播之3.美颜

      任何一款直播软件都必须进行美颜,不然哪来的那么多美女,所以技术改变世界,不只是说说而已.美颜在采集的时候就得就行,让主播实时看到直播的效果. 1.美颜原理   其实美颜的本质就是美白和磨皮,分别通 ...

  9. Atitit  godaddy 文件权限 root权限设置

    Atitit  godaddy 文件权限 root权限设置 1. ubuntu需要先登录,再su切换到root1 2. sudo 授权许可使用的su,也是受限制的su1 3. ubuntu默认吗roo ...

  10. myrocks复制中断问题排查

    背景 mysql可以支持多种不同的存储引擎,innodb由于其高效的读写性能,并且支持事务特性,使得它成为mysql存储引擎的代名词,使用非常广泛.随着SSD逐渐普及,硬件存储成本越来越高,面向写优化 ...