草庐IT

多网卡之指定程序访问特定网卡

无聊看风景 2023-04-04 原文

如果当时有好好听计算机网络这门课,那下面聊的东西非常简单。

起因来自一次驻场开发,所在工作环境需要用网线连接虚拟云桌面进行操作,于是我就一会插网线云桌面开发,又一会手机热点腾讯会议,偶尔就会有一个问题,就是同时连着无线和有线的时候,要么无法访问外网要么云桌面就会断开,实在是麻烦。

当时我的想法就是如果能控制不同的程序使用不同的网络就好了,所以我也搜索了一下,但是才疏学浅也没有抱着什么希望所以简单搜了下便放弃了。

直到某天我使用virtualBox创建虚拟机的时候,在网络配置界面发现了有两个网卡可以选择,很明显分别是线网卡和无线网卡,于是我创建两个虚拟机,分别选取不同网卡,发现可在两个虚拟机下同时访问外部网络和云桌面系统。
在我看来虚拟机也是一个程序,它是怎么做到使用指定的网卡呢?

于是一个的名词就来了:路由。

什么是路由?

路由(routing)是指分组从源到目的地时,决定端到端路径的网络范围的进程 。路由工作在OSI参考模型第三层——网络层的数据包转发设备。路由器通过转发数据包来实现网络互连。虽然路由器可以支持多种协议(如TCP/IP、IPX/SPX、AppleTalk等协议),但是在我国绝大多数路由器运行TCP/IP协议。路由器通常连接两个或多个由IP子网或点到点协议标识的逻辑端口,至少拥有1个物理端口。路由器根据收到数据包中的网络层地址以及路由器内部维护的路由表决定输出端口以及下一跳地址,并且重写链路层数据包头实现转发数据包。路由器通过动态维护路由表来反映当前的网络拓扑,并通过网络上其他路由器交换路由和链路信息来维护路由表。以上来自百度百科。

举个通俗的例子就好比快递投递的过程,一个包裹会根据省市区街道小区最终放到你家门口,放在网络世界中,数据也要有他的路线指引,而路由就是这个路标。

路由器查看了数据包的目的协议地址后,确定是否知道如何转发该包,如果路由器不知道如何转发,通常就将之丢弃。如果路由器知道如何转发,就把目的物理地址变成下一跳的物理地址并向之发送。下一跳可能就是最终的目的主机,如果不是,通常为另一个路由器,它将执行同样的步骤。当分组在网络中流动时,它的物理地址在改变,但其协议地址始终不变。

那么路由是怎么匹配转发的呢?一般来说就是通过路由表进行最长匹配原则,路由表中会对ip进行分段划分,每一个ip段都对应的下一跳的网络地址,随着一级级的路由跳转,最终将数据发送到请求地址。同样在你的系统中也有一个路由表,可以理解为你的数据投递过程中的第一站。

如何查看系统路由表,因为我的是ubunu系统,因此下面都是linux系统的操作指令,当然window也是异曲同工。
查看路由表

route -n 
ubuntu:~$ route -n
内核 IP 路由表
目标             网关            子 网掩码        标志   跃点   引用     使用  接口
0.0.0.0         192.168.0.1     0.0.0.0         UG    600    0        0  eth1
0.0.0.0         146.10.0.1      0.0.0.0         UG  20600    0        0  eth2
192.168.146.1   0.0.0.0         255.255.255.0   U     600    0        0  eth1

隐去了真实信息,上面是我自己编写的一个路由表,其中目标ip和子网掩码会计算出一个ip段,如果当前请求地址处于这个ip段,那么数据包将会发到对应的接口,也就是下一跳的地址中,其中192.168.146.1 就是我的虚拟机的ip,当我创建了一个这个虚拟机的时候

所以我现在的目标就是当使用云桌面的时候,让数据包转发至有线网卡中,而其他的请求转发至无线网卡中。比如现在云桌面的地址是147.20.0.111,那么如何在请求这个ip的时候让其跳转到有线网卡eth2中呢?

于是第二个的名词就来了:子网掩码(CIDR)。

什么是子网掩码?

子网掩码(subnet mask)又叫网络掩码、地址掩码、子网络遮罩,它用来指明一个IP地址的哪些位标识的是主机所在的子网,以及哪些位标识的是主机的位掩码。子网掩码不能单独存在,它必须结合IP地址一起使用。

通俗来说就是一种ip匹配规则,再直白点就是ip世界的正则表达式写法,(只是为了方便理解)。
具体如何计算,大家可以自行检索相关资料,在这里我简单说下计算规则。

比如一个ip 1.1.1.1,他的二进制如下:
00000001 00000001 00000001 00000001
下面是子网掩码
255.255.255.0
他的二进制为
11111111 11111111 11111111 00000000 
通过IP地址的二进制格式与子网掩码的二进制格式进行and运算,相等,说明处于同一个子网,可以直接通信。
如果这句话还不明白,可以这么理解:根据子网掩码从左到右,当等于1的时候,表示ip对应的位置不可变,等于0的时候,代表可变1或者0,因此通过上面的计算ip段为
00000001 00000001 00000001 00000000 
00000001 00000001 00000001 11111111
转换为十进制
1.1.1.0
1.1.1.255
这种情况下其中0是网络地址,255为广播地址,因此实际可用ip为1.1.1.1-1.1.1.254。

那么接下来我打算将147.20.0.0-147.20.255.255ip段指向网卡eth2,因此我需要设置ip 146.20.0.0的子网掩码为255.255.0.0,于是执行以下命令

#首先要删除原来网卡自动配置的路由表配置,具体可搜索route del用法
sudo route del -net 0.0.0.0 gw 146.1.224.1 
#接着增加新的路由表配置
sudo route add -net 146.15.0.0/16  gw 146.1.224.1 
#gw 是gateway网关的意思,可通过网卡属性查看网关地址。

在这里使用了146.20.0.0/16的形式,这是子网掩码的另一种表达方式:CIDR;

CIDR(Classless Inter-Domain Routing,无类域间路由选择)它消除了传统的A类、B类和C类地址以及划分子网的概念,因而可以更加有效地分配IPv4的地址空间。它可以将好几个IP网络结合在一起,使用一种无类别的域际路由选择算法,使它们合并成一条路由从而较少路由表中的路由条目减轻Internet路由器的负担。

通俗来说就是子网掩码的另一种形式,但是这种形式更加有效划分网络地址。可以在 https://ip.liangwei.cc/network.html 这个网站计算CIDR值。

到此设置完路由表,打开程序实测云桌面、外部网络同时可用。
现在想来这些其实是大学期间计算机网络很基础的知识,如果不是因为工作中遇到这个问题,估计我没有机会重拾复习。而作为一名软件研发,不能仅停留来业务层面的CURD。

有关多网卡之指定程序访问特定网卡的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby - 在 Ruby 程序执行时阻止 Windows 7 PC 进入休眠状态 - 2

    我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0

  3. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  4. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  5. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  6. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  7. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  8. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  9. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  10. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

随机推荐