和容器打交道感觉就像魔法。理解的人就会得心应手,不理解的会觉得很难。幸运的是,我们已经对容器技术有一定的掌握,甚至发现容器只是被隔离和限制的Linux进程。运行容器不一定需要镜像,相反构建镜像的时候反而需要运行容器。
今天我们来说说容器网络问题。或者,更准确地说,是单主机容器网络问题。在本文中,我们将回答以下几个问题:
在回答以上问题时,我们将使用Linux工具从零开始创建容器网络。因此,很明显,单主机容器网络只不过是各种Linux工具的简单组合:
任何Linux发行版就足够了。本文中的所有示例都是在一个全新的Linux虚拟机上完成的:
接下来所有的命令在root用户下执行
# uname -a
Linux instance-20220709-1624 5.4.17-2136.308.9.el8uek.x86_64 #2 SMP Mon Jun 13 20:36:40 PDT 2022 x86_64 x86_64 x86_64 GNU/Linux
为了简化示例,在本文中,我们不打算依赖任何成熟的容器化解决方案(例如docker或podman)。相反,我们将专注于基本概念,并使用最简单的工具来实现我们的学习目标。
Linux网络栈由什么组成?很明显,网络设备的集合。还有什么呢?比如,路由规则的集合。不要忘记,还有一组netfilter钩子,包括由iptables规则定义的钩子。
我们可以快速写一个不全面的inspect-net-stack.sh脚本:
#!/usr/bin/env bash
echo "> Network devices"
ip link
echo -e "\n> Route table"
ip route
echo -e "\n> Iptables rules"
iptables --list-rules
在运行脚本之前,我们稍微修改一下iptables规则,使它们易于识别:
# iptables -N ROOT_NS
接着,在我的虚拟机上执行inspect脚本会产生以下输出:
# sh inspect-net-stack.sh
> Network devices
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 02:00:17:01:ae:0d brd ff:ff:ff:ff:ff:ff
> Route table
default via 10.0.0.1 dev ens3
default via 10.0.0.1 dev ens3 proto dhcp metric 100
10.0.0.0/21 dev ens3 proto kernel scope link src 10.0.2.13
10.0.0.0/21 dev ens3 proto kernel scope link src 10.0.2.13 metric 100
169.254.0.0/16 dev ens3 scope link
169.254.0.0/16 dev ens3 proto dhcp scope link metric 100
> Iptables rules
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
-N BareMetalInstanceServices
-N ROOT_NS
我们对这个输出很感兴趣,因为我们希望确保即将创建的每个容器网络都将得到一个独立的网络堆栈。
您可能已经听说过,用于容器隔离的Linux命名空间之一被称为网络命名空间。在man ip-netns中,“网络命名空间在逻辑上是网络堆栈的另一个副本,具有自己的路由、防火墙规则和网络设备。”为了简单起见,该命名空间是本文中使用的惟一命名空间。与其创建完全隔离的容器,我们宁愿将范围限制为仅网络栈。
创建网络命名空间的一种方法是ip工具——是标准iproute2集合的一部分。
# ip netns
# ip netns
netns0
如何使用刚刚创建的命名空间?有一个实用的Linux命令叫做nsenter。它能进入一个或多个指定的命名空间,然后执行给定的程序:
# nsenter --net=/var/run/netns/netns0 bash
# ls
inspect-net-stack.sh
# sh inspect-net-stack.sh
> Network devices
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
> Route table
> Iptables rules
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT
从上面的输出可以清楚地看到,在netns0名称空间中运行的bash进程看到的是一个完全不同的网络堆栈。没有路由规则,没有自定义iptables链,只有一个环回网络设备。到目前为止,一切顺利……

