Skip to main content

私有消息 - 第三部分

¥Private messaging - Part III

本指南分为四个不同的部分:

¥This guide has four distinct parts:

这是 第二部分 结束时的情况:

¥Here's where we were at the end of the 2nd part:

Chat (v2)

一切都运行得很好,但最后一个问题非常烦人:

¥All is working pretty well, but there is a last issue which is quite annoying:

  • 当发送方断开连接时,它发送的所有数据包都是 buffered,直到重新连接(在这种情况下这很好)

    ¥when the sender gets disconnected, all the packets it sends are buffered until reconnection (which is great in this case)

Chat with sender that gets disconnected
  • 但是当接收者断开连接时,数据包就会丢失,因为给定房间中没有监听 Socket 实例

    ¥but when the recipient gets disconnected, the packets are lost, since there is no listening Socket instance in the given room

Chat with recipient that gets disconnected

这个问题有多种解决方案,我们将选择最容易实现的:将所有消息存储在服务器端。

¥There are multiple solutions to this problem, and we will go for the easiest to implement: store all the messages on the server-side.

注意:第三部分很简短,但它强调了 Socket.IO 的一个重要属性:你不能依赖连接的状态。大多数情况下它应该是正常的,但是有很多事情可以终止 TCP 连接(在移动浏览器上尤其如此)。

¥Note: this 3rd part will be brief, but it underlines an important property of Socket.IO: you cannot rely on the status of the connection. It should be up most of the time but there is a myriad of things that can kill a TCP connection (this is particularly true on mobile browsers).

安装

¥Installation

让我们检查一下第三部分的分支:

¥Let's checkout the branch for part III:

git checkout examples/private-messaging-part-3

你应该在当前目录中看到以下内容:

¥Here's what you should see in the current directory:

├── babel.config.js
├── package.json
├── public
│ ├── favicon.ico
│ ├── fonts
│ │ └── Lato-Regular.ttf
│ └── index.html
├── README.md
├── server
│ ├── index.js (updated)
│ ├── messageStore.js (created)
│ ├── package.json
│ └── sessionStore.js
└── src
├── App.vue
├── components
│ ├── Chat.vue (updated)
│ ├── MessagePanel.vue
│ ├── SelectUsername.vue
│ ├── StatusIcon.vue
│ └── User.vue
├── main.js
└── socket.js

完整的差异可以在 此处 中找到。

¥The complete diff can be found here.

工作原理

¥How it works

持久消息

¥Persistent messages

在服务器端 (server/index.js),我们现在将消息保留在新存储中:

¥On the server-side (server/index.js), we now persist the message in our new store:

io.on("connection", (socket) => {
// ...
socket.on("private message", ({ content, to }) => {
const message = {
content,
from: socket.userID,
to,
};
socket.to(to).to(socket.userID).emit("private message", message);
messageStore.saveMessage(message);
});
// ...
});

我们在连接时获取消息列表:

¥And we fetch the list of messages upon connection:

io.on("connection", (socket) => {
// ...
const users = [];
const messagesPerUser = new Map();
messageStore.findMessagesForUser(socket.userID).forEach((message) => {
const { from, to } = message;
const otherUser = socket.userID === from ? to : from;
if (messagesPerUser.has(otherUser)) {
messagesPerUser.get(otherUser).push(message);
} else {
messagesPerUser.set(otherUser, [message]);
}
});
sessionStore.findAllSessions().forEach((session) => {
users.push({
userID: session.userID,
username: session.username,
connected: session.connected,
messages: messagesPerUser.get(session.userID) || [],
});
});
socket.emit("users", users);
// ...
});

代码非常简单。我们不应该再在断开连接时丢失消息:

¥The code is quite straightforward. We shouldn't lose messages upon disconnection anymore:

Chat (v3)

审查

¥Review

现在我们已经进行了功能聊天,我们将在本指南的 第四部分 中了解如何扩展到多个 Socket.IO 服务器。

¥Now that we have a functional chat, we will see in the 4th part of this guide how to scale to multiple Socket.IO servers.

谢谢阅读!

¥Thanks for reading!