|
|
@@ -0,0 +1,120 @@
|
|
|
+'use strict';
|
|
|
+
|
|
|
+/**
|
|
|
+ * 2个字节是16位(二进制),共能表示2^16=65536个数。但由于数是有符号的,最高位用作符号位,所以只有低15位表示绝对值。
|
|
|
+ * 整型在内存中以补码存储,能表示的最大正整数是2^15-1=32767。
|
|
|
+ */
|
|
|
+
|
|
|
+const net = require('node:net');
|
|
|
+const { Stick, MaxBodyLen } = require('@lvgithub/stick');
|
|
|
+
|
|
|
+const stick = new Stick(1024).setReadIntBE('32');
|
|
|
+stick.setMaxBodyLen(MaxBodyLen['2048M']);
|
|
|
+
|
|
|
+const LOCAL_PORT = 1936;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 头长度(Byte)
|
|
|
+ */
|
|
|
+const HEAD_LEN = 4;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 标识长度(Byte)
|
|
|
+ */
|
|
|
+const IDENTIFIER_LEN = 2;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 媒体服务器映射
|
|
|
+ */
|
|
|
+const MEDIA_SERVER_MAP = new Map([
|
|
|
+ ['9999', { port: 1935, host: '192.168.66.73' }],
|
|
|
+]);
|
|
|
+
|
|
|
+/**
|
|
|
+ * 媒体服务器连接池
|
|
|
+ */
|
|
|
+const MEDIA_SERVER_POOLS = new Map();
|
|
|
+
|
|
|
+/**
|
|
|
+ * 初始化多媒体服务器连接池
|
|
|
+ */
|
|
|
+for (let [key, value] of MEDIA_SERVER_MAP) {
|
|
|
+ const socket = net.createConnection({ host: value.host, port: value.port });
|
|
|
+ MEDIA_SERVER_POOLS.set(key, socket);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 关闭所有目标服务器连接
|
|
|
+ */
|
|
|
+const closeAllTargetSocket = function () {
|
|
|
+ for (let [, value] of MEDIA_SERVER_POOLS) {
|
|
|
+ value.end();
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+const proxy = net.createServer(function (socket) {
|
|
|
+ socket.on('data', function (data) {
|
|
|
+ stick.putData(data);
|
|
|
+ });
|
|
|
+
|
|
|
+ stick.onData(function (data) {
|
|
|
+ const totalHeadLen = HEAD_LEN + IDENTIFIER_LEN;
|
|
|
+
|
|
|
+ // 解析包头长度
|
|
|
+ const head = Buffer.alloc(HEAD_LEN);
|
|
|
+ data.copy(head, 0, 0, HEAD_LEN);
|
|
|
+ const dataLen = head.readInt32BE() - IDENTIFIER_LEN;
|
|
|
+
|
|
|
+ // 解析标识
|
|
|
+ const identifier = Buffer.alloc(IDENTIFIER_LEN);
|
|
|
+ data.copy(identifier, 0, HEAD_LEN, totalHeadLen);
|
|
|
+
|
|
|
+ // 解析数据包内容
|
|
|
+ const body = Buffer.alloc(dataLen);
|
|
|
+ data.copy(body, 0, totalHeadLen, head.readInt32BE() + HEAD_LEN);
|
|
|
+
|
|
|
+ const mediaId = identifier.readInt16BE() + '';
|
|
|
+ console.log('data length: ' + dataLen);
|
|
|
+ console.log('identifier content: ' + mediaId);
|
|
|
+ // console.log('body content: ' + body.toString('hex'));
|
|
|
+
|
|
|
+ if (MEDIA_SERVER_POOLS.has(mediaId)) {
|
|
|
+ const mediaServer = MEDIA_SERVER_MAP.get(mediaId);
|
|
|
+ console.log('matched media server: %s:%s', mediaServer.host, mediaServer.port);
|
|
|
+ const targetSocket = MEDIA_SERVER_POOLS.get(mediaId);
|
|
|
+ /** 测试代码START: 正式环境为一个代理网关对应多个媒体服务 */
|
|
|
+ if (targetSocket.listeners('data').length === 0) {
|
|
|
+ targetSocket.on('data', function (chunk) {
|
|
|
+ // TODO 向客户端回写需要实现推送一样的逻辑
|
|
|
+ socket.write(chunk);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (targetSocket.listeners('close').length === 0) {
|
|
|
+ targetSocket.on('close', function () {
|
|
|
+ socket.end();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ /** 测试代码END */
|
|
|
+ targetSocket.write(body);
|
|
|
+ } else {
|
|
|
+ console.log('not match media server');
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ socket.on('close', function () {
|
|
|
+ closeAllTargetSocket();
|
|
|
+ console.log('client disconnected');
|
|
|
+ });
|
|
|
+
|
|
|
+ socket.on('error', function (error) {
|
|
|
+ console.log(`error:客户端异常断开: ${error}`);
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+proxy.on('error', function (err) {
|
|
|
+ throw err;
|
|
|
+});
|
|
|
+
|
|
|
+proxy.listen(LOCAL_PORT, function () {
|
|
|
+ console.log('Zone2 proxy listening on ' + LOCAL_PORT);
|
|
|
+});
|