通过本地网络连接WSL2服务器

148

我正在使用WSL2/Ubuntu在我的Windows 10电脑上开发一个Rails应用,感觉很棒!但问题是我无法从同一网络中的另一台计算机连接到我的服务器

为了更清楚地说明,我在本地主机上运行一个Puma服务器,端口为localhost:3000。

我尝试了以下方法:

  1. 直接连接以太网适配器vEthernet(WSL)分配的IP地址 -> 172.26.208.1:3000
  2. 直接连接主机的IPv4地址 -> 192.168.0.115
  3. 添加防火墙例外(使用Bitdefender)
  4. 绑定上述IP地址rails s -b 172.26.208.1 -p 3000

到目前为止,以上所有方法都没有奏效... 我想做的是:

  • 在另一台笔记本电脑/平板电脑/手机上测试网站
  • 从另一台计算机上使用VScode

是否还有其他需要注意的地方,可以让我至少正确地查看网站?(对于VScode部分的任何评论将不胜感激)


以下任何答案都对我没有用(尝试在端口7946上连接TCP和UDP的虚拟化serf集群)。我使用了这里的解决方案:https://github.com/microsoft/WSL/issues/4150#issuecomment-1018524753(也在WSLHostPatcher网站上提到)。唯一的区别是我在`.wslconf`中添加了`dhcp=true`,因为wsl eth0保留了先前的IP(用于先前的Hyper-v适配器)。 - James Schinner
18个回答

169

看这个视频,它帮助了我:

https://www.youtube.com/watch?v=yCK3easuYm4

netsh interface portproxy add v4tov4 listenport=<port-to-listen> listenaddress=0.0.0.0 connectport=<port-to-forward> connectaddress=<forward-to-this-IP-address>
例如
netsh interface portproxy add v4tov4 listenport=3000 listenaddress=0.0.0.0 connectport=3000 connectaddress=172.30.16.3

微软在其WSL1与WSL2比较页面上发布了一些信息。


35
谢谢,这非常有效。对于任何不想观看整个视频的人来说,需要在管理员权限的PowerShell提示符下运行,而不是从WSL中运行,connectaddress应该是你的WSL IP,并且你可能需要允许端口通过Windows防火墙。顺便说一句,这与顶部答案中的脚本执行的是同样的操作,但只需看到必要的命令比整个脚本更好。 - Mogzol
4
很遗憾,这对我来说无法工作。我正在尝试使用iPhone连接到我的本地Web服务器(以测试网站的移动版本)。我的代码在WSL机器上运行,并且在npm start后在端口1337上打开服务器。因此,我输入了以下内容:netsh interface portproxy add v4tov4 listenport=1337 listenaddress=0.0.0.0 connectport=1337 connectaddress=127.22.240.165。我想也许我可以在iPhone上尝试:192.168.1.32:1337,但也许我误解了程序...有什么帮助吗? - jmpp
3
我想补充一下,我的WSL在powershell中运行ipconfig后的IPv4与Ubuntu所说的不同,这是一个奇怪的问题。 Ubuntu所说的IP是正确的,不知何故,ipconfig命令中的WSL ipv4是错误的。可能与以太网端口有关。 - JakeAve
9
别忘了将监听端口添加到防火墙中,以便能够连接:netsh advfirewall firewall add rule name="打开端口3000" dir=in action=allow protocol=TCP localport=3000 - Jos
2
@JakeAve 我觉得这个信息很关键。我也有这个问题。我的猜测是Windows将WSL的流量转发到ipconfig中列出的IP,但WSL在WSL内部由ip addr列出的IP上进行监听。 - Ian Sudbery
显示剩余5条评论

85

首先,您需要在计算机中打开一个端口,以便从网络访问它。

netsh advfirewall firewall add rule name="Allowing LAN connections" dir=in action=allow protocol=TCP localport=5000

在打开端口后(以我为例,是5000),您需要将该端口从WSL转发到您的应用程序侦听的端口。

netsh interface portproxy add v4tov4 listenaddress=0.0.0.0 listenport=5000 connectaddress=localhost connectport=<the port that your app is listening on>

