Python能在打开套接字时选择网络适配器吗?

28 投票
3 回答
48585 浏览
提问于 2025-04-17 08:00

运行这个Python应用程序的目标机器上有三个网络接口可用。一般来说,这三个网络会有很大的不同,不过有可能其中两个网络会比较相似。

在下面的例子中,我无法控制ETH 2的目标地址(因为这是一个预先配置好的系统),所以我不得不通过编程的方式来选择使用哪个适配器。

我比较确定这和操作系统如何处理连接的路由有关。我希望能找到一种不依赖平台的方法来解决这个问题,因为这个应用程序可能需要在Windows 7和Linux机器上运行。

示例代码

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.0.2', 8000)) # Which device will this connect to??

正常情况

  • ETH 0 源地址: 192.168.0.1
  • ETH 0 目标地址: 192.168.0.2
  • ETH 1 源地址: 10.20.30.1
  • ETH 1 目标地址: 10.20.30.2
  • ETH 2 源地址: 60.50.40.1
  • ETH 2 目标地址: 60.50.40.1

可能出现问题的情况

  • ETH 0 源地址: 192.168.0.1
  • ETH 0 目标地址: 192.168.0.2
  • ETH 1 源地址: 10.20.30.1
  • ETH 1 目标地址: 10.20.30.2
  • ETH 2 源地址: 192.168.0.3
  • ETH 2 目标地址: 192.168.0.2

附加信息
适配器ETH0、ETH1和ETH2都连接到不同的物理网络

3 个回答

-1

SO_BINDTODEVICE这个选项听起来不错,但通常你会通过绑定到哪个IP地址来间接选择一个设备。更常见的是,你会绑定到''(空字符串),这样就可以绑定到机器上的所有地址。

27

我对Windows不太了解,但在Linux上,通常在做出路由决策之前,接口是不会被选择的,所以你通常无法决定你的数据包是通过哪个接口发送的。

不过,你可以选择在Linux上使用 SO_BINDTODEVICE(可以查看 man 7 socket)。这个选项可以把一个套接字绑定到某个设备上,但只有超级用户(root)才能设置这个选项。


我刚查了一下,Python的套接字库里没有定义 SO_BINDTODEVICE,不过你可以从 socket.h 中获取这个选项:

# from socket.h
# define SO_BINDTODEVICE 25

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, 25, 'eth0')

另请参阅:

19

在Windows系统上,如果你知道想要使用的网络接口的IP地址,可以在连接之前先绑定到那个地址。至于Linux,可以使用一个叫SO_BINDTODEVICE的选项,正如JimB所建议的(这似乎也是一个需要特权的调用)。

也就是说,在Windows上

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('192.168.0.1', 0))
s.connect(('...'))

在Windows下绑定源地址时,会选择与该设备相同的IP地址的接口,即使这个IP地址的路由成本更高也没关系。不过在Linux上,这种方法不管用,因为它总是会用选定设备的IP地址覆盖源地址。路由只根据目标地址来决定。唯一的例外是,如果你把源地址设置为127.0.0.1,Linux就会阻止这些数据包离开这台机器。

撰写回答