这篇文章介绍的是Node.js利用Net模块实现命令行式的多人聊天室,下面话不多说,来看看详细的介绍吧。
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++网络编程差不多的,都是以下步骤。
服务端:
- 创建socket套接字
- 绑定IP和端口
- 启动监听
- 等待客户端连接
- 与客户端进行通信
- 关闭socket
客户端:
- 创建socket套接字
- 连接server服务器
- 与服务器进行通信
- 关闭socket
如下图所示:
2、聊天室的设计和实现
上面学习了NET模块API的使用,接下来便开始实现命令行聊天室,我们不需要做的很复杂,只需实现如下功能即可:
- 用户自定义昵称,不可更改
- 当有新的用户进入聊天室,或者用户离开聊天室,广播给其他用户
- 用户发送信息,需广播给其他用户
- 客户端与服务端建立心跳机制
- 用户输入'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); }
执行结果如下如下:
到此,一个命令行聊天室便做完了。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 群星《青春重置计划9新的凡人歌》[320KMP3][89.41MB]
- 苏曼《苏曼的夜晚HQII》[低速原抓WAV+CUE]
- 【电影原声】汉斯·季默《星际穿越》2014[FLAC+CUE整轨]
- 诹访内晶子AkikoSuwanai巴赫:小提琴协奏曲《J.S.BachViolinConcertos》(flac)
- 王力宏《十八般武艺》[低速原抓WAV+CUE]
- 王力宏《十八般武艺》[低速原抓WAV+CUE]
- 【爵士乐】VA-2024-ParamountJazzLoungeChillingExperience(FLAC)
- 群星《离歌 网剧原声带》[320K/MP3][249.78MB]
- 群星《离歌 网剧原声带》[FLAC/分轨][538.53MB]
- 群星《承欢记 电视剧影视原声带》[320K/MP3][109.99MB]
- 【欧美乡村】JohnnyLee-2024-13thOfJulyandEmotions(FLAC)
- 【流行爵士】PattiAustin-2024-LoveSongs(FLAC)
- 【爵士乐】VA-2024-SupremeLoungeChillSelectionJazzyVibeTunes(FLAC)
- 群星《承欢记 电视剧影视原声带》[FLAC/分轨][219.8MB]
- 群星《乘风2024 第1期》[320K/MP3][110.09MB]