注意:上面的nsenter命令在netns0网络名称空间中启动了一个嵌套的bash会话。不要忘记退出它,或者使用一个新的终端继续。
如果我们不能与专用的网络栈进行通信,那么该网络栈就没什么用了。幸运的是,Linux为此提供了合适的工具—虚拟以太网设备veth。从man veth命令中可知,它们可以作为网络命名空间之间的隧道,创建到另一个命名空间中的物理网络设备的桥梁,但也可以用作独立的网络设备。
虚拟以太网设备总是成对使用。我们看一下创建命令就会清楚了:
# ip link add veth0 type veth peer name ceth0
通过这个命令,我们创建了一对相互连接的虚拟以太网设备。名称veth0和ceth0是任意选择的:
# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 02:00:17:01:ae:0d brd ff:ff:ff:ff:ff:ff
3: ceth0@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether 66:8a:fe:30:be:72 brd ff:ff:ff:ff:ff:ff
4: veth0@ceth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether c6:87:96:fe:97:6b brd ff:ff:ff:ff:ff:ff
创建后的veth0和ceth0都驻留在主机的网络堆栈上(也称为root网络命名空间)。为了连接root命名空间和netns0命名空间,我们需要在root命名空间中保留一个设备,并将另一个设备移动到netns0中:
# ip link set ceth0 netns netns0
# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
link/ether 02:00:17:01:ae:0d brd ff:ff:ff:ff:ff:ff
4: veth0@if3: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
link/ether c6:87:96:fe:97:6b brd ff:ff:ff:ff:ff:ff link-netns netns0
一旦我们激活设备并分配适当的IP地址,任何发送到其中一个设备上的数据包都会立即出现在连接两个名称空间的对等设备上。让我们从root命名空间开始:
# ip link set veth0 up
# ip addr add 172.18.0.11/16 dev veth0
然后在netns0命名空间也执行类似命令激活ceth0:
# nsenter --net=/var/run/netns/netns0
# ip link set lo up
# ip link set ceth0 up
# ip addr add 172.18.0.10/16 dev ceth0
# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
3: ceth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 66:8a:fe:30:be:72 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.18.0.10/16 scope global ceth0
valid_lft forever preferred_lft forever
inet6 fe80::648a:feff:fe30:be72/64 scope link
valid_lft forever preferred_lft forever

