【WSL的端口开放给局域网内多个openclaw网关使用】一款实现WSL中运行的vLLM服务器端口开放软件(简单实现)
摘要 本文介绍了一种将WSL中的vLLM服务器端口开放给局域网设备的解决方案。当在Windows 11的WSL中运行vLLM大语言模型服务器时,虽然本地可通过localhost访问7988端口,但局域网设备无法直接连接。文章提出使用Node.js编写端口转发代理程序,将Win11的8988端口请求转发至WSL的7988端口。关键步骤包括:1)创建Node环境并安装依赖;2)编写代理脚本,通过Pow
【WSL的端口开放给局域网内多个openclaw网关使用】一款实现WSL中运行的vLLM服务器端口开放软件(简单实现)
痛点:
因为用windows 11 系统中运行了WSL来跑vLLM大语言模型的服务器,用了WSL config文件配置之后,windows 11 可以通过Localhost来访问WSL中的vLLM的服务端口(我用了7988端口)。但是又想让局域网中的多台设备(Mac)的openclaw网关来访问vLLM服务器(服务器配置好了Qwen3-Coder-Next)。但是惊奇发现,WSL的服务器只能在windows 11中访问,于是:
需要win11将WSL中的vLLM服务端口暴露给局域网中的其它设备。
首先,我们可以配置好windows 11是可以直接访问到vLLM服务器的端口的,例如,当你运行http://localhost:7988/v1/models 这个地址是会返回{Qwen3-Coder-Next} 的模型数据给你的,又或者你win11 可以用base URL来访问WSL中的服务端口:可以用 curl http://localhost:7988/v1/api 等地址来确认访问。如果localhost不能访问到WSL,你需要设置一下(详情问AI即可)
一旦win11 可以通过localhost来访问WSL中的服务端口,那么就进行以下步骤:
思路:通过node或者python来写一个简单的端口报文转发服务来实现,让局域网中的其它设备访问win11 服务器的 8988端口,然后通过自己编写的node程序来将8988端口请求转发到7988端口,然后将7988端口的报文转发到8988端口,实现相互通讯。(端口号可以自定义)
详细操作步骤:
1:在win 11 中建立一个新文件夹,例如:D:\repo\wsl-port-forward这个路径下,用powershell打开,用node新建一个运行环境(也可以用python),不过node的环境创建对应的是 npm init -y,python对应python的虚拟程序创建是:python -m venv .venv
2:安装依赖(node已经初始化完成):npm install express http-proxy-middleware axios
3:创建一个proxy.js文件,复制以下代码:
// proxy.js (修复版)
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const axios = require('axios');
const { execSync, spawnSync } = require('child_process');
const os = require('os');
const LOCAL_PORT = 7988;
const WSL_PORT = 7988;
let wslIp = null; // 缓存 WSL IP,避免频繁调用
// 【关键修复】增强 WSL IP 获取逻辑(返回 Promise)
function getWslIp() {
return new Promise((resolve, reject) => {
console.log('🔍 正在获取 WSL2 IP...');
try {
// 方案1: 尝试标准命令 (Windows 10/11)
let output = '';
if (os.platform() === 'win32') {
// 使用 PowerShell 更可靠
const psResult = spawnSync('powershell.exe', [
'-Command',
'(wsl hostname -I).Trim().Split(" ")[0]'
], { encoding: 'utf-8', timeout: 5000 });
if (psResult.status === 0 && psResult.stdout.trim()) {
output = psResult.stdout.trim();
console.log(`💡 通过 PowerShell 获取到 IP: ${output}`);
} else {
console.warn('⚠️ PowerShell 方法失败,尝试备用方案');
// 备用方案:直接调用 wsl
const cmdResult = execSync('wsl hostname -I', {
encoding: 'utf-8',
timeout: 3000
});
output = cmdResult.trim().split(' ')[0];
}
} else {
// 非 Windows 系统 (如 macOS/Linux)
output = execSync('hostname -I', {
encoding: 'utf-8',
timeout: 3000
}).trim().split(' ')[0];
}
if (!output || !output.match(/\d+\.\d+\.\d+\.\d+/)) {
throw new Error(`无效的 IP 格式: "${output}"`);
}
wslIp = output;
console.log(`✅ 成功获取 WSL2 IP: ${wslIp}`);
resolve(wslIp);
} catch (err) {
console.error('🔥 获取 WSL IP 时发生严重错误:');
console.error(' 命令输出:', err.stdout?.trim() || '无');
console.error(' 错误详情:', err.message);
console.error(' 完整错误对象:', JSON.stringify(err, null, 2));
// 【关键】不立即退出,而是每 5 秒重试
console.log('⏳ 5 秒后重试获取 WSL IP...');
setTimeout(() => {
getWslIp().then(resolve).catch(reject);
}, 5000);
}
});
}
// 【关键修复】异步初始化
async function initProxy() {
// 1. 先获取 WSL IP
wslIp = await getWslIp();
// 2. 检查端口是否可用
try {
await new Promise((resolve, reject) => {
const testServer = express().listen(LOCAL_PORT, '0.0.0.0', () => {
testServer.close(); // 立即关闭测试服务器
resolve();
});
testServer.on('error', (err) => {
reject(new Error(`端口 ${LOCAL_PORT} 被占用或无权限: ${err.message}`));
});
});
console.log(`✅ 端口 ${LOCAL_PORT} 可用`);
} catch (err) {
console.error('🔥 端口检查失败:', err.message);
console.error('💡 解决方案:');
console.error(' 1. 关闭已占用 7988 端口的程序 (如 netsh portproxy / 其他服务)');
console.error(' 2. 以管理员身份运行 PowerShell: netsh interface portproxy delete v4tov4 listenport=7988');
console.error(' 3. 或修改 LOCAL_PORT 为其他值 (如 8080)');
process.exit(1);
}
// 4. 创建代理服务器(此时 WSL IP 已就绪)
const app = express();
// 【增强】详细的请求日志
app.use((req, res, next) => {
const start = Date.now();
console.log(`📥 [${new Date().toISOString()}] ${req.method} ${req.url}`);
res.on('finish', () => {
console.log(`📤 [${new Date().toISOString()}] ${req.method} ${req.url} - ${res.statusCode} (${Date.now() - start}ms)`);
});
next();
});
// 健康检查 (带重试)
app.get('/health', async (req, res) => {
if (!wslIp) {
return res.status(503).json({ error: 'WSL IP 未初始化' });
}
try {
const targetUrl = `http://${wslIp}:${WSL_PORT}/health`;
console.log(`🩺 健康检查: GET ${targetUrl}`);
const response = await axios.get(targetUrl, { timeout: 3000 });
res.json({
status: 'ok',
target: targetUrl,
upstream: response.data
});
} catch (err) {
console.error('🩺 健康检查失败:', err.message);
res.status(503).json({
error: 'Upstream unhealthy',
details: err.message,
lastKnownIp: wslIp
});
}
});
// 【关键修复】代理中间件
app.use(
'/',
createProxyMiddleware({
target: `http://${wslIp}:${WSL_PORT}`,
changeOrigin: true,
logLevel: 'debug', // 启用详细日志
onError: (err, req, res) => {
console.error(`🚨 代理错误 [${req.method} ${req.url}]:`, err.message);
res.status(502).json({
error: 'Bad Gateway',
details: err.message,
timestamp: new Date().toISOString()
});
},
onProxyReq: (proxyReq, req) => {
console.log(`📡 转发请求到: ${proxyReq.path}`);
}
})
);
// 【关键】处理服务器错误
const server = app.listen(LOCAL_PORT, '0.0.0.0', () => {
console.log(`🚀 HTTP 代理服务器已启动!`);
console.log(` 本地访问: http://localhost:${LOCAL_PORT}/docs`);
console.log(` 局域网访问: http://<你的局域网IP>:${LOCAL_PORT}/docs`);
console.log(` 代理目标: http://${wslIp}:${WSL_PORT} (初始 IP,动态更新)`);
console.log('✅ 服务器将持续运行,按 Ctrl+C 停止');
});
// 【关键】防止进程意外退出
server.on('error', (err) => {
console.error('🔥 服务器致命错误:', err.message);
if (err.code === 'EADDRINUSE') {
console.error(`💡 端口 ${LOCAL_PORT} 已被占用! 请先执行:`);
console.error(' netsh interface portproxy delete v4tov4 listenport=7988 listenaddress=0.0.0.0');
}
process.exit(1);
});
// 保持进程活跃
setInterval(() => {
if (!wslIp) {
console.log('🔄 后台重试获取 WSL IP...');
getWslIp();
}
}, 10000);
}
// 【关键】全局错误捕获
process.on('uncaughtException', (err) => {
console.error('💥 未捕获的异常:', err.message);
console.error(err.stack);
});
process.on('unhandledRejection', (reason, promise) => {
console.error('💥 未处理的 Promise rejection:', reason);
});
// 【关键】优雅关闭
process.on('SIGINT', () => {
console.log('\n👋 正在关闭代理服务器...');
process.exit(0);
});
// 启动!
console.log('🔧 初始化代理服务...');
initProxy().catch(err => {
console.error('🔥 初始化失败:', err);
process.exit(1);
});
4: 保存文件后,新建一个ReadMe.md文件:
# WSL 端口转发代理服务
将请求从 Windows 端口转发到 WSL2。
## 启动方式
```bash
node proxy.js
```
## 说明
- 本地访问: http://localhost:7988
- 局域网访问: http://<你的局域网IP>:7988
- 代理目标: WSL2 的 `http://<wsl-ip>:7988`
5:现在你可以运行程序了:node proxy.js
(确保你已经安装了node, 一般openclaw的环境要求node>22)
搞定,现在你的局域网中的其它设备都可以访问到WSL中的vLLM服务器了。(请自行解决防火墙问题,你都看到这里了,小问题就能自己解决了吧)
不足和改进:
1:端口转发没有设置鉴权,请勿用于公网访问。
2:端口接收的数据是明文直接转发,为了安全,你可以增加鉴权层,增加加密层,增加有害信息过滤层,关键词过滤层以及其它需要符合法律法规和安全规范的行为能力,让软件世界更加安全。
3:祝大家新年快乐!
更多推荐




所有评论(0)