我正在使用 VPN(带有 OpenVPN)来保持我家和工作电脑之间的访问,今天我尝试 ssh-forward 一个仅有 GUI 的应用程序,我发现这是可怕很慢。我以前用过 SSH X11 转发,确实有延迟,但这两个主机之间的延迟真的很大。从点击按钮到输出显示在本地机器上大约需要 20 秒。
这两个主机之间存在rtt min/avg/max/mdev = 84.393/86.858/91.297/3.163 ms
延迟,SSH 连接的速度约为 1.2MiB/s,我思考这应该足够了:\
我正在使用-YCX
,并且我已经实验性地尝试了使用和不使用Y
和C
(openvpn 已经使用 lzo 压缩内容),以及不同的密码,得到了类似的结果。
我开始认为它可能是 GTK 主题,它可能真的很重或者其他什么的。
有人知道这是否正常吗?我该怎么做才能减少延迟?(3-5 秒可以忍受,但 20 秒太多了)
答案1
转发问题当代的X(不是那“旧”X(当其网络透明度被发明时)具有字体平滑功能:正确平滑每个字形在某些表面上渲染的文本,X 服务器必须从希望渲染该字形的客户端获取位于该字形边界框下的位图。(这是平滑算法正常工作所必需的,因为它考虑了渲染字形的上下文。)
因此,使用当代的 GUI 工具包,X 服务器与其客户端之间传输的流量是巨大的:你可以在本地 X 服务器中启用 TCP 来看到这一点(现在它们通常以 启动-nolisten tcp
),并强制一些基于 GTK 或 Qt 的 X 客户端通过 TCP 与服务器对话:
$ DISPLAY=localhost:x11 /usr/bin/that/x-app
(请参阅grep x11 </etc/services
了解 X 服务器的标准端口)。您会立即注意到,即使 X 流量没有离开本地主机,客户端的行为也非常缓慢:这仅仅是因为 X 流量通常通过 Unix 域套接字传输,该套接字基本上只是在内存缓冲区之间复制字节,因此开销非常低,现在它遍历整个 TCP/IP 堆栈及其所有队列和复杂逻辑。现在考虑一下在您的案例中发送此流量时会发生什么 - 包裹在三层数据传输协议中:SSH 隧道由 VPN 隧道承载,由 TCP/IP 承载,由线路承载。
至于该怎么办,我不太确定。
和mosh
退出比赛,我会尝试使用IPQoS
OpenSSH 客户端的选项。
另一种方法是从另一个角度解决问题:尝试基于 VNC 访问您的应用程序。选项如下:
答案2
我遇到的问题和你的非常相似:一些 gtk 应用程序(例如 meld)通过 ssh 启动时很慢,而其他一些则不然(例如 synaptic)。我实际上可能更准确地描述它有多慢:
$ echo "$TIMEFORMAT"
%R
$ time meld --version
meld 3.20.0
25.293
这与我发现的其他慢速应用程序(nemo、gedit)的时间完全相同。
使用 strace 检查 meld,它似乎在等待某个永远不会发生的事件,并且超时时间恰好为 25 秒。
该问题很可能与此处报告的问题相同: https://bbs.archlinux.org/viewtopic.php?id=230036
这是一个 dbus 问题 - 一种未通过 ssh 设置的会话环境。不幸的是,我不知道如何解决这个问题。
我发现,解决 meld 问题的唯一办法是在后台启动一个 meld,然后才能以正常方式使用它。
编辑
找到了!简单启动 dbus 并导出报告的变量:
$ dbus-launch
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-2EzslkNeji,guid=c9dec1622d6575f468559f8b5d9ee0e0
DBUS_SESSION_BUS_PID=4745
设置以上并导出,然后:
$ time meld --version
meld 3.20.0
0.268
我会将其放入远程启动脚本中并在此处报告。
跟进
这相当冗长,并且很大程度上超出了范围,但由于我确实喜欢我所看到的帖子中易于尝试的复制/粘贴解决方案,我可能也认为您也喜欢。我已经测试了以下内容作为会话脚本来为 ssh 提供信息(例如 ssh -X my@dark-side ~/bin/session):
#!/bin/bash
LAUNCH=(
# xload
# thunderbird
dbus
konsole
)
Tmp=$(mktemp -d)
mkdir -p $Tmp
KILLS=()
WAITS=()
echo "info: starting session" >&2
app_dbus() { # # Launch dbus and remember to kill
# redirect error because of "create ~/.dbus/session-bus/"
# deal with failure by checking DBUS_SESSION_BUS_PID afterwards
dbus-launch > $Tmp/dbus.sh 2>/dev/null
. $Tmp/dbus.sh
if [ "$DBUS_SESSION_BUS_PID" ] &&
ps --no-header -o pid -p "$DBUS_SESSION_BUS_PID" \
> /dev/null 2>&1
then
export DBUS_SESSION_BUS_ADDRESS DBUS_SESSION_BUS_PID
KILLS+=( $DBUS_SESSION_BUS_PID )
else
echo "warning: failed dbus" >&2
fi
}
app_konsole() { # # Launch konsole and remember to wait for
konsole &
WAITS+=( $! )
}
for APP in "${LAUNCH[@]}"
do
app_$APP
done
rm -r $Tmp # not needed anymore
wait "${WAITS[@]}"
echo "info: ending session" >&2
for ID in "${KILLS[@]}"
do
kill $ID
done