为WebSocket和net.Socket分配唯一的ID

时间:2019-03-07 09:59:57

标签: node.js typescript sockets websocket

我希望为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) => {
   ...
   }
});

对于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) {
    ...
    });
});

问题

在编译过程中,错误“类型'WebSocket'[或相应地为'Socket']上不存在属性'id'” This解释了原因。

可选解决方案#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);
    });
});

问题:在我看来,这不是好习惯。除了预感之外,我没有其他原因的参考。

可选解决方案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);
    });
});

经过一些尝试后,它似乎可以正常工作。

所以-问题:

  1. 在可选解决方案#2 中:是否可以确保id变量在此范围内始终可用?换句话说:此解决方案有效吗?对于net.Socket和Websocket?还是我想念的东西?
  2. 一般:是否可以使用其他标识符(也许在WebSocket和net.Socket中内置标识符)?

[打字稿版本: 3.2.4]。

1 个答案:

答案 0 :(得分:4)

我不知道该字段已经存在。

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

添加自定义字段

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

declare module "net" {
    interface Socket {
        id: string;
    }
}

这正在扩展net.Socket接口,以添加一个id字段,它是一个字符串。 这比类型声明要整洁,因为类型声明可以使拼写错误通过(例如(conn as any).ids)。我使用了您的代码,删除了类型声明并将其添加到文件中我放在包含您的代码的externals.d.ts文件旁边的名为.ts的文件中。 tsc不再抱怨该领域。请注意,您不需要导入此文件或以任何方式引用它。您只需要有一个tsconfig.json即可将其与您的其他源代码一起拾取。默认情况下,由于扩展名.d.ts而被选择。

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

使用WeakMap

您可以使用另一种方法。您可以设置将套接字与ID关联的WeakMap。这是一个例子。您可能有一个模块socket-map,该模块仅导出将套接字映射到字符串的映射:

import * as net from "net";

export const socketMap = new WeakMap<net.Socket, string>();

然后在获取套接字时,将套接字与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);
    });
});

然后,在另一个模块中,您可以使用以下方式获取ID:

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

export function foo(conn: net.Socket) {
  const id = socketMap.get(conn);
}

在这里,我刚刚将套接字与id字符串相关联,但是您可以在WeakMap的值中具有所需的任何结构。它可能是一个包含ID之外的信息的对象。

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