我们检查网络连接:
#从netns0, ping root命名空间的veth0
ping -c 2 172.18.0.11
PING 172.18.0.11 (172.18.0.11) 56(84) bytes of data.
64 bytes from 172.18.0.11: icmp_seq=1 ttl=64 time=0.065 ms
64 bytes from 172.18.0.11: icmp_seq=2 ttl=64 time=0.023 ms
--- 172.18.0.11 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1008ms
rtt min/avg/max/mdev = 0.023/0.044/0.065/0.021 ms
# 退出 `netns0`
# exit
# 从root命名空间ping ceth0
# ping -c 2 172.18.0.10
PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.
64 bytes from 172.18.0.10: icmp_seq=1 ttl=64 time=0.045 ms
64 bytes from 172.18.0.10: icmp_seq=2 ttl=64 time=0.025 ms
--- 172.18.0.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1039ms
rtt min/avg/max/mdev = 0.025/0.035/0.045/0.010 ms
同时,如果我们试图从netns0命名空间访问其他地址是不会成功的:
# 在root命名空间执行以下命令
# ip addr show dev ens3
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9000 qdisc pfifo_fast state UP group default qlen 1000
link/ether 02:00:17:01:ae:0d brd ff:ff:ff:ff:ff:ff
inet 10.0.2.13/21 brd 10.0.7.255 scope global dynamic ens3
valid_lft 83445sec preferred_lft 83445sec
inet6 fe80::17ff:fe01:ae0d/64 scope link
valid_lft forever preferred_lft forever
# 记住ip:10.0.2.13
# nsenter --net=/var/run/netns/netns0 #进入netns0名空间
# ping 10.0.2.13
connect: Network is unreachable
#试试其他公网IP
# ping 8.8.8.8
connect: Network is unreachable
不过,这很容易解释。netns0路由表中根本没有针对此类数据包的路由。唯一的一条路有显示了如何到达172.18.0.0/16网络:
# 在netns0命名空间执行以下命令
# ip route
172.18.0.0/16 dev ceth0 proto kernel scope link src 172.18.0.10
Linux有很多增加路由表的方法。其中一种方法是从直接连接的网络接口中提取路由。记住,netns0中的路由表在创建名称空间之后是空的。但随后我们在那里添加了ceth0设备,并为其分配了一个172.18.0.10/16的IP地址。由于我们使用的不是一个简单的IP地址,而是地址和网络掩码的组合,网络栈会从中提取路由信息。每个去往172.18.0.0/16网络的报文都要通过ceth0设备发送。但其他数据包会被丢弃。类似地,在root命名空间中有一个新的路由:
# ip route
# ...省略部分...
172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11
到这里,我们已经准备好回答第一个问题了。我们已经知道了如何隔离、虚拟化和连接Linux网络栈。
容器化的整个理念归结为高效的资源共享。也就是说,每台机器只有一个容器是不合理的。相反,我们的目标是在共享环境中运行尽可能多的独立进程。那么,如果我们按照上面的虚拟以太网veth方法在同一个主机上配置多个容器会发生什么呢?让我们添加第二个容器:
#ip netns add netns1
# ip link add veth1 type veth peer name ceth1
# ip link set ceth1 netns netns1
# ip link set veth1 up
# ip addr add 172.18.0.21/16 dev veth1
#进入netns1名空间
# nsenter --net=/var/run/netns/netns1
# ip link set lo up
# ip link set ceth1 up
# ip addr add 172.18.0.20/16 dev ceth1
我最喜欢的部分是检查连通性:
#从netns1名空间ping不通veth1
# ping -c 2 172.18.0.21
PING 172.18.0.21 (172.18.0.21) 56(84) bytes of data.
From 172.18.0.20 icmp_seq=1 Destination Host Unreachable
From 172.18.0.20 icmp_seq=2 Destination Host Unreachable
--- 172.18.0.21 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1030ms
pipe 2
##但是netns1中存在一条路有
# ip route
172.18.0.0/16 dev ceth1 proto kernel scope link src 172.18.0.20
# 退出`netns1`
# exit
#从root名空间也ping不通ceth1
# ping -c 2 172.18.0.20
PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.
From 172.18.0.11 icmp_seq=1 Destination Host Unreachable
From 172.18.0.11 icmp_seq=2 Destination Host Unreachable
--- 172.18.0.20 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 1062ms
pipe 2
#从`netns0`可以ping通veth1
# nsenter --net=/var/run/netns/netns0
# ping -c 2 172.18.0.21
PING 172.18.0.21 (172.18.0.21) 56(84) bytes of data.
64 bytes from 172.18.0.21: icmp_seq=1 ttl=64 time=0.066 ms
64 bytes from 172.18.0.21: icmp_seq=2 ttl=64 time=0.033 ms
--- 172.18.0.21 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1062ms
rtt min/avg/max/mdev = 0.033/0.049/0.066/0.017 ms
有点不对劲……netns1现象和netns0不一样。由于某些原因,它不能与root通信,从root名空间我们也不能访问它。但是,由于两个容器都位于同一个IP网络172.18.0.0/16中,我们现在可以通过netns0名空间与主机的veth1通信。这确实有点意思...
我花了些时间才弄明白,很显然我们遇到了路由冲突。让我们检查一下root命名空间中的路由表:
# ip route
#...省略部分内容...
172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11
172.18.0.0/16 dev veth1 proto kernel scope link src 172.18.0.21
尽快在添加第二个veth对后,root名空间网络栈学习到新的路由172.18.0.0/16 dev veth1 proto kernel scope link src 172.18.0.21,但之前已经有一条路由生效了。当第二个容器尝试ping veth1设备时,根据路由匹配顺序会选择第一条路由,从而中断连接。如果我们删除第一个路由sudo ip route delete 172.18.0.0/16 dev veth0 proto kernel scope link src 172.18.0.11,并重新检查连通性,情况将变成netns1的连接将被恢复,但netns0将处于不确定状态。

