使用Node.js的Readline和Socket.io实现实时聊天

wufei123 2024-06-02 阅读:10 评论:0
Node.js 在其标准库中有一个未被充分重视的模块,但却非常有用。 Readline 模块按照包装盒上的说明执行操作:从终端读取一行输入。这可用于询问用户一两个问题,或在屏幕底部创建提示。在本教程中,我打算展示 Readline 的功...

使用node.js的readline和socket.io实现实时聊天

Node.js 在其标准库中有一个未被充分重视的模块,但却非常有用。 Readline 模块按照包装盒上的说明执行操作:从终端读取一行输入。这可用于询问用户一两个问题,或在屏幕底部创建提示。在本教程中,我打算展示 Readline 的功能并制作一个由 Socket.io 支持的实时 CLI 聊天室。客户端不仅可以发送简单的消息,还可以使用 /me 发送表情命令,使用 /msg 发送私人消息,并允许使用 /nick。

关于 Readline 的一点

这可能是 Readline 最简单的用法:

var readline = require('readline'); var rl = readline.createInterface(process.stdin, process.stdout); rl.question("What is your name? ", function(answer) { console.log("Hello, " + answer ); rl.close(); });

我们包含该模块,使用标准输入和输出流创建 Readline 接口,然后向用户询问一个一次性问题。这是Readline的第一个用法:提问。如果您需要与用户确认某些内容,可能采用流行的形式,“您想这样做吗?(y/n)”,这种形式普遍存在于 CLI 工具中,readline.question () 就是这样做的方法。

Readline 提供的另一个功能是提示,可以根据其默认的“>”字符进行自定义,并暂时暂停以防止输入。对于我们的 Readline 聊天客户端,这将是我们的主要界面。将会出现一次 readline.question() 来询问用户昵称,但其他所有内容都将是 readline.prompt()。

管理您的依赖项

让我们从无聊的部分开始:依赖关系。该项目将使用 socket.io、socket.io-client 包和 ansi-color。您的 packages.json 文件应如下所示:

{ "name": "ReadlineChatExample", "version": "1.0.0", "description": "CLI chat with readline and socket.io", "author": "Matt Harzewski", "dependencies": { "socket.io": "latest", "socket.io-client": "latest", "ansi-color": "latest" }, "private": true }

运行 npm install 就可以了。

服务器

在本教程中,我们将使用一个非常简单的 Socket.io 服务器。没有比这更基本的了:

var socketio = require('socket.io'); // Listen on port 3636 var io = socketio.listen(3636); io.sockets.on('connection', function (socket) { // Broadcast a user's message to everyone else in the room socket.on('send', function (data) { io.sockets.emit('message', data); }); });

它所做的只是从一个客户端接收传入消息并将其传递给其他所有人。对于更大规模的应用程序来说,服务器可能会更加健壮,但对于这个简单的示例来说,它应该足够了。

这应该保存在项目目录中,名称为 server.js。

客户端:包括和设置

在我们开始有趣的部分之前,我们需要包含我们的依赖项,定义一些变量,并启动 Readline 接口和套接字连接。

var readline = require('readline'), socketio = require('socket.io-client'), util = require('util'), color = require("ansi-color").set; var nick; var socket = socketio.connect('localhost', { port: 3636 }); var rl = readline.createInterface(process.stdin, process.stdout);

此时,代码几乎是不言自明的。我们已经有了昵称变量、套接字连接(通过 socket.io-client 包)和 Readline 接口。

在此示例中,Socket.io 将通过端口 3636 连接到本地主机,当然,如果您正在制作生产聊天应用程序,这将更改为您自己的服务器的域和端口。 (与自己聊天没有多大意义!)

客户端:询问用户名

现在我们第一次使用 Readline!我们想询问用户选择的昵称,这将在聊天室中识别他们。为此,我们将使用 Readline 的 question() 方法。

// Set the username rl.question("Please enter a nickname: ", function(name) { nick = name; var msg = nick + " has joined the chat"; socket.emit('send', { type: 'notice', message: msg }); rl.prompt(true); });

我们将之前的 nick 变量设置为从用户收集的值,向服务器发送一条消息(该消息将转发到其他客户端)我们的用户已加入聊天,然后将 Readline 界面切换回提示模式。传递给 prompt() 的 true 值可确保正确显示提示符。 (否则光标可能会移动到该行的零位置,并且不会显示“>”。)

