Node 使用事件发射器实现简单的聊天室(广播功能)
该Demo, 使用事件发射器实现了聊天室中的广播功能 (用到了发布/订阅设计模式)
一步一步来从零开始, 实现这个功能
1. 事件发射器
在Node中 事件发射器(EventEmitter
)是一个很重要的概念
事件发射器会触发事件, 并且在那么事件被触发的时候能处理它们
所有能触发事件的对象都是 EventEmitter
类的实例, 可见它多么的重要
创建事件发射器
const events = require('events')
const channel = new events.EventEmitter()
- 注册监听器(触发事件的时候, 会执行回调函数)
channel.on(eventName, callback)
- 触发事件
channel.emit(eventName, args)
2. 创建服务器
我们创建了一个服务器, 并监听了用户进入, 这一步很容易就能够完成
const net = require('net')
channel.on('join', function(id, client) {
console.log(`监听到${id}号用户加入聊天室`)
})
let id = 0
const server = net.createServer((client) => {
const gamer = ++id
console.log(`${gamer}号用户进入聊天室`)
channel.emit('join', gamer, client)
})
server.listen(8888)
前置工作基本完成了, 可以使用win10下的telnet来连接服务器试试看
telnet 127.0.0.1 8888
接下来要考虑多个用户加入的时候, 如何广播消息
3. 广播消息
这一步可能有些跳跃.
使用到了发布/订阅的设计模式, 还有闭包的概念
const events = require('events')
const net = require('net')
const channel = new events.EventEmitter()
// 发布/订阅
channel.clients = {}
channel.subscriptions = {}
channel.on('join', function(id, client) {
console.log(`监听到${id}号玩家加入游戏`)
this.clients[id] = client
this.subscriptions[id] = (senderId, msg) => {
// 忽略发送广播消息的用户
if (id != senderId) {
this.clients[id].write(msg)
console.log(`${id}号玩家收到广播消息${msg}`)
}
}
this.on('broadcast', this.subscriptions[id])
})
// 服务器中定义了两个闭包, id和gamer, 因为我们有两层函数作用域, 一个闭包是不行的
let id = 0
const server = net.createServer((client) => {
const user = ++id
console.log(`玩家${user}进入服务器`)
channel.emit('join', user, client)
client.on('data', (data) => {
console.log(`${user}玩家发送广播消息: ${data.toString()} .`)
channel.emit('broadcast', user, data.toString())
})
})
server.listen(8888)
4. 优化
上面一版的代码会出现问题, 因为我们给每个用户都绑定了监听器, 当用户退出的时候, 监听器没有关闭, 就会出现错误
所以我们要在用户退出的时候, 删去与之对应的监听器
// ...
channel.on('leave', function(id) {
console.log(`${id}号用户已经退出房间`)
channel.removeListener('broadcast', this.subscriptions[id])
})
// ...
let id = 0
const server = net.createServer((client) => {
const user = ++id
console.log(`用户${user}进入服务器`)
channel.emit('join', user, client)
client.on('data', (data) => {
console.log(`${user}用户发送广播消息: ${data.toString()} .`)
channel.emit('broadcast', user, data.toString())
})
client.on('close', () => {
channel.emit('leave', user)
})
})
当客户端关闭连接的时候, 我们发射leave事件, 通知事件发射器删除事件
5. 功能测试
广播功能
退出房间
参考:
Node 文档
Node 实战第二版