注意:我将 connectaddress 设置为 localhost 而不是 WSL 的 IP 地址,因为默认情况下发送到 localhost 的请求会被转发到 WSL。这样做可以避免每次重启机器时都需要设置端口转发,因为 WSL 的 IP 地址是动态的。


11
我不知道为什么这不是最佳答案。基本上只需要粘贴两行PowerShell代码,而无需下载任何内容。 - Hunkoys
5
嗯,对我来说本地主机没有起作用,我不得不直接指定WSL IP地址。 - Ben Creasy
2
只有本地主机或127.0.0.1对我有效。在我的情况下,指定WSL IP无效。 - Naourass Derouichi
2
在我的Win11上,使用WSL2进行了所有更新,并将localhost作为连接地址,这对我起作用了。 - aXul
3
connectaddress设为localhost对我有用。我尝试按照微软的文档来解决这个问题。他们说要把connectaddress设为WSL2虚拟机的IP地址,但这对我没有用。 - ChrisCrossCrash
显示剩余12条评论

45

选项1:使用端口转发

这个方法对我起了作用:

  1. 通过xmeng1的脚本运行端口转发:https://gist.github.com/xmeng1/aae4b223e9ccc089911ee764928f5486

该脚本中的防火墙命令在我的系统上不起作用。因此,我完全停用了Windows防火墙,并使用以下简化版本。(最终用户将使用第三方防火墙,所以这没问题)。

$remoteport = bash.exe -c "ifconfig eth0 | grep 'inet '"
$found = $remoteport -match '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}';

if ($found)
{
  $remoteport = $matches[0];
} else {
  echo "The Script Exited, the ip address of WSL 2 cannot be found";
  exit;
}

#[Ports]

#All the ports you want to forward separated by coma
$ports=@(123,456);

#[Static ip]
#You can change the addr to your ip config to listen to a specific address
$addr='0.0.0.0';
$ports_a = $ports -join ",";

for ($i = 0; $i -lt $ports.length; $i++)
{
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport=$port listenaddress=$addr";
  iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=$addr connectport=$port connectaddress=$remoteport";
}

#Then so something, e.g.
#bash.exe -c "cd /g-inverter; ./start_docker.sh"

在脚本中使用ifconfig可能需要执行“apt install net-tools”。另外,在某个互联网上有一种解决方案,可以不使用ifconfig,但我现在没有链接。

注意

此方法仅适用于TCP流量。netsh interface portproxy不支持UDP流量的端口转发。

选项2:桥接模式

解决方案:从NAT模式切换到桥接模式

WSL2默认为NAT模式。因此,wsl2系统具有与主机不同子网中的另一个IP。对外部对等体而言,电脑只能通过Windows IP可见,并且wsl2 IP/网络是隐藏的/内部的。因此,所有流量都需要由Windows IP接受,然后转发到wsl2 IP(端口转发)。

还有另一种称为桥接模式的模式。在桥接模式下,您的网络接口卡将被共享到wsl2系统,并且它将在wsl2中获得自己的IP/网络。因此,实际上您的网络卡被共享到两个系统(Windows / wsl2)并且将拥有两个IP,就好像您有两个具有各自网络卡的系统一样。很酷的一点是:当Windows也使用相同的端口时,您将永远不会有端口冲突,就像wsl2应用程序(如111)一样。

启用桥接模式

以管理员身份打开Hyper-V管理器

选择你的电脑,打开虚拟交换机管理器

选择WSL

设置为外部网络

选择流量通过的网络卡

然后登录到wsl2终端并配置IP地址。例如:

sudo ip addr add 192.168.0.116/24 dev eth0

您需要使用另一个免费的IP地址(不是您的Windows IP地址)。如果您的网络有DHCP服务器,则可以通过以下方式获取wsl:

sudo ip addr flush dev eth0
sudo dhclient eth0

注意

