[{"data":1,"prerenderedAt":3131},["ShallowReactive",2],{"blog-\u002Fblog\u002Fwsl2-port-forwarding-guide":3,"blog-siblings-\u002Fblog\u002Fwsl2-port-forwarding-guide":3124},{"id":4,"title":5,"body":6,"category":3107,"date":3108,"description":3109,"draft":3110,"extension":3111,"meta":3112,"navigation":889,"path":3113,"seo":3114,"source":3115,"stem":3116,"tags":3117,"__hash__":3123},"blog\u002Fblog\u002Fwsl2-port-forwarding-guide.md","WSL2 端口转发完整指南",{"type":7,"value":8,"toc":3047},"minimark",[9,25,30,33,59,65,71,74,78,88,94,120,138,140,144,233,238,248,253,292,309,312,318,328,334,336,340,348,363,368,480,485,543,548,583,588,622,626,631,638,743,749,752,790,796,800,832,838,840,844,848,856,862,865,1289,1293,1342,1346,1349,1412,1416,1422,1437,1440,1446,1448,1452,1456,1465,1468,1472,1496,1510,1517,1521,1574,1622,1642,1644,1648,1655,1660,1674,1680,1684,1699,1702,1706,1748,1752,1787,1800,1804,1811,1814,1823,1826,1832,1835,1841,1864,1867,1873,1915,1919,1924,1930,1935,1941,1949,1955,1984,1987,1993,1997,2000,2020,2024,2063,2067,2081,2086,2092,2098,2102,2117,2125,2131,2137,2140,2164,2169,2173,2177,2183,2188,2197,2244,2246,2250,2253,2257,2263,2403,2407,2460,2466,2512,2516,2590,2594,2633,2635,2639,2676,2682,2684,2688,2692,2802,2806,2879,2881,2885,2889,2892,2916,2921,2940,2944,2947,2971,2974,2978,2988,2990,2994,3004,3007,3021,3024,3036,3043],[10,11,12],"blockquote",{},[13,14,15,16,20,21,24],"p",{},"在 WSL2 里跑 ",[17,18,19],"code",{},"npm run serve","，本机 ",[17,22,23],{},"localhost:25011"," 能开，但同事用你的局域网 IP 怎么也访问不到？这篇文档把整个网络拓扑、所有方案、所有踩坑、一键脚本和开机自动化全部讲透。",[26,27,29],"h2",{"id":28},"一问题描述","一、问题描述",[13,31,32],{},"典型场景：",[34,35,36,40,47],"ol",{},[37,38,39],"li",{},"你在 WSL2 Ubuntu 里跑了一个 Vue\u002FReact\u002FNode 的 dev server（端口 25011）",[37,41,42,43,46],{},"本机 Chrome 打开 ",[17,44,45],{},"http:\u002F\u002Flocalhost:25011\u002F"," 能加载页面 ✅",[37,48,49,50,53,54,58],{},"让同事访问 ",[17,51,52],{},"http:\u002F\u002F192.168.1.99:25011\u002F","（你的 Windows LAN IP）—— ",[55,56,57],"strong",{},"连接不上"," ❌",[13,60,61,64],{},[55,62,63],{},"期望","：像 VMware 桥接模式那样，给虚拟机一个 192.168.x.x 的局域网 IP，物理机和虚拟机平级，局域网其他机器直接访问。",[13,66,67,70],{},[55,68,69],{},"现实","：WSL2 不是这么设计的，需要做端口转发。",[72,73],"hr",{},[26,75,77],{"id":76},"二网络拓扑-为什么默认访问不到","二、网络拓扑 —— 为什么默认访问不到",[79,80,85],"pre",{"className":81,"code":83,"language":84},[82],"language-text","                  +-------------------------+\n                  |  局域网（路由器\u002F交换机） |\n                  +-----------+-------------+\n                              |\n                              |  192.168.1.0\u002F24\n                              |\n                  +-----------+-------------+\n                  |  Windows 主机           |\n                  |  以太网: 192.168.1.99   |\n                  |                         |\n                  |  vEthernet (WSL):       |\n                  |  172.20.144.1           |   ← Hyper-V 内部 NAT 网关\n                  |       |                 |\n                  |       | NAT             |\n                  |       v                 |\n                  |  WSL2: 172.20.151.103   |\n                  |  (Ubuntu 容器)          |\n                  +-------------------------+\n\n  局域网 ──> Windows (192.168.1.99) ──[NAT]──> WSL (172.20.151.103)\n                  ↑                                    ↑\n              实体 LAN IP                          内部 NAT IP\n\n  ❌ 局域网 → Windows IP → WSL：默认不转发\n  ✅ Windows本机 → localhost → WSL：localhostForwarding 自动转发\n","text",[17,86,83],{"__ignoreMap":87},"",[13,89,90,93],{},[55,91,92],{},"关键认知","：",[95,96,97,104,107,114],"ul",{},[37,98,99,100,103],{},"WSL2 是 Hyper-V 内部的\"轻量虚拟机\"，跑在隔离的 NAT 子网（如 ",[17,101,102],{},"172.20.0.0\u002F16","）",[37,105,106],{},"外部世界看不到这个网段",[37,108,109,110,113],{},"WSL 出网时，包经过 Windows NAT 转出（",[17,111,112],{},"172.20.144.1"," 是网关）",[37,115,116,119],{},[55,117,118],{},"入站要从 Windows 主动转发到 WSL","，否则到达不了",[13,121,122,125,126,129,130,133,134,137],{},[17,123,124],{},"localhostForwarding=true"," 这个设置只让 ",[55,127,128],{},"Windows 本机"," 的 ",[17,131,132],{},"localhost:port"," 自动转给 WSL，",[55,135,136],{},"不会主动暴露给局域网","。",[72,139],{},[26,141,143],{"id":142},"三三种方案对比","三、三种方案对比",[145,146,147,169],"table",{},[148,149,150],"thead",{},[151,152,153,157,160,163,166],"tr",{},[154,155,156],"th",{},"方案",[154,158,159],{},"等效 VMware 模式",[154,161,162],{},"优点",[154,164,165],{},"缺点",[154,167,168],{},"推荐度",[170,171,172,192,214],"tbody",{},[151,173,174,180,183,186,189],{},[175,176,177],"td",{},[55,178,179],{},"A. Bridged（桥接）",[175,181,182],{},"Bridged",[175,184,185],{},"WSL 拿 192.168.x.x，跟物理机平级",[175,187,188],{},"多虚拟化环境必然冲突，配置复杂",[175,190,191],{},"❌",[151,193,194,199,202,205,211],{},[175,195,196],{},[55,197,198],{},"B. Mirrored（镜像）",[175,200,201],{},"NAT 自动同步",[175,203,204],{},"端口自动同步，最优雅",[175,206,207,208],{},"VMware\u002FDocker 共存时报 ",[17,209,210],{},"0x8007054f",[175,212,213],{},"⚠️ 看环境",[151,215,216,221,224,227,230],{},[175,217,218],{},[55,219,220],{},"C. NAT + 批量 portproxy",[175,222,223],{},"NAT + 手动端口映射",[175,225,226],{},"稳、可控、对其他虚拟化无影响",[175,228,229],{},"需要预先配端口段",[175,231,232],{},"✅",[234,235,237],"h3",{"id":236},"为什么选方案-cportproxy","为什么选方案 C（portproxy）",[13,239,240,243,244,247],{},[55,241,242],{},"Bridged 模式","虽然听起来很美好（WSL 拿到 192.168.x.x），但要在 Hyper-V 管理器创建\"外部虚拟交换机\"，它会",[55,245,246],{},"接管你的物理网卡","，跟 VMware Workstation 的 NAT\u002F桥接虚拟网卡严重冲突，可能导致整机网络抽风。",[13,249,250,93],{},[55,251,252],{},"Mirrored 模式",[79,254,258],{"className":255,"code":256,"language":257,"meta":87,"style":87},"language-ini shiki shiki-themes github-light github-dark","[wsl2]\nnetworkingMode=mirrored\ndnsTunneling=true\nfirewall=true\nautoProxy=true\n","ini",[17,259,260,268,274,280,286],{"__ignoreMap":87},[261,262,265],"span",{"class":263,"line":264},"line",1,[261,266,267],{},"[wsl2]\n",[261,269,271],{"class":263,"line":270},2,[261,272,273],{},"networkingMode=mirrored\n",[261,275,277],{"class":263,"line":276},3,[261,278,279],{},"dnsTunneling=true\n",[261,281,283],{"class":263,"line":282},4,[261,284,285],{},"firewall=true\n",[261,287,289],{"class":263,"line":288},5,[261,290,291],{},"autoProxy=true\n",[13,293,294,295,298,299,302,303,305,306,137],{},"它让 WSL ",[55,296,297],{},"共用 Windows 网络栈","，WSL 监听 ",[17,300,301],{},"0.0.0.0:8080"," = Windows 监听 ",[17,304,301],{},"，局域网直接访问 Windows IP 即可。",[55,307,308],{},"理论上是终极方案",[13,310,311],{},"但是！**多虚拟化环境（VMware Workstation + Hyper-V + Docker Desktop）**必然报错：",[79,313,316],{"className":314,"code":315,"language":84},[82],"wsl: 出现了内部错误。\n错误代码: CreateInstance\u002FCreateVm\u002FConfigureNetworking\u002F0x8007054f\nwsl: 无法配置网络 (networkingMode Mirrored)，回退到 networkingMode None。\n",[17,317,315],{"__ignoreMap":87},[13,319,320,321,324,325,137],{},"更糟的是 ",[55,322,323],{},"WSL 不会回退到 NAT","，而是回退到 ",[55,326,327],{},"None 模式（完全无网络）",[13,329,330,333],{},[55,331,332],{},"所以只剩方案 C：NAT + 批量 portproxy","。下面详细讲。",[72,335],{},[26,337,339],{"id":338},"四方案-c-完整实施","四、方案 C 完整实施",[234,341,343,344,347],{"id":342},"_41-让-wsl-内的服务监听-0000最容易遗漏的一步","4.1 让 WSL 内的服务监听 ",[17,345,346],{},"0.0.0.0","（最容易遗漏的一步）",[13,349,350,351,354,355,358,359,362],{},"如果你的服务只监听 ",[17,352,353],{},"127.0.0.1"," 或 ",[17,356,357],{},"localhost","，",[55,360,361],{},"它只接受 WSL 内部连接","，Windows 都转发不进去，更别提局域网。",[13,364,365,93],{},[55,366,367],{},"Vue CLI（vue.config.js）",[79,369,373],{"className":370,"code":371,"language":372,"meta":87,"style":87},"language-js shiki shiki-themes github-light github-dark","module.exports = {\n  devServer: {\n    host: '0.0.0.0',           \u002F\u002F ← 关键\n    port: 25011,\n    allowedHosts: 'all',       \u002F\u002F 允许任意 Host 头访问（局域网 IP 访问时需要）\n    client: {\n      webSocketURL: 'auto:\u002F\u002F0.0.0.0:0\u002Fws',  \u002F\u002F HMR WebSocket 自适应\n    },\n  }\n}\n","js",[17,374,375,395,400,416,427,441,447,462,468,474],{"__ignoreMap":87},[261,376,377,381,385,388,392],{"class":263,"line":264},[261,378,380],{"class":379},"sj4cs","module",[261,382,384],{"class":383},"sVt8B",".",[261,386,387],{"class":379},"exports",[261,389,391],{"class":390},"szBVR"," =",[261,393,394],{"class":383}," {\n",[261,396,397],{"class":263,"line":270},[261,398,399],{"class":383},"  devServer: {\n",[261,401,402,405,409,412],{"class":263,"line":276},[261,403,404],{"class":383},"    host: ",[261,406,408],{"class":407},"sZZnC","'0.0.0.0'",[261,410,411],{"class":383},",           ",[261,413,415],{"class":414},"sJ8bj","\u002F\u002F ← 关键\n",[261,417,418,421,424],{"class":263,"line":282},[261,419,420],{"class":383},"    port: ",[261,422,423],{"class":379},"25011",[261,425,426],{"class":383},",\n",[261,428,429,432,435,438],{"class":263,"line":288},[261,430,431],{"class":383},"    allowedHosts: ",[261,433,434],{"class":407},"'all'",[261,436,437],{"class":383},",       ",[261,439,440],{"class":414},"\u002F\u002F 允许任意 Host 头访问（局域网 IP 访问时需要）\n",[261,442,444],{"class":263,"line":443},6,[261,445,446],{"class":383},"    client: {\n",[261,448,450,453,456,459],{"class":263,"line":449},7,[261,451,452],{"class":383},"      webSocketURL: ",[261,454,455],{"class":407},"'auto:\u002F\u002F0.0.0.0:0\u002Fws'",[261,457,458],{"class":383},",  ",[261,460,461],{"class":414},"\u002F\u002F HMR WebSocket 自适应\n",[261,463,465],{"class":263,"line":464},8,[261,466,467],{"class":383},"    },\n",[261,469,471],{"class":263,"line":470},9,[261,472,473],{"class":383},"  }\n",[261,475,477],{"class":263,"line":476},10,[261,478,479],{"class":383},"}\n",[13,481,482,93],{},[55,483,484],{},"Vite（vite.config.js）",[79,486,488],{"className":370,"code":487,"language":372,"meta":87,"style":87},"export default {\n  server: {\n    host: '0.0.0.0',\n    port: 25011,\n    strictPort: true,    \u002F\u002F 端口被占就报错，不自动找下一个\n  }\n}\n",[17,489,490,500,505,513,521,535,539],{"__ignoreMap":87},[261,491,492,495,498],{"class":263,"line":264},[261,493,494],{"class":390},"export",[261,496,497],{"class":390}," default",[261,499,394],{"class":383},[261,501,502],{"class":263,"line":270},[261,503,504],{"class":383},"  server: {\n",[261,506,507,509,511],{"class":263,"line":276},[261,508,404],{"class":383},[261,510,408],{"class":407},[261,512,426],{"class":383},[261,514,515,517,519],{"class":263,"line":282},[261,516,420],{"class":383},[261,518,423],{"class":379},[261,520,426],{"class":383},[261,522,523,526,529,532],{"class":263,"line":288},[261,524,525],{"class":383},"    strictPort: ",[261,527,528],{"class":379},"true",[261,530,531],{"class":383},",    ",[261,533,534],{"class":414},"\u002F\u002F 端口被占就报错，不自动找下一个\n",[261,536,537],{"class":263,"line":443},[261,538,473],{"class":383},[261,540,541],{"class":263,"line":449},[261,542,479],{"class":383},[13,544,545,93],{},[55,546,547],{},"Node\u002FExpress",[79,549,551],{"className":370,"code":550,"language":372,"meta":87,"style":87},"app.listen(25011, '0.0.0.0', ...)   \u002F\u002F 别写 'localhost' 或 '127.0.0.1'\n",[17,552,553],{"__ignoreMap":87},[261,554,555,558,562,565,567,570,572,574,577,580],{"class":263,"line":264},[261,556,557],{"class":383},"app.",[261,559,561],{"class":560},"sScJk","listen",[261,563,564],{"class":383},"(",[261,566,423],{"class":379},[261,568,569],{"class":383},", ",[261,571,408],{"class":407},[261,573,569],{"class":383},[261,575,576],{"class":390},"...",[261,578,579],{"class":383},")   ",[261,581,582],{"class":414},"\u002F\u002F 别写 'localhost' 或 '127.0.0.1'\n",[13,584,585,93],{},[55,586,587],{},"验证 WSL 内监听情况",[79,589,593],{"className":590,"code":591,"language":592,"meta":87,"style":87},"language-bash shiki shiki-themes github-light github-dark","ss -tlnp | grep 25011\n# 预期：LISTEN  0  511  0.0.0.0:25011  0.0.0.0:*  users:((\"node\",pid=...))\n#                       ↑ 必须是 0.0.0.0，不能是 127.0.0.1\n","bash",[17,594,595,612,617],{"__ignoreMap":87},[261,596,597,600,603,606,609],{"class":263,"line":264},[261,598,599],{"class":560},"ss",[261,601,602],{"class":379}," -tlnp",[261,604,605],{"class":390}," |",[261,607,608],{"class":560}," grep",[261,610,611],{"class":379}," 25011\n",[261,613,614],{"class":263,"line":270},[261,615,616],{"class":414},"# 预期：LISTEN  0  511  0.0.0.0:25011  0.0.0.0:*  users:((\"node\",pid=...))\n",[261,618,619],{"class":263,"line":276},[261,620,621],{"class":414},"#                       ↑ 必须是 0.0.0.0，不能是 127.0.0.1\n",[234,623,625],{"id":624},"_42-端口段规划","4.2 端口段规划",[13,627,628,137],{},[55,629,630],{},"铁律：选 Windows 不会用的高位端口段",[13,632,633,634,637],{},"Windows 端常见预留端口（",[55,635,636],{},"别给 WSL 用","）：",[145,639,640,650],{},[148,641,642],{},[151,643,644,647],{},[154,645,646],{},"端口",[154,648,649],{},"用途",[170,651,652,660,668,676,684,695,703,711,719,727,735],{},[151,653,654,657],{},[175,655,656],{},"80, 443",[175,658,659],{},"IIS \u002F 系统 Web",[151,661,662,665],{},[175,663,664],{},"135, 139, 445",[175,666,667],{},"Windows 网络共享",[151,669,670,673],{},[175,671,672],{},"1433, 1434",[175,674,675],{},"SQL Server",[151,677,678,681],{},[175,679,680],{},"3306",[175,682,683],{},"MySQL",[151,685,686,689],{},[175,687,688],{},"3389",[175,690,691,692,103],{},"RDP（",[55,693,694],{},"绝对不能动",[151,696,697,700],{},[175,698,699],{},"5432",[175,701,702],{},"PostgreSQL",[151,704,705,708],{},[175,706,707],{},"5900-5910",[175,709,710],{},"VNC",[151,712,713,716],{},[175,714,715],{},"6379",[175,717,718],{},"Redis",[151,720,721,724],{},[175,722,723],{},"7890, 7891, 9090",[175,725,726],{},"Clash",[151,728,729,732],{},[175,730,731],{},"8081",[175,733,734],{},"Nexus（很多公司私服）",[151,736,737,740],{},[175,738,739],{},"49152-65535",[175,741,742],{},"Windows 动态端口范围",[13,744,745,748],{},[55,746,747],{},"推荐策略：用 2xxxx 高位端口","（24011-26064 这种），跟 Windows 系统服务完全不打架。",[13,750,751],{},"我自己的端口约定示例：",[145,753,754,764],{},[148,755,756],{},[151,757,758,761],{},[154,759,760],{},"服务类型",[154,762,763],{},"端口段",[170,765,766,774,782],{},[151,767,768,771],{},[175,769,770],{},"前端 dev server",[175,772,773],{},"24011-24064",[151,775,776,779],{},[175,777,778],{},"后端 API",[175,780,781],{},"25011-25064",[151,783,784,787],{},[175,785,786],{},"备用",[175,788,789],{},"26011-26064",[13,791,792,795],{},[55,793,794],{},"写到团队 README","，避免冲突。",[234,797,799],{"id":798},"_43-查看哪些端口已经被-windows-占用","4.3 查看哪些端口已经被 Windows 占用",[79,801,805],{"className":802,"code":803,"language":804,"meta":87,"style":87},"language-powershell shiki shiki-themes github-light github-dark","# 管理员 PowerShell\nGet-NetTCPConnection -State Listen |\n  Select-Object LocalPort, OwningProcess,\n    @{N='Process';E={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).Name}} |\n  Sort-Object LocalPort | Format-Table -AutoSize\n","powershell",[17,806,807,812,817,822,827],{"__ignoreMap":87},[261,808,809],{"class":263,"line":264},[261,810,811],{},"# 管理员 PowerShell\n",[261,813,814],{"class":263,"line":270},[261,815,816],{},"Get-NetTCPConnection -State Listen |\n",[261,818,819],{"class":263,"line":276},[261,820,821],{},"  Select-Object LocalPort, OwningProcess,\n",[261,823,824],{"class":263,"line":282},[261,825,826],{},"    @{N='Process';E={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).Name}} |\n",[261,828,829],{"class":263,"line":288},[261,830,831],{},"  Sort-Object LocalPort | Format-Table -AutoSize\n",[13,833,834,835,137],{},"输出会显示所有 Windows 上正在监听的端口和对应进程，",[55,836,837],{},"用来确认你选的端口段没被占用",[72,839],{},[26,841,843],{"id":842},"五批量端口转发脚本完整版","五、批量端口转发脚本（完整版）",[234,845,847],{"id":846},"_51-脚本本体","5.1 脚本本体",[13,849,850,851,137],{},"保存为 ",[55,852,853],{},[17,854,855],{},"C:\\Users\\\u003C你的用户名>\\scripts\\wsl-port-forward-batch.ps1",[13,857,858,861],{},[55,859,860],{},"重要：文件必须是 UTF-8 with BOM 编码","，否则 Windows PowerShell 5.x 会按 GBK 解读，中文崩、语法崩。",[13,863,864],{},"用记事本\u002FVS Code 保存时选 \"UTF-8 with BOM\"。",[79,866,868],{"className":802,"code":867,"language":804,"meta":87,"style":87},"# WSL2 batch port-forward script (run as Administrator)\n# Forwards specified port ranges from Windows to WSL so LAN peers can access WSL services\n# Ports already bound by Windows processes are automatically skipped\n\n# ---------- Configuration ----------\n$portRanges = @(\n    @{ Start = 24011; End = 24064 },\n    @{ Start = 25011; End = 25064 },\n    @{ Start = 26011; End = 26064 }\n)\n# -----------------------------------\n\nfunction Test-PortInUse {\n    param([int]$Port)\n    $conn = Get-NetTCPConnection -State Listen -LocalPort $Port -ErrorAction SilentlyContinue\n    if ($conn) {\n        foreach ($c in $conn) {\n            $proc = Get-Process -Id $c.OwningProcess -ErrorAction SilentlyContinue\n            # portproxy 自己是 svchost 跑的，不算冲突\n            if ($proc -and $proc.Name -ne 'svchost') {\n                return $true\n            }\n        }\n    }\n    return $false\n}\n\n# 获取 WSL IP\n$wslIP = (wsl hostname -I).Trim().Split()[0]\nif (-not $wslIP) {\n    Write-Error \"WSL not running. Start WSL first (e.g. open Ubuntu terminal).\"\n    exit 1\n}\nWrite-Host \"WSL IP: $wslIP\" -ForegroundColor Green\n\n$skipped = @()\n$added = @()\n\nforeach ($range in $portRanges) {\n    for ($port = $range.Start; $port -le $range.End; $port++) {\n        # 先清掉旧规则\n        netsh interface portproxy delete v4tov4 listenport=$port listenaddress=0.0.0.0 2>$null | Out-Null\n\n        # 检查端口是否被 Windows 进程占用\n        if (Test-PortInUse -Port $port) {\n            $conn = Get-NetTCPConnection -State Listen -LocalPort $port -ErrorAction SilentlyContinue | Select-Object -First 1\n            $procName = (Get-Process -Id $conn.OwningProcess -ErrorAction SilentlyContinue).Name\n            $skipped += \"$port ($procName)\"\n            continue\n        }\n\n        netsh interface portproxy add v4tov4 listenport=$port listenaddress=0.0.0.0 connectport=$port connectaddress=$wslIP | Out-Null\n        $added += $port\n    }\n\n    # 防火墙规则按段添加\n    $ruleName = \"WSL2 Range $($range.Start)-$($range.End)\"\n    Remove-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue\n    New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Protocol TCP -LocalPort \"$($range.Start)-$($range.End)\" -Action Allow | Out-Null\n}\n\nWrite-Host \"\"\nWrite-Host \"Added portproxy for $($added.Count) ports to WSL ($wslIP)\" -ForegroundColor Green\nif ($skipped.Count -gt 0) {\n    Write-Host \"Skipped ports already used by Windows:\" -ForegroundColor Yellow\n    $skipped | ForEach-Object { Write-Host \"  - $_\" -ForegroundColor Yellow }\n}\n\nWrite-Host \"\"\nWrite-Host \"Windows LAN IPs (use these to access from other machines):\" -ForegroundColor Cyan\nGet-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Dhcp, Manual -ErrorAction SilentlyContinue |\n    Where-Object { $_.IPAddress -notlike \"169.254.*\" -and $_.IPAddress -ne \"127.0.0.1\" } |\n    Select-Object IPAddress, InterfaceAlias |\n    Format-Table -AutoSize\n",[17,869,870,875,880,885,891,896,901,906,911,916,921,927,932,938,944,950,956,962,968,974,980,986,992,998,1004,1010,1015,1020,1026,1032,1038,1044,1050,1055,1061,1066,1072,1078,1083,1089,1095,1101,1107,1112,1118,1124,1130,1136,1142,1148,1153,1158,1164,1170,1175,1180,1186,1192,1198,1204,1209,1214,1220,1226,1232,1238,1244,1249,1254,1259,1265,1271,1277,1283],{"__ignoreMap":87},[261,871,872],{"class":263,"line":264},[261,873,874],{},"# WSL2 batch port-forward script (run as Administrator)\n",[261,876,877],{"class":263,"line":270},[261,878,879],{},"# Forwards specified port ranges from Windows to WSL so LAN peers can access WSL services\n",[261,881,882],{"class":263,"line":276},[261,883,884],{},"# Ports already bound by Windows processes are automatically skipped\n",[261,886,887],{"class":263,"line":282},[261,888,890],{"emptyLinePlaceholder":889},true,"\n",[261,892,893],{"class":263,"line":288},[261,894,895],{},"# ---------- Configuration ----------\n",[261,897,898],{"class":263,"line":443},[261,899,900],{},"$portRanges = @(\n",[261,902,903],{"class":263,"line":449},[261,904,905],{},"    @{ Start = 24011; End = 24064 },\n",[261,907,908],{"class":263,"line":464},[261,909,910],{},"    @{ Start = 25011; End = 25064 },\n",[261,912,913],{"class":263,"line":470},[261,914,915],{},"    @{ Start = 26011; End = 26064 }\n",[261,917,918],{"class":263,"line":476},[261,919,920],{},")\n",[261,922,924],{"class":263,"line":923},11,[261,925,926],{},"# -----------------------------------\n",[261,928,930],{"class":263,"line":929},12,[261,931,890],{"emptyLinePlaceholder":889},[261,933,935],{"class":263,"line":934},13,[261,936,937],{},"function Test-PortInUse {\n",[261,939,941],{"class":263,"line":940},14,[261,942,943],{},"    param([int]$Port)\n",[261,945,947],{"class":263,"line":946},15,[261,948,949],{},"    $conn = Get-NetTCPConnection -State Listen -LocalPort $Port -ErrorAction SilentlyContinue\n",[261,951,953],{"class":263,"line":952},16,[261,954,955],{},"    if ($conn) {\n",[261,957,959],{"class":263,"line":958},17,[261,960,961],{},"        foreach ($c in $conn) {\n",[261,963,965],{"class":263,"line":964},18,[261,966,967],{},"            $proc = Get-Process -Id $c.OwningProcess -ErrorAction SilentlyContinue\n",[261,969,971],{"class":263,"line":970},19,[261,972,973],{},"            # portproxy 自己是 svchost 跑的，不算冲突\n",[261,975,977],{"class":263,"line":976},20,[261,978,979],{},"            if ($proc -and $proc.Name -ne 'svchost') {\n",[261,981,983],{"class":263,"line":982},21,[261,984,985],{},"                return $true\n",[261,987,989],{"class":263,"line":988},22,[261,990,991],{},"            }\n",[261,993,995],{"class":263,"line":994},23,[261,996,997],{},"        }\n",[261,999,1001],{"class":263,"line":1000},24,[261,1002,1003],{},"    }\n",[261,1005,1007],{"class":263,"line":1006},25,[261,1008,1009],{},"    return $false\n",[261,1011,1013],{"class":263,"line":1012},26,[261,1014,479],{},[261,1016,1018],{"class":263,"line":1017},27,[261,1019,890],{"emptyLinePlaceholder":889},[261,1021,1023],{"class":263,"line":1022},28,[261,1024,1025],{},"# 获取 WSL IP\n",[261,1027,1029],{"class":263,"line":1028},29,[261,1030,1031],{},"$wslIP = (wsl hostname -I).Trim().Split()[0]\n",[261,1033,1035],{"class":263,"line":1034},30,[261,1036,1037],{},"if (-not $wslIP) {\n",[261,1039,1041],{"class":263,"line":1040},31,[261,1042,1043],{},"    Write-Error \"WSL not running. Start WSL first (e.g. open Ubuntu terminal).\"\n",[261,1045,1047],{"class":263,"line":1046},32,[261,1048,1049],{},"    exit 1\n",[261,1051,1053],{"class":263,"line":1052},33,[261,1054,479],{},[261,1056,1058],{"class":263,"line":1057},34,[261,1059,1060],{},"Write-Host \"WSL IP: $wslIP\" -ForegroundColor Green\n",[261,1062,1064],{"class":263,"line":1063},35,[261,1065,890],{"emptyLinePlaceholder":889},[261,1067,1069],{"class":263,"line":1068},36,[261,1070,1071],{},"$skipped = @()\n",[261,1073,1075],{"class":263,"line":1074},37,[261,1076,1077],{},"$added = @()\n",[261,1079,1081],{"class":263,"line":1080},38,[261,1082,890],{"emptyLinePlaceholder":889},[261,1084,1086],{"class":263,"line":1085},39,[261,1087,1088],{},"foreach ($range in $portRanges) {\n",[261,1090,1092],{"class":263,"line":1091},40,[261,1093,1094],{},"    for ($port = $range.Start; $port -le $range.End; $port++) {\n",[261,1096,1098],{"class":263,"line":1097},41,[261,1099,1100],{},"        # 先清掉旧规则\n",[261,1102,1104],{"class":263,"line":1103},42,[261,1105,1106],{},"        netsh interface portproxy delete v4tov4 listenport=$port listenaddress=0.0.0.0 2>$null | Out-Null\n",[261,1108,1110],{"class":263,"line":1109},43,[261,1111,890],{"emptyLinePlaceholder":889},[261,1113,1115],{"class":263,"line":1114},44,[261,1116,1117],{},"        # 检查端口是否被 Windows 进程占用\n",[261,1119,1121],{"class":263,"line":1120},45,[261,1122,1123],{},"        if (Test-PortInUse -Port $port) {\n",[261,1125,1127],{"class":263,"line":1126},46,[261,1128,1129],{},"            $conn = Get-NetTCPConnection -State Listen -LocalPort $port -ErrorAction SilentlyContinue | Select-Object -First 1\n",[261,1131,1133],{"class":263,"line":1132},47,[261,1134,1135],{},"            $procName = (Get-Process -Id $conn.OwningProcess -ErrorAction SilentlyContinue).Name\n",[261,1137,1139],{"class":263,"line":1138},48,[261,1140,1141],{},"            $skipped += \"$port ($procName)\"\n",[261,1143,1145],{"class":263,"line":1144},49,[261,1146,1147],{},"            continue\n",[261,1149,1151],{"class":263,"line":1150},50,[261,1152,997],{},[261,1154,1156],{"class":263,"line":1155},51,[261,1157,890],{"emptyLinePlaceholder":889},[261,1159,1161],{"class":263,"line":1160},52,[261,1162,1163],{},"        netsh interface portproxy add v4tov4 listenport=$port listenaddress=0.0.0.0 connectport=$port connectaddress=$wslIP | Out-Null\n",[261,1165,1167],{"class":263,"line":1166},53,[261,1168,1169],{},"        $added += $port\n",[261,1171,1173],{"class":263,"line":1172},54,[261,1174,1003],{},[261,1176,1178],{"class":263,"line":1177},55,[261,1179,890],{"emptyLinePlaceholder":889},[261,1181,1183],{"class":263,"line":1182},56,[261,1184,1185],{},"    # 防火墙规则按段添加\n",[261,1187,1189],{"class":263,"line":1188},57,[261,1190,1191],{},"    $ruleName = \"WSL2 Range $($range.Start)-$($range.End)\"\n",[261,1193,1195],{"class":263,"line":1194},58,[261,1196,1197],{},"    Remove-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue\n",[261,1199,1201],{"class":263,"line":1200},59,[261,1202,1203],{},"    New-NetFirewallRule -DisplayName $ruleName -Direction Inbound -Protocol TCP -LocalPort \"$($range.Start)-$($range.End)\" -Action Allow | Out-Null\n",[261,1205,1207],{"class":263,"line":1206},60,[261,1208,479],{},[261,1210,1212],{"class":263,"line":1211},61,[261,1213,890],{"emptyLinePlaceholder":889},[261,1215,1217],{"class":263,"line":1216},62,[261,1218,1219],{},"Write-Host \"\"\n",[261,1221,1223],{"class":263,"line":1222},63,[261,1224,1225],{},"Write-Host \"Added portproxy for $($added.Count) ports to WSL ($wslIP)\" -ForegroundColor Green\n",[261,1227,1229],{"class":263,"line":1228},64,[261,1230,1231],{},"if ($skipped.Count -gt 0) {\n",[261,1233,1235],{"class":263,"line":1234},65,[261,1236,1237],{},"    Write-Host \"Skipped ports already used by Windows:\" -ForegroundColor Yellow\n",[261,1239,1241],{"class":263,"line":1240},66,[261,1242,1243],{},"    $skipped | ForEach-Object { Write-Host \"  - $_\" -ForegroundColor Yellow }\n",[261,1245,1247],{"class":263,"line":1246},67,[261,1248,479],{},[261,1250,1252],{"class":263,"line":1251},68,[261,1253,890],{"emptyLinePlaceholder":889},[261,1255,1257],{"class":263,"line":1256},69,[261,1258,1219],{},[261,1260,1262],{"class":263,"line":1261},70,[261,1263,1264],{},"Write-Host \"Windows LAN IPs (use these to access from other machines):\" -ForegroundColor Cyan\n",[261,1266,1268],{"class":263,"line":1267},71,[261,1269,1270],{},"Get-NetIPAddress -AddressFamily IPv4 -PrefixOrigin Dhcp, Manual -ErrorAction SilentlyContinue |\n",[261,1272,1274],{"class":263,"line":1273},72,[261,1275,1276],{},"    Where-Object { $_.IPAddress -notlike \"169.254.*\" -and $_.IPAddress -ne \"127.0.0.1\" } |\n",[261,1278,1280],{"class":263,"line":1279},73,[261,1281,1282],{},"    Select-Object IPAddress, InterfaceAlias |\n",[261,1284,1286],{"class":263,"line":1285},74,[261,1287,1288],{},"    Format-Table -AutoSize\n",[234,1290,1292],{"id":1291},"_52-脚本关键设计","5.2 脚本关键设计",[145,1294,1295,1305],{},[148,1296,1297],{},[151,1298,1299,1302],{},[154,1300,1301],{},"设计",[154,1303,1304],{},"原因",[170,1306,1307,1315,1323,1334],{},[151,1308,1309,1312],{},[175,1310,1311],{},"检测端口占用，跳过 Windows 已用端口",[175,1313,1314],{},"避免抢占 Windows 服务",[151,1316,1317,1320],{},[175,1318,1319],{},"防火墙规则按\"段\"加一条，而不是每端口一条",[175,1321,1322],{},"防火墙列表干净",[151,1324,1325,1331],{},[175,1326,1327,1330],{},[17,1328,1329],{},"Test-PortInUse"," 排除 svchost 进程",[175,1332,1333],{},"portproxy 自己是 svchost，不算冲突",[151,1335,1336,1339],{},[175,1337,1338],{},"最后打印 Windows LAN IPs",[175,1340,1341],{},"方便告诉同事访问地址",[234,1343,1345],{"id":1344},"_53-用-powershell-写入正确编码的方法","5.3 用 PowerShell 写入正确编码的方法",[13,1347,1348],{},"如果用代码生成脚本文件，避免编码问题：",[79,1350,1352],{"className":802,"code":1351,"language":804,"meta":87,"style":87},"$content = @'\n脚本内容...\n'@\n\n# Write with UTF-8 BOM\n$utf8Bom = New-Object System.Text.UTF8Encoding $true\n[System.IO.File]::WriteAllText(\"C:\\Users\\\u003C用户名>\\scripts\\wsl-port-forward-batch.ps1\", $content, $utf8Bom)\n\n# 验证 BOM\n$bytes = [System.IO.File]::ReadAllBytes(\"C:\\Users\\\u003C用户名>\\scripts\\wsl-port-forward-batch.ps1\")\nWrite-Host \"First 3 bytes: $($bytes[0..2] -join ',')\"\n# 期望输出: 239,187,191 (= UTF-8 BOM)\n",[17,1353,1354,1359,1364,1369,1373,1378,1383,1388,1392,1397,1402,1407],{"__ignoreMap":87},[261,1355,1356],{"class":263,"line":264},[261,1357,1358],{},"$content = @'\n",[261,1360,1361],{"class":263,"line":270},[261,1362,1363],{},"脚本内容...\n",[261,1365,1366],{"class":263,"line":276},[261,1367,1368],{},"'@\n",[261,1370,1371],{"class":263,"line":282},[261,1372,890],{"emptyLinePlaceholder":889},[261,1374,1375],{"class":263,"line":288},[261,1376,1377],{},"# Write with UTF-8 BOM\n",[261,1379,1380],{"class":263,"line":443},[261,1381,1382],{},"$utf8Bom = New-Object System.Text.UTF8Encoding $true\n",[261,1384,1385],{"class":263,"line":449},[261,1386,1387],{},"[System.IO.File]::WriteAllText(\"C:\\Users\\\u003C用户名>\\scripts\\wsl-port-forward-batch.ps1\", $content, $utf8Bom)\n",[261,1389,1390],{"class":263,"line":464},[261,1391,890],{"emptyLinePlaceholder":889},[261,1393,1394],{"class":263,"line":470},[261,1395,1396],{},"# 验证 BOM\n",[261,1398,1399],{"class":263,"line":476},[261,1400,1401],{},"$bytes = [System.IO.File]::ReadAllBytes(\"C:\\Users\\\u003C用户名>\\scripts\\wsl-port-forward-batch.ps1\")\n",[261,1403,1404],{"class":263,"line":923},[261,1405,1406],{},"Write-Host \"First 3 bytes: $($bytes[0..2] -join ',')\"\n",[261,1408,1409],{"class":263,"line":929},[261,1410,1411],{},"# 期望输出: 239,187,191 (= UTF-8 BOM)\n",[234,1413,1415],{"id":1414},"_54-执行","5.4 执行",[13,1417,1418,1421],{},[55,1419,1420],{},"必须管理员权限","（New-NetFirewallRule、netsh portproxy 都需要）：",[79,1423,1425],{"className":802,"code":1424,"language":804,"meta":87,"style":87},"# 启动管理员 PowerShell（搜索 powershell → 右键 → 以管理员身份运行）\npowershell -ExecutionPolicy Bypass -File \"C:\\Users\\\u003C用户名>\\scripts\\wsl-port-forward-batch.ps1\"\n",[17,1426,1427,1432],{"__ignoreMap":87},[261,1428,1429],{"class":263,"line":264},[261,1430,1431],{},"# 启动管理员 PowerShell（搜索 powershell → 右键 → 以管理员身份运行）\n",[261,1433,1434],{"class":263,"line":270},[261,1435,1436],{},"powershell -ExecutionPolicy Bypass -File \"C:\\Users\\\u003C用户名>\\scripts\\wsl-port-forward-batch.ps1\"\n",[13,1438,1439],{},"预期输出：",[79,1441,1444],{"className":1442,"code":1443,"language":84},[82],"WSL IP: 172.20.151.103\n\nAdded portproxy for 162 ports to WSL (172.20.151.103)\n\nWindows LAN IPs (use these to access from other machines):\n\nIPAddress      InterfaceAlias\n---------      --------------\n192.168.1.99   以太网\n192.168.3.69   WLAN\n",[17,1445,1443],{"__ignoreMap":87},[72,1447],{},[26,1449,1451],{"id":1450},"六验证-测试","六、验证 + 测试",[234,1453,1455],{"id":1454},"_61-看-portproxy-规则","6.1 看 portproxy 规则",[79,1457,1459],{"className":802,"code":1458,"language":804,"meta":87,"style":87},"netsh interface portproxy show all\n",[17,1460,1461],{"__ignoreMap":87},[261,1462,1463],{"class":263,"line":264},[261,1464,1458],{},[13,1466,1467],{},"应该看到所有 24011-26064 的规则。",[234,1469,1471],{"id":1470},"_62-看-windows-实际监听情况关键","6.2 看 Windows 实际监听情况（关键）",[79,1473,1475],{"className":802,"code":1474,"language":804,"meta":87,"style":87},"Get-NetTCPConnection -State Listen |\n  Where-Object { $_.LocalPort -ge 24011 -and $_.LocalPort -le 26064 } |\n  Select-Object LocalAddress, LocalPort, OwningProcess |\n  Format-Table -AutoSize\n",[17,1476,1477,1481,1486,1491],{"__ignoreMap":87},[261,1478,1479],{"class":263,"line":264},[261,1480,816],{},[261,1482,1483],{"class":263,"line":270},[261,1484,1485],{},"  Where-Object { $_.LocalPort -ge 24011 -and $_.LocalPort -le 26064 } |\n",[261,1487,1488],{"class":263,"line":276},[261,1489,1490],{},"  Select-Object LocalAddress, LocalPort, OwningProcess |\n",[261,1492,1493],{"class":263,"line":282},[261,1494,1495],{},"  Format-Table -AutoSize\n",[13,1497,1498,1509],{},[55,1499,1500,1501,1504,1505,1508],{},"必须看到大量 ",[17,1502,1503],{},"0.0.0.0:24011","、",[17,1506,1507],{},"0.0.0.0:25011"," ... 的监听项","，OwningProcess 是 svchost。",[13,1511,1512,1513,1516],{},"如果输出为空 —— 即使 portproxy 规则在，但 Windows 端没真正监听，",[55,1514,1515],{},"就是踩坑了","。看第七节排查。",[234,1518,1520],{"id":1519},"_63-三层测试","6.3 三层测试",[79,1522,1524],{"className":590,"code":1523,"language":592,"meta":87,"style":87},"# WSL 内（验证服务本身）\ncurl http:\u002F\u002Flocalhost:25011\u002F\ncurl http:\u002F\u002F0.0.0.0:25011\u002F\n\nip addr show eth0 | grep \"inet \"   # 拿到 WSL IP\n",[17,1525,1526,1531,1539,1546,1550],{"__ignoreMap":87},[261,1527,1528],{"class":263,"line":264},[261,1529,1530],{"class":414},"# WSL 内（验证服务本身）\n",[261,1532,1533,1536],{"class":263,"line":270},[261,1534,1535],{"class":560},"curl",[261,1537,1538],{"class":407}," http:\u002F\u002Flocalhost:25011\u002F\n",[261,1540,1541,1543],{"class":263,"line":276},[261,1542,1535],{"class":560},[261,1544,1545],{"class":407}," http:\u002F\u002F0.0.0.0:25011\u002F\n",[261,1547,1548],{"class":263,"line":282},[261,1549,890],{"emptyLinePlaceholder":889},[261,1551,1552,1555,1558,1561,1564,1566,1568,1571],{"class":263,"line":288},[261,1553,1554],{"class":560},"ip",[261,1556,1557],{"class":407}," addr",[261,1559,1560],{"class":407}," show",[261,1562,1563],{"class":407}," eth0",[261,1565,605],{"class":390},[261,1567,608],{"class":560},[261,1569,1570],{"class":407}," \"inet \"",[261,1572,1573],{"class":414},"   # 拿到 WSL IP\n",[79,1575,1577],{"className":802,"code":1576,"language":804,"meta":87,"style":87},"# Windows 内\n# A) 直连 WSL IP（绕过 portproxy）\ncurl http:\u002F\u002F172.20.151.103:25011\u002F\n\n# B) 走 localhostForwarding\ncurl http:\u002F\u002Flocalhost:25011\u002F\ncurl http:\u002F\u002F127.0.0.1:25011\u002F\n\n# 注意：Windows 自己访问自己的 LAN IP 走 loopback 优化，结果不代表局域网体验，别用这个测\n",[17,1578,1579,1584,1589,1594,1598,1603,1608,1613,1617],{"__ignoreMap":87},[261,1580,1581],{"class":263,"line":264},[261,1582,1583],{},"# Windows 内\n",[261,1585,1586],{"class":263,"line":270},[261,1587,1588],{},"# A) 直连 WSL IP（绕过 portproxy）\n",[261,1590,1591],{"class":263,"line":276},[261,1592,1593],{},"curl http:\u002F\u002F172.20.151.103:25011\u002F\n",[261,1595,1596],{"class":263,"line":282},[261,1597,890],{"emptyLinePlaceholder":889},[261,1599,1600],{"class":263,"line":288},[261,1601,1602],{},"# B) 走 localhostForwarding\n",[261,1604,1605],{"class":263,"line":443},[261,1606,1607],{},"curl http:\u002F\u002Flocalhost:25011\u002F\n",[261,1609,1610],{"class":263,"line":449},[261,1611,1612],{},"curl http:\u002F\u002F127.0.0.1:25011\u002F\n",[261,1614,1615],{"class":263,"line":464},[261,1616,890],{"emptyLinePlaceholder":889},[261,1618,1619],{"class":263,"line":470},[261,1620,1621],{},"# 注意：Windows 自己访问自己的 LAN IP 走 loopback 优化，结果不代表局域网体验，别用这个测\n",[79,1623,1625],{"className":590,"code":1624,"language":592,"meta":87,"style":87},"# 局域网另一台机器 \u002F 手机（关键测试）\ncurl http:\u002F\u002F192.168.1.99:25011\u002F    # 用 Windows 的 LAN IP\n",[17,1626,1627,1632],{"__ignoreMap":87},[261,1628,1629],{"class":263,"line":264},[261,1630,1631],{"class":414},"# 局域网另一台机器 \u002F 手机（关键测试）\n",[261,1633,1634,1636,1639],{"class":263,"line":270},[261,1635,1535],{"class":560},[261,1637,1638],{"class":407}," http:\u002F\u002F192.168.1.99:25011\u002F",[261,1640,1641],{"class":414},"    # 用 Windows 的 LAN IP\n",[72,1643],{},[26,1645,1647],{"id":1646},"七踩坑排查","七、踩坑排查",[234,1649,1651,1652],{"id":1650},"_71-浏览器报-err_connection_refused","7.1 浏览器报 ",[17,1653,1654],{},"ERR_CONNECTION_REFUSED",[13,1656,1657,93],{},[55,1658,1659],{},"REFUSED ≠ TIMED_OUT",[95,1661,1662,1668],{},[37,1663,1664,1667],{},[55,1665,1666],{},"REFUSED","：TCP 包到了 Windows，但端口没在监听 → portproxy 没真正工作",[37,1669,1670,1673],{},[55,1671,1672],{},"TIMED_OUT","：包都到不了 Windows → 防火墙拦截 \u002F 网络不通",[13,1675,1676,1677,137],{},"REFUSED 几乎肯定是：",[55,1678,1679],{},"portproxy 规则注册了，但 Windows 端实际没在监听",[1681,1682,1683],"h4",{"id":1683},"诊断",[79,1685,1687],{"className":802,"code":1686,"language":804,"meta":87,"style":87},"# 看 25011 端口的 Windows 监听情况\nGet-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -eq 25011 }\n",[17,1688,1689,1694],{"__ignoreMap":87},[261,1690,1691],{"class":263,"line":264},[261,1692,1693],{},"# 看 25011 端口的 Windows 监听情况\n",[261,1695,1696],{"class":263,"line":270},[261,1697,1698],{},"Get-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -eq 25011 }\n",[13,1700,1701],{},"如果输出为空 → portproxy 失效，按下面修复。",[1681,1703,1705],{"id":1704},"修复重置-ip-helper-重新建规则","修复：重置 IP Helper + 重新建规则",[79,1707,1709],{"className":802,"code":1708,"language":804,"meta":87,"style":87},"# 管理员 PowerShell\nnetsh interface portproxy reset                # 清空所有 portproxy\nRestart-Service iphlpsvc -Force                # 重启 IP Helper\npowershell -ExecutionPolicy Bypass -File \"C:\\Users\\\u003C用户名>\\scripts\\wsl-port-forward-batch.ps1\"\nRestart-Service iphlpsvc -Force                # 再重启一次让规则生效\n\n# 验证\nGet-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -ge 24011 -and $_.LocalPort -le 26064 } | Format-Table -AutoSize\n",[17,1710,1711,1715,1720,1725,1729,1734,1738,1743],{"__ignoreMap":87},[261,1712,1713],{"class":263,"line":264},[261,1714,811],{},[261,1716,1717],{"class":263,"line":270},[261,1718,1719],{},"netsh interface portproxy reset                # 清空所有 portproxy\n",[261,1721,1722],{"class":263,"line":276},[261,1723,1724],{},"Restart-Service iphlpsvc -Force                # 重启 IP Helper\n",[261,1726,1727],{"class":263,"line":282},[261,1728,1436],{},[261,1730,1731],{"class":263,"line":288},[261,1732,1733],{},"Restart-Service iphlpsvc -Force                # 再重启一次让规则生效\n",[261,1735,1736],{"class":263,"line":443},[261,1737,890],{"emptyLinePlaceholder":889},[261,1739,1740],{"class":263,"line":449},[261,1741,1742],{},"# 验证\n",[261,1744,1745],{"class":263,"line":464},[261,1746,1747],{},"Get-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -ge 24011 -and $_.LocalPort -le 26064 } | Format-Table -AutoSize\n",[1681,1749,1751],{"id":1750},"修复进阶重启-winnat如果上面无效","修复进阶：重启 WinNAT（如果上面无效）",[79,1753,1755],{"className":802,"code":1754,"language":804,"meta":87,"style":87},"net stop hns      # Host Network Service\nnet stop winnat   # Windows NAT Driver\nnetsh interface portproxy reset\nnet start winnat\nnet start hns\n# 重新跑脚本\n",[17,1756,1757,1762,1767,1772,1777,1782],{"__ignoreMap":87},[261,1758,1759],{"class":263,"line":264},[261,1760,1761],{},"net stop hns      # Host Network Service\n",[261,1763,1764],{"class":263,"line":270},[261,1765,1766],{},"net stop winnat   # Windows NAT Driver\n",[261,1768,1769],{"class":263,"line":276},[261,1770,1771],{},"netsh interface portproxy reset\n",[261,1773,1774],{"class":263,"line":282},[261,1775,1776],{},"net start winnat\n",[261,1778,1779],{"class":263,"line":288},[261,1780,1781],{},"net start hns\n",[261,1783,1784],{"class":263,"line":443},[261,1785,1786],{},"# 重新跑脚本\n",[10,1788,1789],{},[13,1790,1791,1792,1795,1796,1799],{},"⚠️ ",[55,1793,1794],{},"winnat 经常停不下来","（被 Hyper-V\u002Fhns 依赖），停止失败也别慌，",[55,1797,1798],{},"直接重启电脑","是终极方案。重启后第一时间跑脚本，winnat 干净的初始状态下 portproxy 几乎一定能工作。",[234,1801,1803],{"id":1802},"_72-hyper-v-动态端口排除范围","7.2 Hyper-V 动态端口排除范围",[13,1805,1806,1807,1810],{},"WSL2 \u002F Docker Desktop 装好后，Hyper-V 会保留一批 TCP 端口作为自己的动态端口池。这些端口 ",[17,1808,1809],{},"netsh portproxy"," 加规则不报错，但实际监听失败。",[1681,1812,1813],{"id":1813},"查看排除范围",[79,1815,1817],{"className":802,"code":1816,"language":804,"meta":87,"style":87},"netsh interface ipv4 show excludedportrange protocol=tcp\n",[17,1818,1819],{"__ignoreMap":87},[261,1820,1821],{"class":263,"line":264},[261,1822,1816],{},[13,1824,1825],{},"输出示例：",[79,1827,1830],{"className":1828,"code":1829,"language":84},[82],"开始端口    结束端口\n----------    --------\n        80          80\n      5357        5357\n     10620       10719\n     ...\n     24719       24818     ← 这种段是动态保留的\n     25209       25308\n     ...\n     50000       50059  *\n",[17,1831,1829],{"__ignoreMap":87},[1681,1833,1834],{"id":1834},"如果你选的端口段撞上排除范围",[13,1836,1837,1840],{},[55,1838,1839],{},"方案 1","：换端口段（最简单）",[79,1842,1844],{"className":802,"code":1843,"language":804,"meta":87,"style":87},"$portRanges = @(\n    @{ Start = 52011; End = 52064 },    # 50000+ 几乎肯定空闲\n    @{ Start = 53011; End = 53064 }\n)\n",[17,1845,1846,1850,1855,1860],{"__ignoreMap":87},[261,1847,1848],{"class":263,"line":264},[261,1849,900],{},[261,1851,1852],{"class":263,"line":270},[261,1853,1854],{},"    @{ Start = 52011; End = 52064 },    # 50000+ 几乎肯定空闲\n",[261,1856,1857],{"class":263,"line":276},[261,1858,1859],{},"    @{ Start = 53011; End = 53064 }\n",[261,1861,1862],{"class":263,"line":282},[261,1863,920],{},[13,1865,1866],{},"WSL 内 dev server 端口跟着改。",[13,1868,1869,1872],{},[55,1870,1871],{},"方案 2","：把 Hyper-V 动态端口起点挪开",[79,1874,1876],{"className":802,"code":1875,"language":804,"meta":87,"style":87},"# 让 Hyper-V 的动态端口范围改到 50000 起，留出低位段\nnetsh int ipv4 set dynamic tcp start=50000 num=15000\n\n# 重启网络服务\nnet stop hns\nnet stop winnat\nnet start winnat\nnet start hns\n",[17,1877,1878,1883,1888,1892,1897,1902,1907,1911],{"__ignoreMap":87},[261,1879,1880],{"class":263,"line":264},[261,1881,1882],{},"# 让 Hyper-V 的动态端口范围改到 50000 起，留出低位段\n",[261,1884,1885],{"class":263,"line":270},[261,1886,1887],{},"netsh int ipv4 set dynamic tcp start=50000 num=15000\n",[261,1889,1890],{"class":263,"line":276},[261,1891,890],{"emptyLinePlaceholder":889},[261,1893,1894],{"class":263,"line":282},[261,1895,1896],{},"# 重启网络服务\n",[261,1898,1899],{"class":263,"line":288},[261,1900,1901],{},"net stop hns\n",[261,1903,1904],{"class":263,"line":443},[261,1905,1906],{},"net stop winnat\n",[261,1908,1909],{"class":263,"line":449},[261,1910,1776],{},[261,1912,1913],{"class":263,"line":464},[261,1914,1781],{},[234,1916,1918],{"id":1917},"_73-powershell-中文注释报错","7.3 PowerShell 中文注释报错",[13,1920,1921,93],{},[55,1922,1923],{},"症状",[79,1925,1928],{"className":1926,"code":1927,"language":84},[82],"所在位置 C:\\Users\\...\\xxx.ps1:23 字符: 1\n+ }\n+ ~\n表达式或语句中包含意外的标记\"}\"。\n",[17,1929,1927],{"__ignoreMap":87},[13,1931,1932,1934],{},[55,1933,1304],{},"：脚本是 UTF-8 无 BOM，Windows PowerShell 5.x 按 GBK 解读中文，字节流被打乱，解析崩。",[13,1936,1937,1940],{},[55,1938,1939],{},"修复方法 1","：用 UTF-8 with BOM 保存",[95,1942,1943,1946],{},[37,1944,1945],{},"VS Code：右下角点编码 → \"UTF-8 with BOM\" → 重新保存",[37,1947,1948],{},"记事本：另存为时下拉\"编码\" → UTF-8（自动带 BOM）",[13,1950,1951,1954],{},[55,1952,1953],{},"修复方法 2","：用 PowerShell 7 跑",[79,1956,1958],{"className":802,"code":1957,"language":804,"meta":87,"style":87},"# 装 PowerShell 7\nwinget install Microsoft.PowerShell\n\n# 跑（注意是 pwsh 不是 powershell）\npwsh -ExecutionPolicy Bypass -File \"...\"\n",[17,1959,1960,1965,1970,1974,1979],{"__ignoreMap":87},[261,1961,1962],{"class":263,"line":264},[261,1963,1964],{},"# 装 PowerShell 7\n",[261,1966,1967],{"class":263,"line":270},[261,1968,1969],{},"winget install Microsoft.PowerShell\n",[261,1971,1972],{"class":263,"line":276},[261,1973,890],{"emptyLinePlaceholder":889},[261,1975,1976],{"class":263,"line":282},[261,1977,1978],{},"# 跑（注意是 pwsh 不是 powershell）\n",[261,1980,1981],{"class":263,"line":288},[261,1982,1983],{},"pwsh -ExecutionPolicy Bypass -File \"...\"\n",[13,1985,1986],{},"PowerShell 7+ 默认 UTF-8 解码，永久解决。",[13,1988,1989,1992],{},[55,1990,1991],{},"修复方法 3","：注释全英文，避开编码问题",[234,1994,1996],{"id":1995},"_74-同事访问-timed_out-而不是-refused","7.4 同事访问 TIMED_OUT 而不是 REFUSED",[13,1998,1999],{},"如果同事访问超时（不是拒绝），通常是：",[34,2001,2002,2008,2014],{},[37,2003,2004,2007],{},[55,2005,2006],{},"Windows 防火墙拦了入站","：脚本应该加了防火墙规则，但有可能被组策略覆盖",[37,2009,2010,2013],{},[55,2011,2012],{},"火绒 \u002F 360 等安全软件的应用规则拦了入站连接","：临时关一下试试",[37,2015,2016,2019],{},[55,2017,2018],{},"路由器隔离","：公司网络可能开了\"客户端隔离\"，同一 WiFi 下的设备互不可见",[1681,2021,2023],{"id":2022},"排查命令在-windows-上","排查命令（在 Windows 上）",[79,2025,2027],{"className":802,"code":2026,"language":804,"meta":87,"style":87},"# 看防火墙规则有没有\nGet-NetFirewallRule -DisplayName \"WSL2 Range*\" | Format-Table DisplayName, Enabled, Direction\n\n# 临时关 Windows 防火墙试试（小心！测试完恢复）\nSet-NetFirewallProfile -Profile Domain,Public,Private -Enabled False\n# 测完恢复\nSet-NetFirewallProfile -Profile Domain,Public,Private -Enabled True\n",[17,2028,2029,2034,2039,2043,2048,2053,2058],{"__ignoreMap":87},[261,2030,2031],{"class":263,"line":264},[261,2032,2033],{},"# 看防火墙规则有没有\n",[261,2035,2036],{"class":263,"line":270},[261,2037,2038],{},"Get-NetFirewallRule -DisplayName \"WSL2 Range*\" | Format-Table DisplayName, Enabled, Direction\n",[261,2040,2041],{"class":263,"line":276},[261,2042,890],{"emptyLinePlaceholder":889},[261,2044,2045],{"class":263,"line":282},[261,2046,2047],{},"# 临时关 Windows 防火墙试试（小心！测试完恢复）\n",[261,2049,2050],{"class":263,"line":288},[261,2051,2052],{},"Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False\n",[261,2054,2055],{"class":263,"line":443},[261,2056,2057],{},"# 测完恢复\n",[261,2059,2060],{"class":263,"line":449},[261,2061,2062],{},"Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True\n",[234,2064,2066],{"id":2065},"_75-wsl-ip-变化导致-portproxy-失效","7.5 WSL IP 变化导致 portproxy 失效",[13,2068,2069,2072,2073,2076,2077,2080],{},[17,2070,2071],{},"wsl --shutdown"," 或重启 Windows 后，WSL 的 IP 可能从 ",[17,2074,2075],{},"172.20.151.103"," 变成 ",[17,2078,2079],{},"172.20.x.x"," 其他值。原 portproxy 规则指向旧 IP，全部失效。",[13,2082,2083,2085],{},[55,2084,1923],{},"：之前能访问，重启后突然 REFUSED。",[13,2087,2088,2091],{},[55,2089,2090],{},"修复","：重跑脚本（脚本会自动获取新 IP 并重建规则）。",[13,2093,2094,2097],{},[55,2095,2096],{},"最优","：用任务计划程序开机自动跑（见下一节）。",[234,2099,2101],{"id":2100},"_76-windows-本机用-localhost-访问-portproxy-端口不通","7.6 Windows 本机用 localhost 访问 portproxy 端口不通",[79,2103,2105],{"className":802,"code":2104,"language":804,"meta":87,"style":87},"curl http:\u002F\u002Flocalhost:25011\u002F     # ❌ Failed to connect\ncurl http:\u002F\u002F172.20.151.103:25011\u002F  # ✅ OK\n",[17,2106,2107,2112],{"__ignoreMap":87},[261,2108,2109],{"class":263,"line":264},[261,2110,2111],{},"curl http:\u002F\u002Flocalhost:25011\u002F     # ❌ Failed to connect\n",[261,2113,2114],{"class":263,"line":270},[261,2115,2116],{},"curl http:\u002F\u002F172.20.151.103:25011\u002F  # ✅ OK\n",[13,2118,2119,358,2122,137],{},[55,2120,2121],{},"这是 Windows + WSL2 已知 quirk",[55,2123,2124],{},"不影响局域网访问",[13,2126,2127,2128,2130],{},"原因：Windows 的 loopback 接口有内核优化，访问 ",[17,2129,23],{}," 不走 portproxy 链路，而是直接查 127.0.0.1 上的实际监听 —— portproxy 不在 127.0.0.1 上挂监听 socket，所以失败。",[13,2132,2133,2136],{},[55,2134,2135],{},"不用管这个","，你的目标是局域网访问，不是 localhost。",[13,2138,2139],{},"如果你想用 localhost 也能访问：",[79,2141,2143],{"className":255,"code":2142,"language":257,"meta":87,"style":87},"# .wslconfig\n[wsl2]\nnetworkingMode=NAT\nlocalhostForwarding=true    ← 显式开启\n",[17,2144,2145,2150,2154,2159],{"__ignoreMap":87},[261,2146,2147],{"class":263,"line":264},[261,2148,2149],{},"# .wslconfig\n",[261,2151,2152],{"class":263,"line":270},[261,2153,267],{},[261,2155,2156],{"class":263,"line":276},[261,2157,2158],{},"networkingMode=NAT\n",[261,2160,2161],{"class":263,"line":282},[261,2162,2163],{},"localhostForwarding=true    ← 显式开启\n",[13,2165,2166,2168],{},[17,2167,2071],{}," 重启 WSL 即可（这是 Windows 自己劫持 localhost 流量转给 WSL 的机制，跟 portproxy 是两条独立路径）。",[234,2170,2172],{"id":2171},"_77-复制粘贴-powershell-命令报错","7.7 复制粘贴 PowerShell 命令报错",[13,2174,2175,93],{},[55,2176,1923],{},[79,2178,2181],{"className":2179,"code":2180,"language":84},[82],"ParserError:\nLine | 1 | $wslIP = (wsl hostname -I).Trim().Split()[0] Write-Host \"WSL IP: $wsl …\n     | Unexpected token 'Write-Host'\n",[17,2182,2180],{"__ignoreMap":87},[13,2184,2185,2187],{},[55,2186,1304],{},"：复制时把多行挤成一行，PowerShell 不像 bash 自动识别分隔。",[13,2189,2190,2192,2193,2196],{},[55,2191,2090],{},"：用 ",[17,2194,2195],{},";"," 分隔或真换行：",[79,2198,2200],{"className":802,"code":2199,"language":804,"meta":87,"style":87},"# ❌ 错\n$wslIP = (wsl hostname -I).Trim().Split()[0] Write-Host \"WSL IP: $wslIP\"\n\n# ✅ 用分号\n$wslIP = (wsl hostname -I).Trim().Split()[0]; Write-Host \"WSL IP: $wslIP\"\n\n# ✅ 或真换行（最稳）\n$wslIP = (wsl hostname -I).Trim().Split()[0]\nWrite-Host \"WSL IP: $wslIP\"\n",[17,2201,2202,2207,2212,2216,2221,2226,2230,2235,2239],{"__ignoreMap":87},[261,2203,2204],{"class":263,"line":264},[261,2205,2206],{},"# ❌ 错\n",[261,2208,2209],{"class":263,"line":270},[261,2210,2211],{},"$wslIP = (wsl hostname -I).Trim().Split()[0] Write-Host \"WSL IP: $wslIP\"\n",[261,2213,2214],{"class":263,"line":276},[261,2215,890],{"emptyLinePlaceholder":889},[261,2217,2218],{"class":263,"line":282},[261,2219,2220],{},"# ✅ 用分号\n",[261,2222,2223],{"class":263,"line":288},[261,2224,2225],{},"$wslIP = (wsl hostname -I).Trim().Split()[0]; Write-Host \"WSL IP: $wslIP\"\n",[261,2227,2228],{"class":263,"line":443},[261,2229,890],{"emptyLinePlaceholder":889},[261,2231,2232],{"class":263,"line":449},[261,2233,2234],{},"# ✅ 或真换行（最稳）\n",[261,2236,2237],{"class":263,"line":464},[261,2238,1031],{},[261,2240,2241],{"class":263,"line":470},[261,2242,2243],{},"Write-Host \"WSL IP: $wslIP\"\n",[72,2245],{},[26,2247,2249],{"id":2248},"八开机自动运行任务计划程序","八、开机自动运行（任务计划程序）",[13,2251,2252],{},"每次重启都手动跑脚本太烦。用任务计划程序自动化。",[234,2254,2256],{"id":2255},"_81-一键注册任务","8.1 一键注册任务",[13,2258,2259,2262],{},[55,2260,2261],{},"管理员 PowerShell"," 跑一次即可：",[79,2264,2266],{"className":802,"code":2265,"language":804,"meta":87,"style":87},"$scriptPath = \"C:\\Users\\\u003C你的用户名>\\scripts\\wsl-port-forward-batch.ps1\"\n\n$action = New-ScheduledTaskAction `\n    -Execute \"powershell.exe\" `\n    -Argument \"-ExecutionPolicy Bypass -WindowStyle Hidden -File `\"$scriptPath`\"\"\n\n$trigger = New-ScheduledTaskTrigger -AtLogOn -User \"$env:USERDOMAIN\\$env:USERNAME\"\n$trigger.Delay = \"PT30S\"   # 登录后延迟 30 秒再跑，等 WSL 起来\n\n$principal = New-ScheduledTaskPrincipal `\n    -UserId \"$env:USERDOMAIN\\$env:USERNAME\" `\n    -LogonType Interactive `\n    -RunLevel Highest          # 用管理员权限跑\n\n$settings = New-ScheduledTaskSettingsSet `\n    -AllowStartIfOnBatteries `\n    -DontStopIfGoingOnBatteries `\n    -StartWhenAvailable `\n    -ExecutionTimeLimit (New-TimeSpan -Minutes 5)\n\nRegister-ScheduledTask `\n    -TaskName \"WSL Port Forward\" `\n    -Action $action `\n    -Trigger $trigger `\n    -Principal $principal `\n    -Settings $settings `\n    -Description \"Auto-configure WSL2 portproxy rules on login\" `\n    -Force\n",[17,2267,2268,2273,2277,2282,2287,2292,2296,2301,2306,2310,2315,2320,2325,2330,2334,2339,2344,2349,2354,2359,2363,2368,2373,2378,2383,2388,2393,2398],{"__ignoreMap":87},[261,2269,2270],{"class":263,"line":264},[261,2271,2272],{},"$scriptPath = \"C:\\Users\\\u003C你的用户名>\\scripts\\wsl-port-forward-batch.ps1\"\n",[261,2274,2275],{"class":263,"line":270},[261,2276,890],{"emptyLinePlaceholder":889},[261,2278,2279],{"class":263,"line":276},[261,2280,2281],{},"$action = New-ScheduledTaskAction `\n",[261,2283,2284],{"class":263,"line":282},[261,2285,2286],{},"    -Execute \"powershell.exe\" `\n",[261,2288,2289],{"class":263,"line":288},[261,2290,2291],{},"    -Argument \"-ExecutionPolicy Bypass -WindowStyle Hidden -File `\"$scriptPath`\"\"\n",[261,2293,2294],{"class":263,"line":443},[261,2295,890],{"emptyLinePlaceholder":889},[261,2297,2298],{"class":263,"line":449},[261,2299,2300],{},"$trigger = New-ScheduledTaskTrigger -AtLogOn -User \"$env:USERDOMAIN\\$env:USERNAME\"\n",[261,2302,2303],{"class":263,"line":464},[261,2304,2305],{},"$trigger.Delay = \"PT30S\"   # 登录后延迟 30 秒再跑，等 WSL 起来\n",[261,2307,2308],{"class":263,"line":470},[261,2309,890],{"emptyLinePlaceholder":889},[261,2311,2312],{"class":263,"line":476},[261,2313,2314],{},"$principal = New-ScheduledTaskPrincipal `\n",[261,2316,2317],{"class":263,"line":923},[261,2318,2319],{},"    -UserId \"$env:USERDOMAIN\\$env:USERNAME\" `\n",[261,2321,2322],{"class":263,"line":929},[261,2323,2324],{},"    -LogonType Interactive `\n",[261,2326,2327],{"class":263,"line":934},[261,2328,2329],{},"    -RunLevel Highest          # 用管理员权限跑\n",[261,2331,2332],{"class":263,"line":940},[261,2333,890],{"emptyLinePlaceholder":889},[261,2335,2336],{"class":263,"line":946},[261,2337,2338],{},"$settings = New-ScheduledTaskSettingsSet `\n",[261,2340,2341],{"class":263,"line":952},[261,2342,2343],{},"    -AllowStartIfOnBatteries `\n",[261,2345,2346],{"class":263,"line":958},[261,2347,2348],{},"    -DontStopIfGoingOnBatteries `\n",[261,2350,2351],{"class":263,"line":964},[261,2352,2353],{},"    -StartWhenAvailable `\n",[261,2355,2356],{"class":263,"line":970},[261,2357,2358],{},"    -ExecutionTimeLimit (New-TimeSpan -Minutes 5)\n",[261,2360,2361],{"class":263,"line":976},[261,2362,890],{"emptyLinePlaceholder":889},[261,2364,2365],{"class":263,"line":982},[261,2366,2367],{},"Register-ScheduledTask `\n",[261,2369,2370],{"class":263,"line":988},[261,2371,2372],{},"    -TaskName \"WSL Port Forward\" `\n",[261,2374,2375],{"class":263,"line":994},[261,2376,2377],{},"    -Action $action `\n",[261,2379,2380],{"class":263,"line":1000},[261,2381,2382],{},"    -Trigger $trigger `\n",[261,2384,2385],{"class":263,"line":1006},[261,2386,2387],{},"    -Principal $principal `\n",[261,2389,2390],{"class":263,"line":1012},[261,2391,2392],{},"    -Settings $settings `\n",[261,2394,2395],{"class":263,"line":1017},[261,2396,2397],{},"    -Description \"Auto-configure WSL2 portproxy rules on login\" `\n",[261,2399,2400],{"class":263,"line":1022},[261,2401,2402],{},"    -Force\n",[234,2404,2406],{"id":2405},"_82-验证","8.2 验证",[79,2408,2410],{"className":802,"code":2409,"language":804,"meta":87,"style":87},"# 查看任务\nGet-ScheduledTask -TaskName \"WSL Port Forward\"\n\n# 立即手动触发一次\nStart-ScheduledTask -TaskName \"WSL Port Forward\"\n\n# 等 10 秒看结果\nStart-Sleep 10\nGet-ScheduledTaskInfo -TaskName \"WSL Port Forward\" | Select-Object LastRunTime, LastTaskResult\n# LastTaskResult: 0 = 成功\n",[17,2411,2412,2417,2422,2426,2431,2436,2440,2445,2450,2455],{"__ignoreMap":87},[261,2413,2414],{"class":263,"line":264},[261,2415,2416],{},"# 查看任务\n",[261,2418,2419],{"class":263,"line":270},[261,2420,2421],{},"Get-ScheduledTask -TaskName \"WSL Port Forward\"\n",[261,2423,2424],{"class":263,"line":276},[261,2425,890],{"emptyLinePlaceholder":889},[261,2427,2428],{"class":263,"line":282},[261,2429,2430],{},"# 立即手动触发一次\n",[261,2432,2433],{"class":263,"line":288},[261,2434,2435],{},"Start-ScheduledTask -TaskName \"WSL Port Forward\"\n",[261,2437,2438],{"class":263,"line":443},[261,2439,890],{"emptyLinePlaceholder":889},[261,2441,2442],{"class":263,"line":449},[261,2443,2444],{},"# 等 10 秒看结果\n",[261,2446,2447],{"class":263,"line":464},[261,2448,2449],{},"Start-Sleep 10\n",[261,2451,2452],{"class":263,"line":470},[261,2453,2454],{},"Get-ScheduledTaskInfo -TaskName \"WSL Port Forward\" | Select-Object LastRunTime, LastTaskResult\n",[261,2456,2457],{"class":263,"line":476},[261,2458,2459],{},"# LastTaskResult: 0 = 成功\n",[13,2461,2462,2465],{},[17,2463,2464],{},"LastTaskResult"," 常见值：",[145,2467,2468,2478],{},[148,2469,2470],{},[151,2471,2472,2475],{},[154,2473,2474],{},"数值",[154,2476,2477],{},"含义",[170,2479,2480,2488,2496,2504],{},[151,2481,2482,2485],{},[175,2483,2484],{},"0",[175,2486,2487],{},"成功",[151,2489,2490,2493],{},[175,2491,2492],{},"267011",[175,2494,2495],{},"任务从未运行",[151,2497,2498,2501],{},[175,2499,2500],{},"1",[175,2502,2503],{},"脚本里有错误",[151,2505,2506,2509],{},[175,2507,2508],{},"2147942402",[175,2510,2511],{},"WSL 没启动（增加 Delay）",[234,2513,2515],{"id":2514},"_83-关键设计点","8.3 关键设计点",[145,2517,2518,2528],{},[148,2519,2520],{},[151,2521,2522,2525],{},[154,2523,2524],{},"设置",[154,2526,2527],{},"作用",[170,2529,2530,2540,2550,2560,2570,2580],{},[151,2531,2532,2537],{},[175,2533,2534],{},[17,2535,2536],{},"-AtLogOn",[175,2538,2539],{},"用户登录时触发（不是开机时，因为开机时 WSL 还没起来）",[151,2541,2542,2547],{},[175,2543,2544],{},[17,2545,2546],{},"Delay PT30S",[175,2548,2549],{},"等 WSL 启动完毕，避免拿不到 IP",[151,2551,2552,2557],{},[175,2553,2554],{},[17,2555,2556],{},"RunLevel Highest",[175,2558,2559],{},"自动以管理员权限运行，不弹 UAC",[151,2561,2562,2567],{},[175,2563,2564],{},[17,2565,2566],{},"WindowStyle Hidden",[175,2568,2569],{},"不弹黑窗口，后台静默执行",[151,2571,2572,2577],{},[175,2573,2574],{},[17,2575,2576],{},"ExecutionTimeLimit 5min",[175,2578,2579],{},"超时保护，万一卡住自动结束",[151,2581,2582,2587],{},[175,2583,2584],{},[17,2585,2586],{},"-Force",[175,2588,2589],{},"已存在同名任务会覆盖",[234,2591,2593],{"id":2592},"_84-调整-删除","8.4 调整 \u002F 删除",[79,2595,2597],{"className":802,"code":2596,"language":804,"meta":87,"style":87},"# 调整延迟\n$task = Get-ScheduledTask -TaskName \"WSL Port Forward\"\n$task.Triggers[0].Delay = \"PT60S\"\n$task | Set-ScheduledTask\n\n# 删除\nUnregister-ScheduledTask -TaskName \"WSL Port Forward\" -Confirm:$false\n",[17,2598,2599,2604,2609,2614,2619,2623,2628],{"__ignoreMap":87},[261,2600,2601],{"class":263,"line":264},[261,2602,2603],{},"# 调整延迟\n",[261,2605,2606],{"class":263,"line":270},[261,2607,2608],{},"$task = Get-ScheduledTask -TaskName \"WSL Port Forward\"\n",[261,2610,2611],{"class":263,"line":276},[261,2612,2613],{},"$task.Triggers[0].Delay = \"PT60S\"\n",[261,2615,2616],{"class":263,"line":282},[261,2617,2618],{},"$task | Set-ScheduledTask\n",[261,2620,2621],{"class":263,"line":288},[261,2622,890],{"emptyLinePlaceholder":889},[261,2624,2625],{"class":263,"line":443},[261,2626,2627],{},"# 删除\n",[261,2629,2630],{"class":263,"line":449},[261,2631,2632],{},"Unregister-ScheduledTask -TaskName \"WSL Port Forward\" -Confirm:$false\n",[72,2634],{},[26,2636,2638],{"id":2637},"九完整工作流每次新项目","九、完整工作流（每次新项目）",[34,2640,2641,2651,2670],{},[37,2642,2643,2646,2647,2650],{},[55,2644,2645],{},"服务端","：dev server ",[17,2648,2649],{},"host: '0.0.0.0'","，端口选 24011-26064 之一",[37,2652,2653,2656,2657],{},[55,2654,2655],{},"客户端","：\n",[95,2658,2659,2664],{},[37,2660,2661,2662],{},"本机：",[17,2663,45],{},[37,2665,2666,2667],{},"局域网：",[17,2668,2669],{},"http:\u002F\u002F\u003CWindows的LAN IP>:25011\u002F",[37,2671,2672,2675],{},[55,2673,2674],{},"不需要任何手动操作"," —— 开机时任务计划自动跑脚本",[13,2677,2678,2679,137],{},"新项目零配置，",[55,2680,2681],{},"端口段一次定好用一辈子",[72,2683],{},[26,2685,2687],{"id":2686},"十附录诊断命令速查","十、附录：诊断命令速查",[234,2689,2691],{"id":2690},"windows-侧","Windows 侧",[79,2693,2695],{"className":802,"code":2694,"language":804,"meta":87,"style":87},"# 看 portproxy 规则\nnetsh interface portproxy show all\n\n# 看 Windows 监听情况\nGet-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -ge 24011 -and $_.LocalPort -le 26064 } | Format-Table\n\n# 看 Hyper-V 端口排除范围\nnetsh interface ipv4 show excludedportrange protocol=tcp\n\n# 看防火墙规则\nGet-NetFirewallRule -DisplayName \"WSL2 Range*\"\n\n# 拿 WSL IP\nwsl hostname -I\n\n# 拿 Windows LAN IP\nGet-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.IPAddress -like \"192.168.*\" -or $_.IPAddress -like \"10.*\" }\n\n# IP Helper 服务状态\nGet-Service iphlpsvc\n\n# 清空所有 portproxy\nnetsh interface portproxy reset\n",[17,2696,2697,2702,2706,2710,2715,2720,2724,2729,2733,2737,2742,2747,2751,2756,2761,2765,2770,2775,2779,2784,2789,2793,2798],{"__ignoreMap":87},[261,2698,2699],{"class":263,"line":264},[261,2700,2701],{},"# 看 portproxy 规则\n",[261,2703,2704],{"class":263,"line":270},[261,2705,1458],{},[261,2707,2708],{"class":263,"line":276},[261,2709,890],{"emptyLinePlaceholder":889},[261,2711,2712],{"class":263,"line":282},[261,2713,2714],{},"# 看 Windows 监听情况\n",[261,2716,2717],{"class":263,"line":288},[261,2718,2719],{},"Get-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -ge 24011 -and $_.LocalPort -le 26064 } | Format-Table\n",[261,2721,2722],{"class":263,"line":443},[261,2723,890],{"emptyLinePlaceholder":889},[261,2725,2726],{"class":263,"line":449},[261,2727,2728],{},"# 看 Hyper-V 端口排除范围\n",[261,2730,2731],{"class":263,"line":464},[261,2732,1816],{},[261,2734,2735],{"class":263,"line":470},[261,2736,890],{"emptyLinePlaceholder":889},[261,2738,2739],{"class":263,"line":476},[261,2740,2741],{},"# 看防火墙规则\n",[261,2743,2744],{"class":263,"line":923},[261,2745,2746],{},"Get-NetFirewallRule -DisplayName \"WSL2 Range*\"\n",[261,2748,2749],{"class":263,"line":929},[261,2750,890],{"emptyLinePlaceholder":889},[261,2752,2753],{"class":263,"line":934},[261,2754,2755],{},"# 拿 WSL IP\n",[261,2757,2758],{"class":263,"line":940},[261,2759,2760],{},"wsl hostname -I\n",[261,2762,2763],{"class":263,"line":946},[261,2764,890],{"emptyLinePlaceholder":889},[261,2766,2767],{"class":263,"line":952},[261,2768,2769],{},"# 拿 Windows LAN IP\n",[261,2771,2772],{"class":263,"line":958},[261,2773,2774],{},"Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.IPAddress -like \"192.168.*\" -or $_.IPAddress -like \"10.*\" }\n",[261,2776,2777],{"class":263,"line":964},[261,2778,890],{"emptyLinePlaceholder":889},[261,2780,2781],{"class":263,"line":970},[261,2782,2783],{},"# IP Helper 服务状态\n",[261,2785,2786],{"class":263,"line":976},[261,2787,2788],{},"Get-Service iphlpsvc\n",[261,2790,2791],{"class":263,"line":982},[261,2792,890],{"emptyLinePlaceholder":889},[261,2794,2795],{"class":263,"line":988},[261,2796,2797],{},"# 清空所有 portproxy\n",[261,2799,2800],{"class":263,"line":994},[261,2801,1771],{},[234,2803,2805],{"id":2804},"wsl-侧","WSL 侧",[79,2807,2809],{"className":590,"code":2808,"language":592,"meta":87,"style":87},"# 看服务监听情况\nss -tlnp | grep 25011\n\n# 拿 WSL IP\nip addr show eth0 | grep \"inet \"\n\n# 测试本地服务\ncurl -v http:\u002F\u002Flocalhost:25011\u002F\ncurl -v http:\u002F\u002F0.0.0.0:25011\u002F\n",[17,2810,2811,2816,2828,2832,2836,2853,2857,2862,2871],{"__ignoreMap":87},[261,2812,2813],{"class":263,"line":264},[261,2814,2815],{"class":414},"# 看服务监听情况\n",[261,2817,2818,2820,2822,2824,2826],{"class":263,"line":270},[261,2819,599],{"class":560},[261,2821,602],{"class":379},[261,2823,605],{"class":390},[261,2825,608],{"class":560},[261,2827,611],{"class":379},[261,2829,2830],{"class":263,"line":276},[261,2831,890],{"emptyLinePlaceholder":889},[261,2833,2834],{"class":263,"line":282},[261,2835,2755],{"class":414},[261,2837,2838,2840,2842,2844,2846,2848,2850],{"class":263,"line":288},[261,2839,1554],{"class":560},[261,2841,1557],{"class":407},[261,2843,1560],{"class":407},[261,2845,1563],{"class":407},[261,2847,605],{"class":390},[261,2849,608],{"class":560},[261,2851,2852],{"class":407}," \"inet \"\n",[261,2854,2855],{"class":263,"line":443},[261,2856,890],{"emptyLinePlaceholder":889},[261,2858,2859],{"class":263,"line":449},[261,2860,2861],{"class":414},"# 测试本地服务\n",[261,2863,2864,2866,2869],{"class":263,"line":464},[261,2865,1535],{"class":560},[261,2867,2868],{"class":379}," -v",[261,2870,1538],{"class":407},[261,2872,2873,2875,2877],{"class":263,"line":470},[261,2874,1535],{"class":560},[261,2876,2868],{"class":379},[261,2878,1545],{"class":407},[72,2880],{},[26,2882,2884],{"id":2883},"十一其他相关方案不推荐但记录一下","十一、其他相关方案（不推荐但记录一下）",[234,2886,2888],{"id":2887},"_111-wsl-bridged-模式","11.1 WSL Bridged 模式",[13,2890,2891],{},"技术上可以实现\"WSL 拿 192.168.x.x\"：",[79,2893,2895],{"className":255,"code":2894,"language":257,"meta":87,"style":87},"[wsl2]\nnetworkingMode=bridged\nvmSwitch=WSLExternal       # 需要先在 Hyper-V 管理器手动建一个\"外部\"虚拟交换机\nipv6=true\n",[17,2896,2897,2901,2906,2911],{"__ignoreMap":87},[261,2898,2899],{"class":263,"line":264},[261,2900,267],{},[261,2902,2903],{"class":263,"line":270},[261,2904,2905],{},"networkingMode=bridged\n",[261,2907,2908],{"class":263,"line":276},[261,2909,2910],{},"vmSwitch=WSLExternal       # 需要先在 Hyper-V 管理器手动建一个\"外部\"虚拟交换机\n",[261,2912,2913],{"class":263,"line":282},[261,2914,2915],{},"ipv6=true\n",[13,2917,2918,93],{},[55,2919,2920],{},"为什么不推荐",[95,2922,2923,2926,2931,2934,2937],{},[37,2924,2925],{},"外部虚拟交换机会\"接管\"物理网卡，跟 VMware NAT\u002F桥接虚拟网卡打架",[37,2927,2928,2930],{},[17,2929,210],{}," 复发风险高",[37,2932,2933],{},"火绒 ARP 防护可能再次干扰",[37,2935,2936],{},"公司交换机 DHCP Snooping \u002F 端口隔离可能让 WSL 拿不到 IP",[37,2938,2939],{},"DNS 和 Clash 配置都得重做",[234,2941,2943],{"id":2942},"_112-ssh-反向隧道","11.2 ssh 反向隧道",[13,2945,2946],{},"如果只是临时给某个同事看一下：",[79,2948,2950],{"className":590,"code":2949,"language":592,"meta":87,"style":87},"# WSL 内\nssh -R 0.0.0.0:25011:localhost:25011 user@some-public-server.com\n",[17,2951,2952,2957],{"__ignoreMap":87},[261,2953,2954],{"class":263,"line":264},[261,2955,2956],{"class":414},"# WSL 内\n",[261,2958,2959,2962,2965,2968],{"class":263,"line":270},[261,2960,2961],{"class":560},"ssh",[261,2963,2964],{"class":379}," -R",[261,2966,2967],{"class":407}," 0.0.0.0:25011:localhost:25011",[261,2969,2970],{"class":407}," user@some-public-server.com\n",[13,2972,2973],{},"但这是云端方案，跟\"局域网访问\"不同。",[234,2975,2977],{"id":2976},"_113-ngrok-frp-sish","11.3 ngrok \u002F frp \u002F sish",[13,2979,2980,2981,2984,2985,137],{},"云隧道服务，适合",[55,2982,2983],{},"公网展示","（让外部互联网访问你的 dev 环境），不适合",[55,2986,2987],{},"局域网内部访问",[72,2989],{},[26,2991,2993],{"id":2992},"十二结语","十二、结语",[13,2995,2996,2997,3000,3001,137],{},"WSL2 网络的痛点根源是 ",[55,2998,2999],{},"NAT 子网隔离","，但 Windows 没有提供像 VMware \"桥接模式\"那样的开箱即用方案（mirrored 又坑多）。所以",[55,3002,3003],{},"手动 portproxy 是务实之选",[13,3005,3006],{},"把这套配置一次做好：",[95,3008,3009,3012,3015,3018],{},[37,3010,3011],{},"✅ 项目端口固定 → 团队规范",[37,3013,3014],{},"✅ 批量转发脚本 → 一次配好整段",[37,3016,3017],{},"✅ 任务计划程序 → 开机自动跑",[37,3019,3020],{},"✅ 端口冲突自动跳过 → 不抢 Windows 服务",[13,3022,3023],{},"之后开发体验跟 Linux 物理机一致：写代码、起 dev server、丢给同事 URL，一气呵成。",[13,3025,3026,3027,3030,3031,137],{},"如果你的环境跟我一样有 ",[55,3028,3029],{},"Clash TUN + 火绒","，强烈建议同时看：",[3032,3033,3035],"a",{"href":3034},"\u002Fblog\u002Fwsl2-clash-huorong-troubleshooting","WSL2 + Clash TUN + 火绒的网络踩坑记录",[13,3037,3038,3039,137],{},"入门请看：",[3032,3040,3042],{"href":3041},"\u002Fblog\u002Fwsl2-setup-guide","WSL2 初始使用与开发环境完整配置指南",[3044,3045,3046],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":87,"searchDepth":276,"depth":276,"links":3048},[3049,3050,3051,3054,3060,3066,3071,3090,3096,3097,3101,3106],{"id":28,"depth":270,"text":29},{"id":76,"depth":270,"text":77},{"id":142,"depth":270,"text":143,"children":3052},[3053],{"id":236,"depth":276,"text":237},{"id":338,"depth":270,"text":339,"children":3055},[3056,3058,3059],{"id":342,"depth":276,"text":3057},"4.1 让 WSL 内的服务监听 0.0.0.0（最容易遗漏的一步）",{"id":624,"depth":276,"text":625},{"id":798,"depth":276,"text":799},{"id":842,"depth":270,"text":843,"children":3061},[3062,3063,3064,3065],{"id":846,"depth":276,"text":847},{"id":1291,"depth":276,"text":1292},{"id":1344,"depth":276,"text":1345},{"id":1414,"depth":276,"text":1415},{"id":1450,"depth":270,"text":1451,"children":3067},[3068,3069,3070],{"id":1454,"depth":276,"text":1455},{"id":1470,"depth":276,"text":1471},{"id":1519,"depth":276,"text":1520},{"id":1646,"depth":270,"text":1647,"children":3072},[3073,3079,3083,3084,3087,3088,3089],{"id":1650,"depth":276,"text":3074,"children":3075},"7.1 浏览器报 ERR_CONNECTION_REFUSED",[3076,3077,3078],{"id":1683,"depth":282,"text":1683},{"id":1704,"depth":282,"text":1705},{"id":1750,"depth":282,"text":1751},{"id":1802,"depth":276,"text":1803,"children":3080},[3081,3082],{"id":1813,"depth":282,"text":1813},{"id":1834,"depth":282,"text":1834},{"id":1917,"depth":276,"text":1918},{"id":1995,"depth":276,"text":1996,"children":3085},[3086],{"id":2022,"depth":282,"text":2023},{"id":2065,"depth":276,"text":2066},{"id":2100,"depth":276,"text":2101},{"id":2171,"depth":276,"text":2172},{"id":2248,"depth":270,"text":2249,"children":3091},[3092,3093,3094,3095],{"id":2255,"depth":276,"text":2256},{"id":2405,"depth":276,"text":2406},{"id":2514,"depth":276,"text":2515},{"id":2592,"depth":276,"text":2593},{"id":2637,"depth":270,"text":2638},{"id":2686,"depth":270,"text":2687,"children":3098},[3099,3100],{"id":2690,"depth":276,"text":2691},{"id":2804,"depth":276,"text":2805},{"id":2883,"depth":270,"text":2884,"children":3102},[3103,3104,3105],{"id":2887,"depth":276,"text":2888},{"id":2942,"depth":276,"text":2943},{"id":2976,"depth":276,"text":2977},{"id":2992,"depth":270,"text":2993},"devops","2026-05-27","WSL2 里跑起 dev server 本机能开、同事却访问不到？讲透 NAT 网络拓扑、portproxy 方案、防火墙放行与开机自动化脚本。",false,"md",{},"\u002Fblog\u002Fwsl2-port-forwarding-guide",{"title":5,"description":3109},null,"blog\u002Fwsl2-port-forwarding-guide",[3118,3119,3120,3121,3122],"wsl2","network","port-forwarding","guide","troubleshooting","nyge9m3vtGE5BB60ZuCsBpO0qqAG_DA1tVjRRU6dFGI",{"prev":3125,"next":3128},{"path":3034,"title":3126,"description":3127},"WSL2 + Clash + 火绒 踩坑排查记录","代理开着 WSL2 就断网，关掉又访问不了外网。记录一次从 ARP 拦截日志追到 TUN 模式冲突的完整排查过程，以及重建 Hyper-V 网络栈的正确恢复步骤。",{"path":3041,"title":3129,"description":3130},"WSL2 初始使用与开发环境配置指南","从安装发行版到搭好可用的前端开发环境，涵盖磁盘迁移、镜像加速、终端配置与常见初始化陷阱。",1788337377921]