使用唯一 ID 分配 WebSocket 和 net.Socket

Mao*_*zio 4 sockets websocket node.js typescript

我希望为 Websockets 和 net.Sockets 分配唯一标识符,因此当接收到消息时,客户端由附加到套接字的标识符标识。

以前的研究:

对于 Websocket:

根据thisthis,需要以下内容:

const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws) => {
   ws.id = uuid.v4(); // This is the relevant line of code
   ws.on('message', (msg: string) => {
   ...
   }
});
Run Code Online (Sandbox Code Playgroud)

对于 net.Socket:

完全相同 - 根据this,需要以下内容:

var server = net.createServer();

server.on('connection', function(conn) {
    conn.id = uuid.v4(); // This is the relevant line of code
    conn.on('data', function(data) {
    ...
    });
});
Run Code Online (Sandbox Code Playgroud)

问题

编译期间出现错误“属性 'id' 不存在于类型 'WebSocket' [或相应的 'Socket'] 上”解释了原因。

可选解决方案 #1:投射到任何

更新 net.Socket 代码将是:

var server = net.createServer();

server.on('connection', function(conn) {
    (conn as any).id = uuid.v4();
    conn.on('data', function(data) {
        console.log('Session id:' + (conn as any).id);
    });
});
Run Code Online (Sandbox Code Playgroud)

问题:对我来说这似乎不是一个好习惯。除了现在的预感之外,我没有关于原因的参考。

可选解决方案 #2:使用局部变量 [似乎是更好的选择]

更新 net.Socket 代码将是:

var server = net.createServer();

server.on('connection', function(conn) {
    const id: string = uuid.v4();
    conn.on('data', function(data) {
        console.log('Session id:' + id);
    });
});
Run Code Online (Sandbox Code Playgroud)

经过一些体验,它似乎可以正常工作。

所以 - 问题:

  1. 在可选解决方案#2 中:是否保证 id 变量在此范围内始终可用?换句话说:这个解决方案有效吗?对于 net.Socket 和 Websocket?或者有什么我想念的吗?
  2. 一般而言:是否有其他标识符(可能是 WebSocket 和 net.Socket 中的内置标识符)可以替代?

[打字稿版本: 3.2.4]。

Lou*_*uis 5

我不知道已经存在的领域。

如果您可以像在选项 2 中那样将套接字 ID 保留在局部变量中,那么这就是我要做的。我通常更喜欢避免向对象添加任意字段。但是,有时您希望整个应用程序都能够访问额外的数据,在这些情况下,使用局部变量将不起作用。

添加自定义字段

any您可以向项目中添加一个包含以下内容的文件,而不是使用类型断言 to :

declare module "net" {
    interface Socket {
        id: string;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是扩充net.Socket接口以添加id一个字符串字段。这比类型断言更简洁,因为类型断言会让拼写错误(例如(conn as any).ids)。我使用了您的代码,删除了类型断言并将上述内容添加到了一个名为externals.d.ts我放在.ts包含您的代码的文件旁边的文件中。tsc停止抱怨该领域。请注意,您不需要导入此文件或以任何方式引用它。您必须有一个可以tsconfig.json将其与其他源代码一起提取的文件。默认情况下,它会因.d.ts扩展而被拾取。

过去,我使用类型断言和接口增强来向 DOM 节点添加任意字段,并且效果很好。但是,当我这样做时,我使用了非常单一的字段名称,这意味着与可能想要添加自己的字段的其他库发生冲突的可能性非常低。您的字段名称是id. 我会担心与其他库的名称冲突,这些库决定要跟踪套接字并将自己的id字段添加到套接字。

使用 WeakMap

您可以使用另一种方法。您可以设置一个WeakMap将套接字与 id 相关联的。这是一个插图。你可以有一个模块socket-map,它只导出一个将套接字映射到字符串的映射:

import * as net from "net";

export const socketMap = new WeakMap<net.Socket, string>();
Run Code Online (Sandbox Code Playgroud)

然后在获取套接字时使用 id 存储套接字:

import * as net from "net";
import * as uuid from "uuid";
import { socketMap } from "./socket-map";

var server = net.createServer();

server.on('connection', function(conn) {
    const id = uuid.v4();
    socketMap.set(conn, id); // You store the socket into the map.
    conn.on('data', function(data) {
        console.log('Session id:' + (conn as any).id);
    });
});
Run Code Online (Sandbox Code Playgroud)

然后,在另一个模块中,您可以通过以下方式获取 id:

import * as net from "net";
import { socketMap } from "./socket-map";

export function foo(conn: net.Socket) {
  const id = socketMap.get(conn);
}
Run Code Online (Sandbox Code Playgroud)

在这里,我刚刚将套接字与一个 id 字符串相关联,但是您可以在WeakMap. 它可能是一个包含除 id 之外的大量信息的对象。

使用的原因WeakMap是,虽然WeakMap对象的键包含对对象的引用,就垃圾收集而言,这些引用不算数。因此,如果您的应用程序使用套接字完成并且不再在 a 以外的任何地方引用它WeakMap,则存在于 中的引用WeakMap仍将允许垃圾收集器收集套接字。