我还没有详细说明在这种情况下如何使DNS正常工作,以防您仍然希望访问互联网(例如apt等)。有一些来自MS的文档写在/etc/resolv.conf中,也许执行其中所写的内容并安装resolvconf(在上述所有步骤之前,因为一旦开始进行桥接,您就无法连接到互联网)可能会有所帮助。


3
更新:这种方法变得很繁琐,网络上的帖子越来越长,人们也越来越难以应对。我们决定WSL2还不够成熟,不能用于稳定的服务器托管,最终决定在Windows上安装所有服务器。WSL2 docker实例将文件放入Windows文件夹中,然后使用Windows服务器进行托管。这意味着我们需要在每台PC上许可、安装和维护第三方产品。但我们还不太确定WSL2是否已经准备好面向行业。我相信这在未来几年内会变得更好。我们现在已经100%长期稳定了。 - Roelof
1
桥接模式听起来可能是一个不错的解决方案,但我的运行WSL2的机器没有启用Hyper-V Windows扩展,阅读Hyper-V文档让我担心这个功能可能会引起问题。 - Mike Slinn
7
“防火墙脚本对我没起作用,所以我把防火墙完全关闭了”这是很糟糕的建议。防火墙脚本对我来说是有效的,所以请大家保持防火墙的完整性! - Orion Edwards
3
由于虚拟交换机管理器无法将 WSL 开关更改为外部选项,因此选项2不可行。它会抛出一个错误:"switch port delete failed(开关端口删除失败)"。 - gabtzi
3
准备好了,因为一旦你重新启动,它会彻底摧毁你的Windows网络适配器,以至于你必须前往控制面板->网络和Internet->高级网络设置->网络重置。然后重新启动。接下来,问题来了。WSL也被破坏了,所以你需要注销并重新安装。祝你好运。微软也完全搞砸了这个问题。像怎么可能不能将UDP端口转发到WSL?谁TM的主意是这样的? - james28909
显示剩余12条评论

31

WSL2在本地接口上公开端口(这就是为什么在Windows中,当您的8080服务运行在WSL2上时,您可以通过localhost: 8080访问),但它们侦听127.0.0.1(这就是为什么您无法在局域网中的其他计算机上访问yourhostname: 8080)。

