tts*_*ras 17 networking ssh netcat
TL;DR 版本
观看此 ASCII 演员表或此视频- 然后找出发生这种情况的任何原因。下面的文字说明提供了更多的上下文。
设置细节
ssh
上生成,连接到运行 Armbian 的 SBC(橙色 PI 零)。使用 iperf3 对链接进行基准测试
以 为基准时iperf3
,笔记本电脑和 SBC 之间的链接低于理论 56 Mbits/sec - 正如预期的那样,因为这是一个非常“拥挤的 2.4GHz” (公寓楼)内的 WiFi 连接。
更具体地说:在iperf3 -s
SBC 上运行后,在笔记本电脑上执行以下命令:
# iperf3 -c 192.168.1.150
Connecting to host 192.168.1.150, port 5201
[ 5] local 192.168.1.89 port 57954 connected to 192.168.1.150 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 2.99 MBytes 25.1 Mbits/sec 0 112 KBytes
...
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 28.0 MBytes 23.5 Mbits/sec 5 sender
[ 5] 0.00-10.00 sec 27.8 MBytes 23.4 Mbits/sec receiver
iperf Done.
# iperf3 -c 192.168.1.150 -R
Connecting to host 192.168.1.150, port 5201
Reverse mode, remote host 192.168.1.150 is sending
[ 5] local 192.168.1.89 port 57960 connected to 192.168.1.150 port 5201
[ ID] Interval Transfer Bitrate
[ 5] 0.00-1.00 sec 3.43 MBytes 28.7 Mbits/sec
...
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 39.2 MBytes 32.9 Mbits/sec 375 sender
[ 5] 0.00-10.00 sec 37.7 MBytes 31.6 Mbits/sec receiver
Run Code Online (Sandbox Code Playgroud)
所以基本上,上传到 SBC 达到大约 24MBits/sec,从它 ( -R
)下载达到 32MBits/sec。
使用 SSH 进行基准测试
鉴于此,让我们看看 SSH 的表现如何。我在使用rsync
and时第一次遇到导致这篇文章的问题borgbackup
- 他们都使用 SSH 作为传输层......所以让我们看看 SSH 在同一链接上的表现:
# cat /dev/urandom | \
pv -ptebar | \
ssh root@192.168.1.150 'cat >/dev/null'
20.3MiB 0:00:52 [ 315KiB/s] [ 394KiB/s]
Run Code Online (Sandbox Code Playgroud)
嗯,这速度太快了!比预期的链接速度慢得多......
(如果您不知道pv -ptevar
:它显示通过它的数据的当前和平均速率。在这种情况下,我们看到从/dev/urandom
SSH读取数据并将数据发送到 SBC平均达到 400KB/s - 即 3.2MBits/sec,远低于预期的 24MBits/sec。)
为什么我们的链路以 13% 的容量运行?
也许是我们/dev/urandom
的错?
# cat /dev/urandom | pv -ptebar > /dev/null
834MiB 0:00:04 [ 216MiB/s] [ 208MiB/s]
Run Code Online (Sandbox Code Playgroud)
不,绝对不是。
可能是 SBC 本身吗?可能是处理太慢了?让我们尝试运行相同的 SSH 命令(即向 SBC 发送数据),但这次是从通过以太网连接的另一台机器(机器 2)运行:
# cat /dev/urandom | \
pv -ptebar | \
ssh root@192.168.1.150 'cat >/dev/null'
240MiB 0:00:31 [10.7MiB/s] [7.69MiB/s]
Run Code Online (Sandbox Code Playgroud)
不,这工作正常 - SBC 上的 SSH 守护程序可以(轻松)处理其以太网链接提供的 11MBytes/sec(即 100MBits/sec)。
执行此操作时是否加载了 SBC 的 CPU?
不。
所以...
iperf3
),我们应该能够将速度提高 10 倍到底发生了什么?
Netcat 和 ProxyCommand 来救援
让我们尝试普通的旧netcat
连接 - 它们的运行速度是否如我们预期的一样快?
在 SBC 中:
# nc -l -p 9988 | pv -ptebar > /dev/null
Run Code Online (Sandbox Code Playgroud)
在笔记本电脑中:
# cat /dev/urandom | pv -ptebar | nc 192.168.1.150 9988
117MiB 0:00:33 [3.82MiB/s] [3.57MiB/s]
Run Code Online (Sandbox Code Playgroud)
有用!并以预期的速度运行 - 好得多,好 10 倍 - 速度。
那么如果我使用 ProxyCommand 运行 SSH 来使用 nc 会发生什么?
# cat /dev/urandom | \
pv -ptebar | \
ssh -o "Proxycommand nc %h %p" root@192.168.1.150 'cat >/dev/null'
101MiB 0:00:30 [3.38MiB/s] [3.33MiB/s]
Run Code Online (Sandbox Code Playgroud)
作品!10 倍速度。
现在我有点困惑——当使用“裸”nc
作为 时Proxycommand
,你是不是在做与 SSH 完全相同的事情?即创建一个套接字,连接到 SBC 的端口 22,然后通过它铲起 SSH 协议?
为什么产生的速度会有如此巨大的差异?
PS 这不是学术练习- 因此我的borg
备份运行速度快了 10 倍。我只是不知道为什么:-)
编辑:在此处添加了该过程的“视频” 。计算从 ifconfig 输出发送的数据包,很明显,在两个测试中,我们发送了 40MB 的数据,以大约 30K 的数据包传输它们 - 不使用ProxyCommand
.
tts*_*ras 15
非常感谢在评论中提交想法的人。我都经历了:
使用tcpdump记录数据包并比较WireShark中的内容
# tcpdump -i wlan0 -w good.ssh & \
cat signature | ssh -o "ProxyCommand nc %h %p" \
root@192.168.1.150 'cat | md5sum' ; \
killall tcpdump
# tcpdump -i wlan0 -w bad.ssh & \
cat signature | ssh root@192.168.1.150 'cat | md5sum' ; \
killall tcpdump
Run Code Online (Sandbox Code Playgroud)
在记录的数据包中没有任何重要的差异。
检查流量整形
对此一无所知 - 但在查看“tc”联机帮助页后,我能够验证
tc filter show
什么都不返回tc class show
什么都不返回tc qdisc show
...返回这些:
qdisc noqueue 0: dev lo root refcnt 2
qdisc noqueue 0: dev docker0 root refcnt 2
qdisc fq_codel 0: dev wlan0 root refcnt 2 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn
Run Code Online (Sandbox Code Playgroud)
...这似乎没有区分“ssh”和“nc” - 事实上,我什至不确定流量整形是否可以在进程级别上运行(我希望它可以在地址/端口/区分IP 标头中的服务字段)。
Debian Chroot,避免 Arch Linux SSH 客户端潜在的“聪明”
不,同样的结果。
最后——纳格尔
在发件人中执行 strace...
pv data | strace -T -ttt -f ssh 192.168.1.150 'cat | md5sum' 2>bad.log
Run Code Online (Sandbox Code Playgroud)
...并查看传输数据的套接字上究竟发生了什么,我在实际传输开始之前注意到了这个“设置”:
1522665534.007805 getsockopt(3, SOL_TCP, TCP_NODELAY, [0], [4]) = 0 <0.000025>
1522665534.007899 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000021>
Run Code Online (Sandbox Code Playgroud)
这将设置 SSH 套接字以禁用 Nagle 算法。您可以谷歌并阅读所有相关信息 - 但它的意思是,SSH 优先考虑响应能力而不是带宽 - 它指示内核立即传输写入此套接字的任何内容,而不是“延迟”等待来自远程的确认。
简而言之,这意味着在其默认配置中,SSH 不是传输数据的好方法 - 当使用的链接速度较慢时(许多 WiFi 链接都是这种情况)。如果我们通过空中发送“主要是报头”的数据包,带宽就被浪费了!
为了证明这确实是罪魁祸首,我使用 LD_PRELOAD 来“删除”这个特定的系统调用:
$ cat force_nagle.c
#include <stdio.h>
#include <dlfcn.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
int (*osetsockopt) (int socket, int level, int option_name,
const void *option_value, socklen_t option_len) = NULL;
int setsockopt(int socket, int level, int option_name,
const void *option_value, socklen_t option_len)
{
int ret;
if (!osetsockopt) {
osetsockopt = dlsym(RTLD_NEXT, "setsockopt");
}
if (option_name == TCP_NODELAY) {
puts("No, Mr Nagle stays.");
return 0;
}
ret = osetsockopt(socket, level, option_name, option_value, option_len);
return ret;
}
$ gcc -fPIC -D_GNU_SOURCE -shared -o force_nagle.so force_nagle.c -ldl
$ pv /dev/shm/data | LD_PRELOAD=./force_nagle.so ssh root@192.168.1.150 'cat >/dev/null'
No, Mr Nagle stays.
No, Mr Nagle stays.
100MiB 0:00:29 [3.38MiB/s] [3.38MiB/s] [================================>] 100%
Run Code Online (Sandbox Code Playgroud)
那里 - 完美的速度(嗯,就像 iperf3 一样快)。
故事的士气
永不放弃 :-)
如果您确实使用类似rsync
或borgbackup
通过 SSH 传输数据的工具,并且您的链接速度很慢,请尝试阻止 SSH 禁用 Nagle(如上所示)-或使用ProxyCommand
将 SSH 切换为通过nc
. 这可以在您的 $HOME/.ssh/config 中自动化:
$ cat .ssh/config
...
Host orangepi
Hostname 192.168.1.150
User root
Port 22
# Compression no
# Cipher None
ProxyCommand nc %h %p
...
Run Code Online (Sandbox Code Playgroud)
...以便将来在 ssh/rsync/borgbackup 中将“orangepi”用作目标主机的所有用途都将nc
用于连接(因此不要理会Nagle)。