我相信如果我们为netns1选择另一个IP子网络,一切都会工作正常。但是,在一个IP子网络中有多个容器是合理的。因此,我们需要以某种方式调整veth方法……
看看Linux网桥吧——这是另一种虚拟网络工具!Linux网桥就像一个网络交换机。实现与它相连的接口之间转发数据包。由于它是一个交换机,它在L2(即以太网)网络层工作。
让我们来一起试试网桥功能。但首先,我们需要清理现前面设置,到目前为止我们所做的一些配置更改实际上已经不需要了。删除网络命名空间就足够了:
# ip netns delete netns0
# ip netns delete netns1
快速重新创建两个容器网络。注意,我们没有为新的veth0和veth1设备分配任何IP地址:
# ip netns add netns0
# ip link add veth0 type veth peer name ceth0
# ip link set veth0 up
# ip link set ceth0 netns netns0
# nsenter --net=/var/run/netns/netns0
# ip link set lo up
# ip link set ceth0 up
# ip addr add 172.18.0.10/16 dev ceth0
# exit
#----------
# ip netns add netns1
# ip link add veth1 type veth peer name ceth1
# ip link set veth1 up
# ip link set ceth1 netns netns1
# nsenter --net=/var/run/netns/netns1
# ip link set lo up
# ip link set ceth1 up
# ip addr add 172.18.0.20/16 dev ceth1
# exit
确保主机上没有新的路由:
# ip route
default via 10.0.0.1 dev ens3
default via 10.0.0.1 dev ens3 proto dhcp metric 100
10.0.0.0/21 dev ens3 proto kernel scope link src 10.0.2.13
10.0.0.0/21 dev ens3 proto kernel scope link src 10.0.2.13 metric 100
169.254.0.0/16 dev ens3 scope link
169.254.0.0/16 dev ens3 proto dhcp scope link metric 100
接着,创建网桥:
# ip link add br0 type bridge
# ip link set br0 up
下面,将veth0和veth1虚拟设备挂载到网桥上:
# ip link set veth0 master br0
# ip link set veth1 master br0

再测试下连通性:
# nsenter --net=/var/run/netns/netns0
# ping -c 2 172.18.0.20
PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.
64 bytes from 172.18.0.20: icmp_seq=1 ttl=64 time=0.069 ms
64 bytes from 172.18.0.20: icmp_seq=2 ttl=64 time=0.030 ms
--- 172.18.0.20 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1047ms
rtt min/avg/max/mdev = 0.030/0.049/0.069/0.020 ms
# nsenter --net=/var/run/netns/netns1
# ping -c 2 172.18.0.10
PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.
64 bytes from 172.18.0.10: icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from 172.18.0.10: icmp_seq=2 ttl=64 time=0.025 ms
--- 172.18.0.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1035ms
rtt min/avg/max/mdev = 0.025/0.031/0.037/0.006 ms
一切工作正常。使用这种新方法,我们根本没有配置veth0和veth1。我们分配的仅有两个IP地址在ceth0和ceth1两端。但由于它们都在同一个以太网段上(记住,我们将它们连接到虚拟交换机),因此在L2层上有连接:
# nsenter --net=/var/run/netns/netns0
# ip neigh
172.18.0.20 dev ceth0 lladdr 7a:63:31:c0:53:0b STALE
# nsenter --net=/var/run/netns/netns1
# ip neigh
172.18.0.10 dev ceth1 lladdr 76:02:44:fa:2d:2f STALE
恭喜你,学会了如何把容器变成友好的邻居,防止它们干扰,但保持连接。
现在容器之间可以相互通信。但是它们能与主机(即root名空间)通信吗?
# nsenter --net=/var/run/netns/netns0
# ping 10.0.2.13
connect: Network is unreachable
这很明显,netns0中没有路由:
# ip route
172.18.0.0/16 dev ceth0 proto kernel scope link src 172.18.0.10
root命名空间也不能与容器通信:
# exit
logout
[root@instance-20220709-1624 opc]# ping -c 2 172.18.0.10
PING 172.18.0.10 (172.18.0.10) 56(84) bytes of data.
--- 172.18.0.10 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1048ms
# ping -c 2 172.18.0.20
PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.
--- 172.18.0.20 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1023ms
为了建立root命名空间和容器名称空间之间的连接,我们需要为网桥分配IP地址:
# ip addr add 172.18.0.1/16 dev br0
# ping -c 2 172.18.0.20
PING 172.18.0.20 (172.18.0.20) 56(84) bytes of data.
64 bytes from 172.18.0.20: icmp_seq=1 ttl=64 time=0.091 ms
64 bytes from 172.18.0.20: icmp_seq=2 ttl=64 time=0.028 ms
--- 172.18.0.20 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1049ms
一旦我们给网桥分配了IP地址,我们就在主机路由表中得到了一条路由:
# ip route
# ...省略部分内容...
172.18.0.0/16 dev br0 proto kernel scope link src 172.18.0.1
容器能够ping桥接接口,但它们仍然无法连接到主机的eth0。我们需要为容器添加默认路由:
# nsenter --net=/var/run/netns/netns0
# ip route add default via 172.18.0.1
# ping -c 2 10.0.2.13
PING 10.0.2.13 (10.0.2.13) 56(84) bytes of data.
64 bytes from 10.0.2.13: icmp_seq=1 ttl=64 time=0.050 ms
64 bytes from 10.0.2.13: icmp_seq=2 ttl=64 time=0.040 ms
--- 10.0.2.13 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1022ms
rtt min/avg/max/mdev = 0.040/0.045/0.050/0.005 ms
这个改变基本上把主机变成了路由器,网桥变成了容器的默认网关。