有一个名为WSLHostPatcher的工具可以解决此问题,您可以在此处找到它(可以通过“Releases”部分下载:https://github.com/CzBiX/WSLHostPatcher)。

WSLHostPatcher更改监听所有IP地址的行为,将任何WSL2服务公开给网络中的所有计算机。

  1. https://github.com/CzBiX/WSLHostPatcher/releases下载最新版本(我测试了v0.1.0)。
  2. 解压文件。
  3. 运行WSLHostPatcher.exe
  4. 在WSL2中(重新)启动您的服务。

现在,该服务应该可以通过网络中的其他计算机访问。


这听起来很不错,但是指示不起作用。没有WSLHostPatcher.exe文件。存储库上的星星不多... 这是合法的吗? - jdunk
2
WSLHostPatcher.exe在release.zip文件中。如果你持怀疑态度,你可以在检查源代码后从repo自己编译它。这不是我的repo,所以我不能保证它! - Malcolm Crum
2
在这里和那里花费了几个小时,这对我来说非常有效。非常感谢你。 - Tashi
这不适用于UDP端口。 - james28909
对我有用!确保按正确顺序执行:启动wsl > 运行exe > 启动服务器。您还可能需要在防火墙弹出窗口上单击允许。 - stackers

14

考虑到上述(正确)解决方案,这是一个对我有用的简化单行版本:

  1. 使用“ifconfig”查找您的WSL2公共IP地址。请记住,每次Windows重新启动时Hyper-V开关IP都会更改,因此WSL2中的公共IP也会更改。 enter image description here
  2. 使用上一步中找到的IP作为connectaddress参数运行以下命令:

netsh interface portproxy add v4tov4 listenport= listenaddress=0.0.0.0 connectport= connectaddress=172.24.26.277

参数说明:

  • listenport: Windows 将要监听的端口。

  • listenaddress: Windows 将要监听的地址。通常使用 0.0.0.0 即可。

  • connectport: Linux 发行版通过 wsl2 将要监听的端口。

  • connectaddress: Linux wsl2 实例的公共 IP (在步骤1中找到)。

请使用管理员身份的 cmd 或 PowerShell 运行上述命令。


1
你必须允许端口通过Windows防火墙,并在路由器上进行转发。请注意,WSL2 IP地址每次重新启动实例时都会被重新分配。 - Константин Ван
3
不需要使用 ifconfig 命令,hostname --all-ip-addresses 命令可以完成此任务。 - Константин Ван
这很棒,但是当我重新启动后,配置又丢失了。如何让它在重启后仍然存在? - Hackeron

12

以下是我发现可用于在WSL2(Windows 10 20H2上的Ubuntu 20)中访问正在运行的东西的完整三个步骤

1. 在开始时进行一次操作:在Windows防火墙中打开Expo端口入站

Windows防火墙应该启用,不要关闭它!而且默认情况下应该阻止入站尝试。

以下内容将在网络上仅打开19000-19001端口的入站流量,但仅适用于您配置为“私有”的网络(如果您在域上,请替换为Domain):

  • 使用您需要的端口替换19000-19001
  • 替换DisplayName,找一个对您有用的。 它很方便以后查找规则。

(以管理员身份运行powershell)

New-NetFireWallRule -Profile Private -DisplayName 'Expo ports for LAN development' `
    -Direction Inbound -LocalPort 19000-19001 -Action Allow -Protocol TCP

(您可以使用 Get-NetFirewallRule | Where-Object {$_.DisplayName -Match "Expo.*"} 进行检查-将-Match参数替换为您选择的DisplayName)

2. 将portproxy指向WSL;重新运行“每次WSL有新IP地址时”

(我还不确定WSL IP地址更改的频率,但我认为只有重新启动才会更改)

我在网上看到了一些东西,包括其他答案,在说将 portproxy 连接到connectaddress=127.0.0.1,但对我并没有起作用(WSL2,Windows 10 20H2)。
我不能说其他人为什么发现它有效,我只能说反复测试证实了对我而言,127.0.0.1无效,但WSL IP地址有效。

因此,这里是一个可重复使用的命令,自动将 connectaddress 设置为正确的 WSL 地址:
(powershell — 只是为了方便内联的 Trim() — 作为管理员使用)

netsh interface portproxy add v4tov4 listenport=19000 listenaddress=0.0.0.0 `
    connectport=19000 connectaddress=$($(wsl hostname -I).Trim());

netsh interface portproxy add v4tov4 listenport=19001 listenaddress=0.0.0.0 `
    connectport=19001 connectaddress=$($(wsl hostname -I).Trim());

3. (可选)如果您的WSL需要知道开发机器的LAN IP地址

(例如QR码移动应用程序,如Expo)

您将需要在WSL中重新运行以下命令,“每次开发主机有新的IP地址时”

  • 这可能是最常更改的一个。当您更改网络(例如家庭/办公室)时,笔记本电脑本地网络IP肯定会更改 - 也可能在其他时间更改。

幸运的是,它也可以粘贴/别名:
WSL2 shell

netsh.exe interface ip show address "Wi-Fi" | grep 'IP Address' | sed -r 's/^.*IP Address:\W*//'

# e.g. for Expo, I used:
export REACT_NATIVE_PACKAGER_HOSTNAME=$(netsh.exe interface ip show address "Wi-Fi" | grep 'IP Address' | sed -r 's/^.*IP Address:\W*//')

echo Meteor will use dev machine IP address: $REACT_NATIVE_PACKAGER_HOSTNAME

我“希望我不必重新运行所有东西,而一切都可以自动化”,但是同样的懒惰让我很高兴至少拥有易于“重新运行”的命令2(和3),并且始终能够获得我需要的 LAN 访问 WSL2 托管服务。


这是唯一可行的答案! - Shinebayar G
顺便说一下,它在HTTP服务器上运行得非常好。但是我无法让我的DNS服务器工作。Pi-Hole在WSL2中运行,并进行了端口转发设置。我可以看到端口53 TCP在Windows和WSL2上都在监听。然而外部客户端无法使用DNS服务器... 有什么想法吗? - Shinebayar G
我可以看到端口53 TCP正在Windows上监听...但是外部客户端无法使用DNS服务器。有趣的场景!@ShinebayarG,您是否在WSL中使用pihole作为Windows名称服务器?也就是说,问题是即使您为Windows配置了端口代理53和80,当您将其他网络设备的名称服务器配置为指向您的Windows设备(将使用这些端口),它对于它们作为名称服务器不起作用? - Daryn
1
@ShinebayarG,你没有错过任何东西,我复制粘贴了错误的链接!我本意是分享来自serverfault的这个链接:https://serverfault.com/a/472692/117183 - Daryn
1
正确 @Abdull;Domain配置文件是另一个安全选项,谢谢您的建议,我会添加到答案中。对于 Public配置文件,你需要强调关于“……这样也可以工作,但是你打开了公共网络上的端口 - 运行"dev"软件……” 的大⚠️、真正需要问的问题是:为什么我要在一个我不能信任的网络上进行基于网络的开发工作,以至于我必须将其配置为“公共”?但是,如果有安全和合理的原因,请继续。 - Daryn
显示剩余6条评论

11

让你的WSL2发行版像网络中的任何其他客户端一样运行

基于Roelofs的建议第二项,以下是我个人案例中使一切正常的方法。可惜我太新了还不能只留下评论。

我的起点:
Win 10专业版
在WSL2下运行Ubuntu
(具有Linux容器的Docker)

我的目标:
将来自网络上的客户端的rtmp流输入并从在Ubuntu机器上运行的nginx服务器输出。

构建桥梁

在我的情况下,我无法使Hyper-V正确设置该桥梁。在Hyper-V管理器的虚拟开关部分选择WSL开关的外部网络并点击应用后,最终以错误0x80070490失败。不知道为什么,也没有时间去调查。WSL和Docker服务都没有运行。

相反,我只是将设置保留在内部网络上,并手动在“网络连接”(运行->ncpa.cpl)下建立了接口桥接。在我的情况下,是WiFi连接和vEthernet(WSL)。 在这样做之后立即失去了互联网连接,并且花费了我尴尬的长时间才发现需要重新启动。 (Windows这一次没有询问我!)

让虚拟机良好运转

重新启动后,我现在可以从主机上访问互联网,桥接设置为DHCP并继承了WiFi接口的IP地址(192.168.1.246)。很好。

但是虚拟机仍然获得虚拟开关的IP地址(或者无论你怎样看待它,Windows似乎会分配给开关和VM的随机172.x.x.x地址)。

启动Ubuntu后,我决定执行:

sudo ip addr flush dev eth0

继续:

sudo dhclient eth0

我从Windows商店使用原版Ubuntu发行版,它向我抛出了一堆错误,因为它没有systemd。尽管如此,它成功将桥接的IP添加到了eth0。但这并不是非常方便,所以我用以下方法解决了这个问题:

sudo ip addr del 192.168.1.248/24 dev eth0

在查看路由表之前,先偷偷瞄一眼:

user@vm:~$ route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
192.168.1.0     0.0.0.0         255.255.255.0   U     0      0        0 eth0

删除旧IP后,我添加了一个来自DHCP范围外的唯一IP:

sudo ip addr add 192.168.1.50/24 dev eth0

我再次检查了路由表,第一项已经不存在了。此时我可以ping通局域网但无法ping通广域网。

使用以下命令重新添加该项:

sudo ip route add default via 192.168.1.1 dev eth0

现在可以ping通WAN IP,但无法解析DNS。

LMGTFM: 添加永久DNS

万一解决方法不见了,在这里,感谢non-static:

创建文件:/etc/wsl.conf。
在文件中加入以下内容
[network] generateResolvConf = false 在cmd窗口中运行wsl --shutdown 重新启动WSL2 创建文件:/etc/resolv.conf。如果已存在该文件,请使用此新文件替换原有文件。 在文件中加入以下内容
nameserver 8.8.8.8
或者你想要使用的任何DNS服务器的IP地址,重复步骤3和4。
因此,总结一下,检查路由并正确设置DNS-conf。不幸的是,每次重新启动WSL时IP设置都会被还原。如果您不想每次手动执行此操作,有关如何自动化此操作的讨论在这里这里。我还没有找到自己最喜欢的方法。

最好的问候, Alexander


在Win 11上,手动桥接会使我的主机下行速度减半,上行速度降至1%。一旦我删除桥接,它就恢复正常。 - Firsh - justifiedgrid.com

10
  1. 在WSL上安装ssh服务器

  2. 在Windows上找到一些ssh客户端

  3. 在Windows上执行:(Windows的IP地址是192.168.x.x,WSL的IP地址是172.28.x.x)

    ssh -L 192.168.x.x:3000:172.28.x.x:3000 someuser@172.28.x.x

如果无法正常工作,请尝试使用另一个本地端口,例如(192.168.x.x:3001:172.28.x.x:3000)


1
我只是想说,在尝试了每一个可能的修复方法超过6个小时,即使完全禁用防火墙和Defender,仍然一无所获,但是最终这个方法解决了我的问题。我无法以其他方式从主机之外访问WSL2的IP地址。谢谢! - jdunk
你也可以直接在Windows上安装netcat来转发端口,而不需要在WSL上安装ssh服务器。https://unix.stackexchange.com/questions/293304/using-netcat-for-port-forwarding - ahmet alp balkan

6
另一种解决方法是直接将WSL2桥接到相关的网络适配器。我尝试在Hyper-V中进行操作,但无法使其正常工作。然而,成功的办法是打开“控制面板\网络和 Internet\网络连接”,选择网卡(在我的情况下是)和,右键单击并选择“桥接连接”:

桥接连接菜单项

你应该会得到类似这样的结果:

桥接的连接

设置好桥接之后,请等待一分钟左右让Windows处理。

接下来打开WSL并运行dhcp以获取新地址:

sudo ip addr flush dev eth0 && sudo dhclient eth0

目前,WSL和Windows都应该已经可以通信。可以通过ping一些IP地址如1.1.1.1或8.8.8.8来检查。如果仍然无法连接,请拆除桥接并重试。记得等待一两分钟,让Windows配置完成。

此外,确保eth0有一个来自您的局域网的地址(ip addr sh eth0)。

现在的问题是,WSL可能无法解析域名,因为WSL在启动时会创建/etc/resolv.conf,指向虚拟网关,而现在没有了。这可以通过在/etc/wsl.conf中添加以下内容来解决(如果文件不存在,请创建它):

[network]
generateResolvConf=false

然后通过重新启动 LxssManager Windows 服务来重启 WSL。 唯一剩下的就是在 WSL 中设置备用 DNS 服务器。不要使用生成的 /etc/resolv.conf,只需创建一个类似如下内容的 /etc/resolv.conf 文件:

nameserver 1.1.1.1
nameserver 1.0.0.1

就这样!WSL现在应该已经桥接到您的网络,并且拥有自己独特的地址。


4

在使用wsl2时,我成功地运行了一个监听端口4200的应用程序。通过在管理员权限的PowerShell中执行以下命令:

添加代理

netsh interface portproxy add v4tov4 listenport=8080 connectport=4200 connectaddress=127.0.0.1

请注意缺少listenaddress=参数,以及不同的监听端口8080(而不是4200)。

添加防火墙规则

netsh advfirewall firewall add rule name="打开端口8080" dir=in action=allow protocol=TCP localport=8080

接下来,在我的连接WiFi的手机上访问 http://192.168.1.2:8080

清除

您可以使用netsh interface portproxy delete v4tov4 listenport=8080 命令删除代理,或使用netsh advfirewall firewall delete rule name="打开端口8080" 命令删除防火墙规则。

注意事项

显然,将端口和IP地址192.168.1.2更改为您的情况。使用ipconfig命令查找它。您可能需要确保在wsl2中运行的代码绑定在0.0.0.0上。

祝好运。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接