许多互联网应用依靠实时数据的即时通讯源码系统为用户提供价值,使用Node.js和Socket.io构建IM源码变得如此简单,您不妨现在就学习它。Socket.IO 支持实时、双向和基于事件的通信. 如果可能,它会尝试建立 WebSocket 连接,但如果没有,它将回退到HTTP长轮询。当您考虑在其之上构建某些东西时,这是一个需要考虑的重要区别。
完整源码:im.jstxym.top
他们的网站还列出了充分利用 Socket.io 的应用程序示例,例如将数据推送到客户端的实时分析(如计数器、图表和日志)或即时消息和聊天(如我们将要做的)和文档协作,其中编辑文档的用户可以实时看到其他用户的更改(想想 Google Docs)。人们还可以想象游戏如何利用这项技术来发送和接收多人数据。
将它集成到 Node.js 应用程序中非常容易(他们说它适用于每个平台,但我没有尝试过)。
这是本教程结束时聊天即时通讯系统的外观:
不言而喻,我们需要安装 Node.js,所以如果您仍然没有,请访问他们的网站并至少下载 LTS 版本。
随之而来的是 npm,即节点包管理器。我更喜欢 Yarn(这就是我将在整个教程中使用的),但如果你愿意,可以随意使用 npm。有了它,继续创建一个文件夹来存储应用程序文件。现在,打开您的终端并导航到新创建的文件夹(例如cd realtime-chat)并运行yarn init -y,它将快速创建一个package.json文件,您将能够添加我们需要的唯一依赖项:yarn add socket.io.
现在,我们需要一个 HTML 页面,用户可以在其中使用聊天和 Node.js 服务器。所以,继续创建一个index.html和一个server.js文件。
有了这个,让我们打开package.json并编辑几行。首先,让我们将mainfrom更改index.js为server.js,然后scripts我们可以删除测试脚本并添加"start": "node server.js"它,这将使我们能够yarn start从应用程序的根文件夹运行并启动我们的服务器。package.json的那部分应该如下所示:
“main”: “server.js”,
“scripts”: {
“start”: “node server.js”
}
界面
由于 HTML 不是这里的重点,您可以继续将其复制到您的index.html文件中:
Chat
class="form-input py-3 px-4 block w-full transition ease-in-out duration-150">
class="w-full inline-flex items-center justify-center px-6 py-3 border border-transparent text-base leading-6 font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-500 focus:outline-none focus:border-indigo-700 focus:shadow-outline-indigo active:bg-indigo-700 transition ease-in-out duration-150">
Send message
Who's online:
class="px-6 py-3 max-w-0 w-full whitespace-no-wrap text-sm leading-5 font-medium text-gray-900">
这是聊天应用程序的基本结构。有一个显示所有消息的框,一个输入消息的表单和一个发送消息的按钮。所有重要的部分都有 id,以便我们以后可以在 JavaScript 中检索它们。在这里,我使用TailwindCSS让它看起来很快。
服务器
现在,打开server.js并添加以下内容:
const fs = require('fs');
const http = require('http');
const SocketIO = require('socket.io');
// Prepares HTML file to be served
const content = fs.readFileSync(__dirname + '/index.html', 'utf8');
const httpServer = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
res.setHeader('Content-Length', Buffer.byteLength(content));
res.end(content);
})
// Creates socket.io connection
const io = SocketIO(httpServer);
// Handles "connect" event
io.on('connect', socket => {
// Handles "message" event sent by client
socket.on('message', data => {
// Emits new message to every connected client
io.emit('newMessage', {
message: data
})
})
});
// Starts up server
httpServer.listen(3000, () => {
console.log("🔥 Listening on localhost:3000");
})
这足以满足应用程序的基本功能。我们可以通过使用类似的框架来进一步简化事情express,但现在,经典的 Node.js 服务器就足够了。它提供index.html文件,然后在第 14 行创建一个 Socket.io 连接。然后我们可以使用事件侦听功能来侦听从客户端发出的“连接”事件并处理该连接。您可以创建自己的事件关键字(如“connect”),但您必须记住,有一些关键字不应使用,因为它们与 Socket.io 实现的关键字冲突。一些示例包括连接、断开连接、重新连接和错误。可以在此处找到这些事件名称的完整列表。
在第 16 行,我们监听一个名为“message”的事件并传递一个回调来处理该事件接收到的数据。然后在第 18 行,我们向所有连接的套接字发出一个名为“newMessage”的事件。请注意,我们监听了socket哪个是连接的单个客户端,并且我们使用io哪个是套接字池发出的。你总是可以参考这个emit 备忘单来查看你拥有的所有选项,比如向除了发射器之外的所有连接的套接字发射事件,或者发射到“房间”或从套接字私下发送到套接字。
现在,我想让事情变得更有趣,并为客户端分配随机名称,将这些名称发送给所有客户端,以便他们知道谁已连接并能够聊天。让我们添加这个:
const animals = [
'fish',
'cat',
'tiger',
'bear',
'bull',
'fox'
]
const colors = [
'red',
'green',
'blue',
'yellow',
'purple',
'pink'
]
/**
* Generates a random name based on an animal and a color
*
* @return {String}
*/
function randomName() {
const color = colors[Math.floor(Math.random() * colors.length)]
const animal = animals[Math.floor(Math.random() * animals.length)]
return `${color}-${animal}`;
}
// Stores the names and ids of connected clients
const sockets = {}
// Creates socket.io connection
const io = SocketIO(httpServer);
然后,在“connect”事件处理中,让我们添加一些新的偶数处理程序:
// Handles “connect” event
io.on('connect', socket => {
sockets[socket.id] = randomName();
socket.emit('name-generated', sockets[socket.id]);
io.emit('update-peers', Object.values(sockets));
// Handles “message” event sent by client
socket.on('message', data => {
// Emits new message to every connected client
io.emit('newMessage', {
sender: sockets[socket.id],
message: data
})
});
// Handles “disconnect” event
socket.on('disconnect', () => {
delete sockets[socket.id]
io.emit('update-peers', Object.values(sockets))
})
})
在这里,我们基本上是在等待客户端连接,然后我们为他们的套接字 id 分配一个随机名称并发送他们的“随机名称”,以便他们知道他们是谁。然后我们发送连接的套接字名称列表。我们还需要处理断开连接事件,因此如果有人断开连接,我们会更新连接的套接字列表并将其发送给聊天中的每个人以更新他们的用户界面。
酷,现在让我们实现客户端,以便它可以连接到服务器并发挥它的魔力。
客户端
转到index.html文件并在关闭 body 标记之前,添加以下内容:
这将“导入” Socket.io 脚本(当您构建更复杂的应用程序并使用模块捆绑器时,这可能看起来会有所不同,因为导入将发生在另一个 JavaScript 文件中,但这超出了范围这篇文章)。
让我们通过访问我们将在整个脚本中使用的一些元素来启动程序:
const form = document.getElementById('form');
const input = document.getElementById('input');
const msgBox = document.getElementById('message-box');
const peerList = document.getElementById('peer-list');
现在,为了使用 Socket.io,我们需要调用它并将其存储在一个变量中,然后我们将开始监听和发送事件:
const form = document.getElementById('form');
const input = document.getElementById('input');
const msgBox = document.getElementById('message-box');
const peerList = document.getElementById('peer-list');
const socket = io();
// Handles the "name-generated" event by storing the client's name in a variable
socket.on('name-generated', () => ());
// Handles the "update-peers" event by updating the peers list
socket.on('update-peers', () => ());
// Handles "newMessage" event and add that message to the chat
socket.on('newMessage', () => ());
上面列出的所有事件socket.on()都是由服务器在某个时候发出的,它们仍然没有实现(即我们在监听这些事件之后仍然不做任何事情,但我们很快就会做。在此之前,让我们处理提交消息:
/**
* Retrieves message from input and emits to the server
*
* @param {Object} evt Event fired by the form submission
*/
function submitHandler(evt) {
evt.preventDefault();
socket.emit('message', input.value);
input.value = ''
msgBox.focus();
}
form.addEventListener('submit', submitHandler)
在这里,我们将一个事件侦听器附加到表单。它会监听“提交”事件并submitHandler阻止默认事件(这样表单就不会触发页面重新加载或导航到action属性),然后我们会发出一个包含输入字段值的“消息”事件。然后我们清除字段并专注于不是字段的内容,因此如果用户在移动设备中,键盘就会消失。
现在让我们回到另一个套接字的事件监听器,我们将实现它们。首先,最简单的一个,我们监听“name-generated”事件,如果你还记得的话,这是服务器在为客户端生成随机名称后发出的事件。我们需要存储此名称以在其他函数中使用,因此让我们在与套接字侦听器相同的范围内创建一个变量,如下所示:
let myName = ''
const socket = io();
// Handles the “name-generated” event by storing the client’s name in a variable
socket.on('name-generated', name => {
myName = name
});
现在,让我们处理“newMessage”事件。每当套接字发出“消息”事件时,服务器就会发出此事件。换句话说,有人向服务器发送一条消息,服务器将这条消息广播给所有连接的人:
// Handles “newMessage” event and add that message to the chat
socket.on('newMessage', ({ sender, message }) => {
let name = document.createElement('strong');
name.textContent = `${sender} says: `
let msgEl = document.createElement('span');
msgEl.textContent = message
let paragraph = document.createElement('p');
paragraph.appendChild(name);
paragraph.appendChild(msgEl);
msgBox.appendChild(paragraph);
});
在这里,我们希望服务器发送一个包含消息和发送者姓名的对象。我们使用这些信息来创建一个类似于这样的段落元素:“blue-fish says: I am a new message”。然后将此段落附加到消息框中。
让我们通过实现在线客户端列表来完成这个:
// Handles the “update-peers” event by updating the peers list
socket.on('update-peers', peers => {
peerList.innerHTML = ''
const template = `
%PEER_NAME%
`
for (const peer of peers) {
let name = peer
if (name === myName) {
name += ' (you)'
}
peerList.innerHTML += template.replace('%PEER_NAME%', name).replace('%PEER_COLOR%', peer.split('-')[0])
}
});
这可能看起来有点复杂,但我们只要在监听“update-peers”事件时清除在线对等点列表,然后创建一个 HTML 模板以连接到 DOM,其中包含已连接客户端的名称和颜色,包括您自己(这将使用myName变量来添加一个指示它是你)。
就是这样!现在,如果您yarn start在终端中运行并访问localhost:3000,您应该会看到聊天页面,如果您连接到其他浏览器窗口、选项卡或设备,您将看到越来越多的已连接用户列表。如果你关闭这些窗口,离开聊天,列表也会更新。