很好,我们将容器与root命名空间连接起来。现在,让我们试着把他们和外界联系起来。缺省情况下,Linux系统关闭报文转发功能,即路由器功能。我们需要打开它:
# 在root命名空间
# bash -c "echo 1 > /proc/sys/net/ipv4/ip_forward"
还是看看连通性:
# nsenter --net=/var/run/netns/netns0
[root@instance-20220709-1624 opc]# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
# 长时间卡住
还是无法连接主机外部网络,难道我们还少了什么没做?如果容器发送数据到外部,目标服务器无法发送返回包到容器,因为容器IP是局域网IP。也就是说只有主机内部才有路由规则到容器IP。而且全网存在大量容器使用相同的局域网IP:172.18.0.10地址。解决局域网访问外部网络的方法是NAT网络地址转换。数据包在出主机之前,由容器发出的数据包的源IP地址将被主机的外部接口地址取代。主机还将跟踪所有局域网IP的映射关系,在到达时,它将恢复源IP地址,然后将数据包转发给容器。听起来很复杂,但我有好消息告诉你!感谢iptables模块,我们只需要一个命令就可以实现:
# sudo iptables -t nat -A POSTROUTING -s 172.18.0.0/16 ! -o br0 -j MASQUERADE
这个命令相当简单。我们在POSTROUTING链的nat表中添加了一条新规则,要求伪装所有来自172.18.0.0/16网络的报文,但不是通过网桥。
将容器端口映射到主机的一些(或全部)接口是一种已知的实践。暴露容器端口究竟意味着什么?
假设容器内部运行着一个web服务:
# nsenter --net=/var/run/netns/netns0
# python3 -m http.server --bind 172.18.0.10 5000
如果我们试图从主机向这个服务器进程发送一个HTTP请求,一切都会正常工作(好吧,root名空间和所有容器接口之间有一个连接)。
# From root namespace
$ curl 172.18.0.10:5000
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
# ... 省略部分内容 ...
但是,如果我们从外部世界访问这个服务器,我们将使用什么IP地址?我们可能知道的唯一IP地址是主机的外部接口地址ens3:
# curl 10.0.2.13:5000
curl: (7) Failed to connect to 10.0.2.13 port 5000: Connection refused
因此,我们需要找到一种方法,将到达主机ens3接口上端口5000的任何包转发到172.18.0.10:5000目的地。或者,换句话说,我们需要在主机的ens3接口上发布容器的端口5000。又是Iptables来拯救!
# 外部流量
# iptables -t nat -A PREROUTING -d 10.0.2.13 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.18.0.10:5000
# 本地流量
# iptables -t nat -A OUTPUT -d 10.0.2.13 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.18.0.10:5000
另外,我们需要启用iptables在网桥网络上拦截流量:
modprobe br_netfilter
测试时间:
# curl 10.0.2.13:5000
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
# ... 忽略部分内容 ...
看看服务端输出:
# python3 -m http.server --bind 172.18.0.10 5000
Serving HTTP on 172.18.0.10 port 5000 (http://172.18.0.10:5000/) ...
172.18.0.1 - - [09/Jul/2022 12:56:07] "GET / HTTP/1.1" 200 -
10.0.2.13 - - [09/Jul/2022 12:58:43] "GET / HTTP/1.1" 200 -
我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
是否可以在不实际下载文件的情况下检查文件是否存在?我有这么大的(~40mb)文件,例如:http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm这与ruby不严格相关,但如果发件人可以设置内容长度就好了。RestClient.get"http://mirrors.sohu.com/mysql/MySQL-6.0/MySQL-6.0.11-0.glibc23.src.rpm",headers:{"Content-Length"=>100} 最佳答案
我在这方面尝试了很多URL,在我遇到这个特定的之前,它们似乎都很好:require'rubygems'require'nokogiri'require'open-uri'doc=Nokogiri::HTML(open("http://www.moxyst.com/fashion/men-clothing/underwear.html"))putsdoc这是结果:/Users/macbookair/.rvm/rubies/ruby-2.0.0-p481/lib/ruby/2.0.0/open-uri.rb:353:in`open_http':404NotFound(OpenURI::HT
深度学习12.CNN经典网络VGG16一、简介1.VGG来源2.VGG分类3.不同模型的参数数量4.3x3卷积核的好处5.关于学习率调度6.批归一化二、VGG16层分析1.层划分2.参数展开过程图解3.参数传递示例4.VGG16各层参数数量三、代码分析1.VGG16模型定义2.训练3.测试一、简介1.VGG来源VGG(VisualGeometryGroup)是一个视觉几何组在2014年提出的深度卷积神经网络架构。VGG在2014年ImageNet图像分类竞赛亚军,定位竞赛冠军;VGG网络采用连续的小卷积核(3x3)和池化层构建深度神经网络,网络深度可以达到16层或19层,其中VGG16和VGG
(本文是网络的宏观的概念铺垫)目录计算机网络背景网络发展认识"协议"网络协议初识协议分层OSI七层模型TCP/IP五层(或四层)模型报头以太网碰撞路由器IP地址和MAC地址IP地址与MAC地址总结IP地址MAC地址计算机网络背景网络发展 是最开始先有的计算机,计算机后来因为多项技术的水平升高,逐渐的计算机变的小型化、高效化。后来因为计算机其本身的计算能力比较的快速:独立模式:计算机之间相互独立。 如:有三个人,每个人做的不同的事物,但是是需要协作的完成。 而这三个人所做的事是需要进行协作的,然而刚开始因为每一台计算机之间都是互相独立的。所以前面的人处理完了就需要将数据
安全产品安全网关类防火墙Firewall防火墙防火墙主要用于边界安全防护的权限控制和安全域的划分。防火墙•信息安全的防护系统,依照特定的规则,允许或是限制传输的数据通过。防火墙是一个由软件和硬件设备组合而成,在内外网之间、专网与公网之间的界面上构成的保护屏障。下一代防火墙•下一代防火墙,NextGenerationFirewall,简称NGFirewall,是一款可以全面应对应用层威胁的高性能防火墙,提供网络层应用层一体化安全防护。生产厂家•联想网御、CheckPoint、深信服、网康、天融信、华为、H3C等防火墙部署部署于内、外网编辑额,用于权限访问控制和安全域划分。UTM统一威胁管理(Un
Linux操作系统——网络配置与SSH远程安装完VMware与系统后,需要进行网络配置。第一个目标为进行SSH连接,可以从本机到VMware进行文件传送,首先需要进行网络配置。1.下载远程软件首先需要先下载安装一款远程软件:FinalShell或者xhell7FinalShellxhell7FinalShell下载:Windows下载http://www.hostbuf.com/downloads/finalshell_install.exemacOS下载http://www.hostbuf.com/downloads/finalshell_install.pkg2.配置CentOS网络安装好
在神经网络方面,我完全是个初学者。我整天都在与ruby-fann和ai4r搏斗,不幸的是我没有任何东西可以展示,所以我想我会来到StackOverflow并询问这里的知识渊博的人。我有一组样本——每天都有一个数据点,但它们不符合我能够找出的任何明确模式(我尝试了几次回归)。不过,我认为看看是否有任何方法可以仅从日期预测future的数据会很好,而且我认为神经网络将是生成希望表达这种关系的函数的好方法.日期是DateTime对象,数据点是十进制数,例如7.68。我一直在将DateTime对象转换为float,然后除以10,000,000,000得到一个介于0和1之间的数字,我一直在将
我有一个nokigiri网络抓取工具,它发布到我试图发布到heroku的数据库。我有一个sinatra应用程序前端,我想从数据库中获取它。我是Heroku和Web开发的新手,不知道处理此类问题的最佳方法。我是否必须将上传到数据库的网络爬虫脚本放在sinatra路由下(如mywebsite.com/scraper),并让它变得如此模糊以至于没有人访问它?最后,我想让sinatra部分成为一个从数据库中提取的restapi。感谢大家的参与 最佳答案 您可以采用两种方法。第一个是通过控制台使用herokurunYOURCMD运行scrap