|
|
@ -1,40 +1,54 @@ |
|
|
const net = require('net'); |
|
|
const net = require('net'); |
|
|
const WebSocket = require('ws'); |
|
|
const WebSocket = require('ws'); |
|
|
|
|
|
|
|
|
// TCP代理配置
|
|
|
// TCP代理配置
|
|
|
let TCP_HOST = '127.0.0.1'; // 因为下位机和上位机在同一台机器上,所以使用localhost
|
|
|
let TCP_HOST = '127.0.0.1'; |
|
|
const TCP_PORT = 2230; |
|
|
const TCP_PORT = 2230; |
|
|
// 创建独立的WebSocket服务器用于TCP代理
|
|
|
|
|
|
function setupTcpProxy(conf) { |
|
|
function setupTcpProxy(conf) { |
|
|
if (conf && conf.flag === 'localdev') { |
|
|
if (conf && conf.flag === 'localdev') { |
|
|
TCP_HOST = '10.8.30.179' //本地开发配置
|
|
|
TCP_HOST = '10.8.30.179' |
|
|
} |
|
|
} |
|
|
console.log(`TCP代理目标地址: ${TCP_HOST}:${TCP_PORT}`); |
|
|
console.log(`TCP代理目标地址: ${TCP_HOST}:${TCP_PORT}`); |
|
|
// 从配置中获取端口,如果没有则使用默认端口
|
|
|
|
|
|
const wsPort = (conf && conf.port) ? Number(conf.port) + 1 : 5001; // WebSocket端口比HTTP端口大1
|
|
|
|
|
|
|
|
|
|
|
|
// console.log(`准备在端口 ${wsPort} 启动WebSocket服务器`);
|
|
|
const wsPort = (conf && conf.port) ? Number(conf.port) + 1 : 5001; |
|
|
|
|
|
|
|
|
const wss = new WebSocket.Server({ |
|
|
const wss = new WebSocket.Server({ |
|
|
port: wsPort, |
|
|
port: wsPort, |
|
|
host: '0.0.0.0', // 监听所有网络接口,允许局域网访问
|
|
|
host: '0.0.0.0', |
|
|
path: '/tcp-proxy' |
|
|
path: '/tcp-proxy' |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
wss.on('connection', (ws, request) => { |
|
|
wss.on('connection', (ws, request) => { |
|
|
// console.log(`WebSocket连接建立,来自: ${request.socket.remoteAddress}`);
|
|
|
console.log(`WebSocket连接建立,来自: ${request.socket.remoteAddress}`); |
|
|
|
|
|
|
|
|
// 创建TCP连接
|
|
|
|
|
|
const tcpClient = new net.Socket(); |
|
|
const tcpClient = new net.Socket(); |
|
|
// TCP数据缓冲区
|
|
|
|
|
|
let tcpDataBuffer = ''; |
|
|
let tcpDataBuffer = ''; |
|
|
let tcpConnected = false; // 新增:TCP连接状态标志
|
|
|
let tcpConnected = false; |
|
|
|
|
|
let connectionTimeout = null; |
|
|
|
|
|
|
|
|
|
|
|
// 设置连接超时
|
|
|
|
|
|
connectionTimeout = setTimeout(() => { |
|
|
|
|
|
if (!tcpConnected) { |
|
|
|
|
|
console.error('TCP连接超时'); |
|
|
|
|
|
if (ws.readyState === WebSocket.OPEN) { |
|
|
|
|
|
ws.close(1011, 'TCP连接超时'); |
|
|
|
|
|
} |
|
|
|
|
|
tcpClient.destroy(); |
|
|
|
|
|
} |
|
|
|
|
|
}, 5000); // 5秒超时
|
|
|
|
|
|
|
|
|
// TCP连接成功
|
|
|
// TCP连接成功
|
|
|
tcpClient.connect(process.env.TCP_PORT || TCP_PORT, process.env.TCP_HOST || TCP_HOST, () => { |
|
|
tcpClient.connect(process.env.TCP_PORT || TCP_PORT, process.env.TCP_HOST || TCP_HOST, () => { |
|
|
// console.log(process.env);
|
|
|
console.log(`TCP连接已建立到 ${TCP_HOST}:${TCP_PORT}`); |
|
|
// console.log(`TCP连接已建立到 ${TCP_HOST}:${TCP_PORT}`);
|
|
|
|
|
|
tcpConnected = true; |
|
|
tcpConnected = true; |
|
|
|
|
|
|
|
|
|
|
|
// 清除超时定时器
|
|
|
|
|
|
if (connectionTimeout) { |
|
|
|
|
|
clearTimeout(connectionTimeout); |
|
|
|
|
|
connectionTimeout = null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 向WebSocket客户端发送就绪信号
|
|
|
// 向WebSocket客户端发送就绪信号
|
|
|
if (ws.readyState === WebSocket.OPEN) { |
|
|
if (ws.readyState === WebSocket.OPEN) { |
|
|
const readySignal = JSON.stringify({ |
|
|
const readySignal = JSON.stringify({ |
|
|
@ -51,60 +65,43 @@ function setupTcpProxy(conf) { |
|
|
|
|
|
|
|
|
// TCP接收数据,转发到WebSocket
|
|
|
// TCP接收数据,转发到WebSocket
|
|
|
tcpClient.on('data', (data) => { |
|
|
tcpClient.on('data', (data) => { |
|
|
let textData; |
|
|
|
|
|
// 尝试解析为文本
|
|
|
|
|
|
try { |
|
|
try { |
|
|
textData = data.toString('utf8'); |
|
|
const textData = data.toString('utf8'); |
|
|
} catch (e) { |
|
|
|
|
|
console.log('TCP数据无法解析为文本'); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 将新数据添加到缓冲区
|
|
|
|
|
|
tcpDataBuffer += textData; |
|
|
tcpDataBuffer += textData; |
|
|
|
|
|
|
|
|
// 检查是否有完整的消息(以\n\n结尾)
|
|
|
|
|
|
let endIndex; |
|
|
let endIndex; |
|
|
while ((endIndex = tcpDataBuffer.indexOf('\n\n')) !== -1) { |
|
|
while ((endIndex = tcpDataBuffer.indexOf('\n\n')) !== -1) { |
|
|
// 提取完整消息
|
|
|
|
|
|
const completeMessage = tcpDataBuffer.substring(0, endIndex); |
|
|
const completeMessage = tcpDataBuffer.substring(0, endIndex); |
|
|
// 从缓冲区移除已处理的消息
|
|
|
|
|
|
tcpDataBuffer = tcpDataBuffer.substring(endIndex + 2); |
|
|
tcpDataBuffer = tcpDataBuffer.substring(endIndex + 2); |
|
|
|
|
|
|
|
|
// console.log('提取到完整消息:', completeMessage.length, '字节');
|
|
|
|
|
|
|
|
|
|
|
|
// 转发完整消息到WebSocket
|
|
|
|
|
|
if (ws.readyState === WebSocket.OPEN) { |
|
|
if (ws.readyState === WebSocket.OPEN) { |
|
|
// console.log('准备发送完整消息到WebSocket:', completeMessage.length, '字节');
|
|
|
|
|
|
// console.log('消息内容:', completeMessage);
|
|
|
|
|
|
ws.send(completeMessage, (err) => { |
|
|
ws.send(completeMessage, (err) => { |
|
|
if (err) { |
|
|
if (err) { |
|
|
console.error(`[${new Date().toLocaleString()}] WebSocket发送数据错误:`, err); |
|
|
console.error(`WebSocket发送数据错误:`, err); |
|
|
} else { |
|
|
|
|
|
// console.log('完整消息已转发到WebSocket客户端');
|
|
|
|
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
} catch (e) { |
|
|
// console.log('缓冲区剩余数据:', tcpDataBuffer.length, '字节');
|
|
|
console.error('TCP数据处理错误:', e); |
|
|
|
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
function compactJson(str) { |
|
|
function compactJson(str) { |
|
|
try { |
|
|
try { |
|
|
// 如果是 JSON 字符串,解析后再 stringify(无换行/缩进)
|
|
|
if (typeof str === 'string') { |
|
|
if (typeof str === 'string') return JSON.stringify(JSON.parse(str)); |
|
|
return JSON.stringify(JSON.parse(str)); |
|
|
// 如果是对象/Buffer等,统一转成无格式 JSON 字符串
|
|
|
} |
|
|
return JSON.stringify(str); |
|
|
return JSON.stringify(str); |
|
|
} catch { |
|
|
} catch { |
|
|
// 非 JSON 的情况,去掉所有换行,避免触发 \n\n 分隔
|
|
|
|
|
|
return String(str).replace(/\r?\n/g, ' '); |
|
|
return String(str).replace(/\r?\n/g, ' '); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// WebSocket接收数据,转发到TCP
|
|
|
// WebSocket接收数据,转发到TCP
|
|
|
ws.on('message', (data) => { |
|
|
ws.on('message', (data) => { |
|
|
// 处理可能的Buffer数据,转换为字符串
|
|
|
try { |
|
|
let messageStr; |
|
|
let messageStr; |
|
|
|
|
|
|
|
|
if (Buffer.isBuffer(data)) { |
|
|
if (Buffer.isBuffer(data)) { |
|
|
messageStr = data.toString('utf8'); |
|
|
messageStr = data.toString('utf8'); |
|
|
} else if (typeof data === 'string') { |
|
|
} else if (typeof data === 'string') { |
|
|
@ -112,75 +109,94 @@ function setupTcpProxy(conf) { |
|
|
} else { |
|
|
} else { |
|
|
return; |
|
|
return; |
|
|
} |
|
|
} |
|
|
messageStr = compactJson(messageStr); |
|
|
|
|
|
// 转发字符串数据到TCP服务器
|
|
|
|
|
|
if (tcpClient.writable) { |
|
|
|
|
|
console.log('发送数据到TCP服务器:', messageStr); |
|
|
|
|
|
|
|
|
|
|
|
// 检查数据大小
|
|
|
messageStr = compactJson(messageStr); |
|
|
// const dataSize = Buffer.byteLength(messageStr, 'utf8');
|
|
|
|
|
|
// console.log(`数据大小: ${dataSize} bytes`);
|
|
|
|
|
|
|
|
|
|
|
|
// 直接发送完整数据
|
|
|
if (tcpClient.writable && tcpConnected) { |
|
|
|
|
|
console.log('发送数据到TCP服务器:', messageStr); |
|
|
tcpClient.write(messageStr + '\n\n', (err) => { |
|
|
tcpClient.write(messageStr + '\n\n', (err) => { |
|
|
if (err) { |
|
|
if (err) { |
|
|
console.error(`[${new Date().toLocaleString()}] TCP发送数据错误:`, err); |
|
|
console.error('TCP发送数据错误:', err); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
} else { |
|
|
} else { |
|
|
// console.warn('TCP连接不可写,无法发送数据');
|
|
|
console.warn('TCP连接不可用,无法发送数据'); |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
console.error('WebSocket消息处理错误:', e); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// TCP连接错误处理
|
|
|
// TCP连接错误处理
|
|
|
tcpClient.on('error', (err) => { |
|
|
tcpClient.on('error', (err) => { |
|
|
console.error(`[${new Date().toLocaleString()}] TCP连接错误:`, err); |
|
|
console.error('TCP连接错误:', err); |
|
|
tcpDataBuffer = ''; // 清理缓冲区
|
|
|
tcpConnected = false; |
|
|
|
|
|
tcpDataBuffer = ''; |
|
|
|
|
|
|
|
|
|
|
|
// 清除超时定时器
|
|
|
|
|
|
if (connectionTimeout) { |
|
|
|
|
|
clearTimeout(connectionTimeout); |
|
|
|
|
|
connectionTimeout = null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (ws.readyState === WebSocket.OPEN) { |
|
|
if (ws.readyState === WebSocket.OPEN) { |
|
|
ws.close(1011, 'TCP连接错误'); |
|
|
ws.close(1011, `TCP连接错误: ${err.message}`); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// TCP连接关闭
|
|
|
// TCP连接关闭
|
|
|
tcpClient.on('close', () => { |
|
|
tcpClient.on('close', () => { |
|
|
// console.log('TCP连接已关闭');
|
|
|
console.log('TCP连接已关闭'); |
|
|
tcpDataBuffer = ''; // 清理缓冲区
|
|
|
tcpConnected = false; |
|
|
|
|
|
tcpDataBuffer = ''; |
|
|
|
|
|
|
|
|
|
|
|
if (connectionTimeout) { |
|
|
|
|
|
clearTimeout(connectionTimeout); |
|
|
|
|
|
connectionTimeout = null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if (ws.readyState === WebSocket.OPEN) { |
|
|
if (ws.readyState === WebSocket.OPEN) { |
|
|
ws.close(); |
|
|
ws.close(1000, 'TCP连接关闭'); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// WebSocket连接关闭
|
|
|
// WebSocket连接关闭
|
|
|
ws.on('close', () => { |
|
|
ws.on('close', (code, reason) => { |
|
|
// console.log('WebSocket连接已关闭');
|
|
|
console.log(`WebSocket连接已关闭: ${code} - ${reason}`); |
|
|
tcpDataBuffer = ''; // 清理缓冲区
|
|
|
tcpDataBuffer = ''; |
|
|
if (tcpClient.writable) { |
|
|
|
|
|
|
|
|
if (connectionTimeout) { |
|
|
|
|
|
clearTimeout(connectionTimeout); |
|
|
|
|
|
connectionTimeout = null; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (tcpClient && !tcpClient.destroyed) { |
|
|
tcpClient.destroy(); |
|
|
tcpClient.destroy(); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// WebSocket错误处理
|
|
|
// WebSocket错误处理
|
|
|
ws.on('error', (err) => { |
|
|
ws.on('error', (err) => { |
|
|
console.error(`[${new Date().toLocaleString()}] WebSocket错误:`, err); |
|
|
console.error('WebSocket错误:', err); |
|
|
if (tcpClient.writable) { |
|
|
if (tcpClient && !tcpClient.destroyed) { |
|
|
tcpClient.destroy(); |
|
|
tcpClient.destroy(); |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
wss.on('listening', () => { |
|
|
wss.on('listening', () => { |
|
|
// console.log(`TCP代理WebSocket服务器已启动在端口 ${wsPort},路径: /tcp-proxy`);
|
|
|
console.log(`TCP代理WebSocket服务器已启动在端口 ${wsPort},路径: /tcp-proxy`); |
|
|
// console.log(`局域网连接地址: ws://[本机IP]:${wsPort}/tcp-proxy`);
|
|
|
console.log(`本地连接地址: ws://localhost:${wsPort}/tcp-proxy`); |
|
|
// console.log(`本地连接地址: ws://localhost:${wsPort}/tcp-proxy`);
|
|
|
|
|
|
// console.log(`注意:请确保防火墙允许端口 ${wsPort} 的访问`);
|
|
|
|
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
wss.on('error', (err) => { |
|
|
wss.on('error', (err) => { |
|
|
console.error(`[${new Date().toLocaleString()}] WebSocket服务器错误:`, err); |
|
|
console.error('WebSocket服务器错误:', err); |
|
|
|
|
|
if (err.code === 'EADDRINUSE') { |
|
|
|
|
|
console.error(`端口 ${wsPort} 已被占用,请检查是否有其他服务在使用该端口`); |
|
|
|
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
return wss; |
|
|
return wss; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = { setupTcpProxy }; |
|
|
module.exports = { setupTcpProxy }; |
|
|
|