不幸的是,Readline 在 prompt() 方法方面存在令人沮丧的问题。它与 console.log() 配合得不太好,它会将文本输出到与提示字符相同的行,从而在各处留下杂散的“>”字符和其他字符怪异。为了解决这个问题,我们不会在此应用程序中的任何位置使用 console.log,仅保留一个位置。相反,输出应该传递给此函数:

function console_out(msg) { process.stdout.clearLine(); process.stdout.cursorTo(0); console.log(msg); rl.prompt(true); }

这个稍微的hacky解决方案可确保控制台中的当前行为空,并且在打印输出之前光标位于零位置。然后它明确要求再次输出提示。

因此,在本教程的其余部分中,您将看到 console_out() 而不是 console.log()。

客户端:处理输入

用户可以输入两种类型的输入:聊天和命令。我们知道命令前面有斜杠,因此很容易区分两者。

Readline 有几个事件处理程序,但最重要的无疑是 line。每当在输入流中检测到换行符(通过 return 或 Enter 键)时,就会触发此事件。因此,我们需要为我们的输入处理程序挂钩 line。

rl.on('line', function (line) { if (line[0] == "/" && line.length > 1) { var cmd = line.match(/[a-z]+\b/)[0]; var arg = line.substr(cmd.length+2, line.length); chat_command(cmd, arg); } else { // send chat message socket.emit('send', { type: 'chat', message: line, nick: nick }); rl.prompt(true); } });

如果输入行的第一个字符是斜杠,我们就知道这是一个命令,这将需要更多的处理。否则,我们只是发送常规聊天消息并重置提示。请注意此处通过套接字发送的数据与上一步中的加入消息之间的差异。它使用不同的 type,因此接收客户端知道如何格式化消息,并且我们还传递 nick 变量。

命令名称(cmd)和后面的文本(arg)用一些正则表达式和子字符串魔术隔离,然后我们将它们传递给处理函数命令。

function chat_command(cmd, arg) { switch (cmd) { case 'nick': var notice = nick + " changed their name to " + arg; nick = arg; socket.emit('send', { type: 'notice', message: notice }); break; case 'msg': var to = arg.match(/[a-z]+\b/)[0]; var message = arg.substr(to.length, arg.length); socket.emit('send', { type: 'tell', message: message, to: to, from: nick }); break; case 'me': var emote = nick + " " + arg; socket.emit('send', { type: 'emote', message: emote }); break; default: console_out("That is not a valid command."); } }

如果用户输入 /nick gollum,则 nick 变量将重置为 gollum,它可能是 smeagol 之前,并将通知推送到服务器。

如果用户输入 /msg bilbo 珍贵在哪里?,使用相同的正则表达式来分隔接收者和消息,然后是一个类型为 tell 被推送到服务器。这将与普通消息的显示方式略有不同,并且其他用户不应该看到。诚然,我们过于简单的服务器会盲目地将消息推送给每个人,但客户端会忽略未发送到正确昵称的通知。更强大的服务器可以更加离散。

表情命令的使用形式为/我正在吃第二顿早餐。昵称以任何使用过 IRC 或玩过多人角色扮演游戏的人都应该熟悉的方式添加到表情符号中,然后将其推送到服务器。

客户端:处理传入消息

现在客户端需要一种接收消息的方法。我们需要做的就是挂钩 Socket.io 客户端的 message 事件并适当地格式化数据以进行输出。

socket.on('message', function (data) { var leader; if (data.type == 'chat' && data.nick != nick) { leader = color("<"+data.nick+"> ", "green"); console_out(leader + data.message); } else if (data.type == "notice") { console_out(color(data.message, 'cyan')); } else if (data.type == "tell" && data.to == nick) { leader = color("["+data.from+"->"+data.to+"]", "red"); console_out(leader + data.message); } else if (data.type == "emote") { console_out(color(data.message, "cyan")); } });

客户端使用我们的昵称发送的类型为 chat 的消息将与昵称和聊天文本一起显示。用户已经可以看到他们在 Readline 中输入的内容,因此没有必要再次输出它。在这里,我使用 ansi-color 包对输出进行一点着色。这并不是绝对必要的,但它使聊天更容易理解。

类型为 notice 或 emote 的消息按原样打印,但颜色为青色。

如果消息是 tell,且昵称等于此客户端的当前名​​称,则输出采用 [Somebody->You] Hi! 的形式。当然,这并不是非常私密的事情。如果您想查看每个人的消息,您只需取出 && data.to == nick 部分即可。理想情况下,服务器应该知道将消息推送到哪个客户端,而不是将其发送给不需要它的客户端。但这增加了不必要的复杂性,超出了本教程的范围。

点燃它!

现在让我们看看是否一切正常。要对其进行测试,请通过运行 node server.js 启动服务器,然后打开几个新的终端窗口。在新窗口中,运行 node client.js 并输入昵称。假设一切顺利,那么您应该能够在他们之间聊天。

希望本教程向您展示了 Readline 模块的入门是多么容易。您可能想尝试向聊天应用程序添加更多功能,以进行更多练习。最后,查看 Readline 文档以获取完整的 API。

以上就是使用Node.js的Readline和Socket.io实现实时聊天的详细内容,更多请关注知识资源分享宝库其它相关文章!

版权声明

本站内容来源于互联网搬运,
仅限用于小范围内传播学习,请在下载后24小时内删除,
如果有侵权内容、不妥之处,请第一时间联系我们删除。敬请谅解!
E-mail:dpw1001@163.com

分享:

扫一扫在手机阅读、分享本文

发表评论
热门文章
  • 华为 Mate 70 性能重回第一梯队 iPhone 16 最后一块遮羞布被掀

    华为 Mate 70 性能重回第一梯队 iPhone 16 最后一块遮羞布被掀
    华为 mate 70 或将首发麒麟新款处理器,并将此前有博主爆料其性能跑分将突破110万,这意味着 mate 70 性能将重新夺回第一梯队。也因此,苹果 iphone 16 唯一能有一战之力的性能,也要被 mate 70 拉近不少了。 据悉,华为 Mate 70 性能会大幅提升,并且销量相比 Mate 60 预计增长40% - 50%,且备货充足。如果 iPhone 16 发售日期与 Mate 70 重合,销量很可能被瞬间抢购。 不过,iPhone 16 还有一个阵地暂时难...
  • 酷凛 ID-COOLING 推出霜界 240/360 一体水冷散热器,239/279 元

    酷凛 ID-COOLING 推出霜界 240/360 一体水冷散热器,239/279 元
    本站 5 月 16 日消息,酷凛 id-cooling 近日推出霜界 240/360 一体式水冷散热器,采用黑色无光低调设计,分别定价 239/279 元。 本站整理霜界 240/360 散热器规格如下: 酷凛宣称这两款水冷散热器搭载“自研新 V7 水泵”,采用三相六极马达和改进的铜底方案,缩短了水流路径,相较上代水泵进一步提升解热能力。 霜界 240/360 散热器的水泵为定速 2800 RPM 设计,噪声 28db (A)。 两款一体式水冷散热器采用 27mm 厚冷排,...
  • 惠普新款战 99 笔记本 5 月 20 日开售:酷睿 Ultra / 锐龙 8040,4999 元起

    惠普新款战 99 笔记本 5 月 20 日开售:酷睿 Ultra / 锐龙 8040,4999 元起
    本站 5 月 14 日消息,继上线官网后,新款惠普战 99 商用笔记本现已上架,搭载酷睿 ultra / 锐龙 8040处理器,最高可选英伟达rtx 3000 ada 独立显卡,售价 4999 元起。 战 99 锐龙版 R7-8845HS / 16GB / 1TB:4999 元 R7-8845HS / 32GB / 1TB:5299 元 R7-8845HS / RTX 4050 / 32GB / 1TB:7299 元 R7 Pro-8845HS / RTX 2000 Ada...
  • python中def什么意思

    python中def什么意思
    python 中,def 关键字用于定义函数,这些函数是代码块,执行特定任务。函数语法为 def (参数列表)。函数可以通过其名字和圆括号调用。函数可以接受参数作为输入,并在函数体中使用参数名访问。函数可以使用 return 语句返回一个值,它将成为函数调用的结果。 Python 中 def 关键字 在 Python 中,def 关键字用于定义函数。函数是代码块,旨在执行特定任务。 语法 def 函数定义的语法如下: def (参数列表): # 函数体 示例 定义...
  • python中int函数的用法

    python中int函数的用法
    int() 函数将值转换为整数,支持多种类型(字符串、字节、浮点数),默认进制为 10。可以指定进制数范围在 2-36。int() 返回 int 类型的转换结果,丢弃小数点。例如,将字符串 "42" 转换为整数为 42,将浮点数 3.14 转换为整数为 3。 Python 中的 int() 函数 int() 函数用于将各种类型的值转换为整数。它接受任何可以解释为整数的值作为输入,包括字符串、字节、浮点数和十六进制表示。 用法 int(object, base=10) 其中...