0 简述
前面的文章里介绍过通过ssh在局域网或者远程访问树莓派,一般而言,对于非图形界面的开发仅通过ssh命令行交互就能够完成的,但是要开发图形界面展示或交互的应用时,光命令行交互的方式就远远不够了。这篇文章将针对树莓派这类系统,怎么在远端的PC机上ssh登入,并进行图形界面的开发和演示。
一 实现思路
实现原理其实是利用了X-Window的框架。那么这里先简单介绍下什么是X-Window。
1.1 X-Window介绍
X Window系统是一个使用网络框架的图形使用者接口软件,如下图所示,X-Server负责管理硬件,X-Client是应用程序,X-Client应用程序会将所想要呈现的画面告知X-Server,最终由X-Server将结果通过它所管理的硬件绘制出来:
- X-Window的框架
- 从我的使用场景的角度看,X-Window是一种将服务端(一般是Linux服务端)的显示需求,透到本地客户端实际显示器上来进行显示的框架,是显示的一种C(Client)-S(Server)框架
- 但是X-Window的C-S,和我们一般的远程服务器登录的C-S看起来是相反的。即我们的远端服务器(我这里是一台树莓派),在显示这块是作为X-Client的,而本地客户端(这里是我的PC机)是作为X-Server,X-Client将X-data发送给X-Server,由X-Server来显示
- X-Window的工作机制
- 客户端ssh登录到服务器时,正确配置的话,会自动设置服务端的$DISPLAY变量,通过X11指向客户端的显示器
- 配置正确的话,登陆服务器,通过“printenv”可以查看其中是否有DISPLAY环境变量,没有的话就有问题,有的话一般是“localhost:10.0”。也可以通过“echo $DISPLAY”来查看该环境变量
- 手动设置“export DISPLAY=localhost:10.0”是没用的,必须通过配置,让客户端ssh到服务端时自动设置,因为还有响应的tcp端口监听等配置。通过“unset DISPLAY”可以删除掉手动设置的环境变量
- DISPLAY变量格式的说明
- DISPLAY环境变量的格式正常为:"hostname: displaynumber.screennumber"
- 某些机器多个显示屏共享同一套输入设备,如一台PC连接两台CRT显示器。但它们共享一个键盘和一个鼠标,因此这一组显示器就拥有一个共同的displaynumber。而这组显示器中每个单独的屏则拥有自己单独的 screennumber
- displaynumber和screennumber都是从零开始的数字。hostname指Xserver所在的主机名或者ip地址, 图形将会显示在这一机器上, 可以是启动了图形界面的Linux/Unix机器, 也可以是安装了Xming, VcXsrv等Xserver软件的Windows机器,还可以是安装了XQarz Xserver软件的MAC笔记本。如果Host为空, 则表示Xserver运行于本机, 并且图形程序(Xclient)使用unix socket方式连接到Xserver, 而不是TCP方式。使用TCP方式连接时, displaynumber为连接的端口减去6000的值, 如果displaynumber为0, 则表示连接到6000端口。使用unix socket方式连接时则表示连接的unix socket的路径, 如果displaynumber为0, 则表示连接到/tmp/.X11-unix/X0。screennumber则几乎总是0
1.2 X11和ssh
前面介绍X-Window时有提到X11和ssh,X11就是X-Window的一种具体实现,有时候也将X-Window叫做X11,ssh有一个额外特性是能够将X11数据传输到计算机上运行的X-Server。因此通过X11转发可以用来在本地计算机上显示运行在树莓派上的应用程序。
接下来将详细介绍如何通过X11,让没有连接显示器的树莓派能够将显示数据传输到我的PC机(X-Server)上来显示。
二 Linux服务端(X-Client)配置
我这里的Linux服务端其实用的是树莓派,所以这里是树莓派的配置为例。
为了让X11转发在网络连接上可工作,需要在树莓派上启用ssh和X11转发,关于ssh的启用,之前的文章有详细介绍过,这里主要介绍X11转发的启用。树莓派操作系统中,默认是启用X11窗口转发功能的。如果发现自己的树莓派X11功能并未开启,那么需要按照以下步骤启用它:
- 通过ssh登录树莓派,然后打开sshd_config文件
# 这里示例通过nano来打开,也可以用vim来打开文件 sudo nano /etc/ssh/sshd_config
- 在文件中找到控制X11转发的一行,取消该行的注释(从开头删除#),并确保它设置为“yes”:
X11Forwarding yes
- 按“Ctrl + X”、“Y”、“Enter”键保存文件,并按如下方法重启(如需更改):
# 1. 可以选择重新启动SSH服务 sudo systemctl restart ssh # 2. 也可以重启树莓派 sudo reboot
三 PC客户端(X-Server)不同形式的配置
服务端常见的是Linux,而客户端的PC机则系统可能多种,同时不同的人对开发工具的需求也不相同,因此这部分会对不同的系统和不同的工具都会做设置介绍,如下的方式,以及和上面的VS Code配置方式,只要选择其中之一即可。
3.1 通过Windows+Putty配置
如果是直接在Windows上要使用X11,还得额外安装Xming软件,可从(http://sourceforge.net/projects/xming/)下载并运行Xming(还有一个可选软件是VcXsrv,不过Xming的安装包要小很多)。另外需要安装Putty(如果还没有安装PuTTY的话),可从http://www.putty.org/网站单独下载Putty工具。
首先运行Xming,然后再打开Putty进行配置。
在“PuTTY configuration”对话框中,选择“Connection | SSH | X11”,勾选“EnableX11 forwarding”前的复选框。如果你保留X display location选项为空,它将假定默认的Server:0.0,如下所示(当Xming运行后,可以通过将鼠标移到系统托盘中的Xming图标上来确认服务器编号,一般就是Server:0.0):
然后在Session设置中输入树莓派的IP地址(这里也可以使用树莓派的主机名-默认是raspberrypi),然后保存Putty设置,单击Open连接到树莓派(此时可能会有警告消息弹出,说明是首次连接到这台树莓派,确认即可):
在Putty界面输入树莓派系统名和密码登入后,就可以输入相关图形界面的命令,本地Window客户机上就能看到对应的图形显示程序了,比如简单可输入gitk命令看看,这里我在树莓派上调用Python通过matplotlib来显示相关图形界面,就会在我的Windows上显示出对应的图形了(感觉速度比较慢,要好几秒才能出图):
3.2 通过Linux终端配置
对于Linux,通过“终端”即可实现与树莓派的连接。这里示例为连接默认的pi用户名,IP地址为192.168.1.69,以下命令的-X选项表明启用X11转发,具体终端命令如下:
ssh -X pi@192.168.1.69
如果一切正常,系统会提示输入密码(请记住pi用户的默认是raspberry)。完成登入后可以命令打开相关图形应用程序来进行效果测试。
3.3 通过MAC OS+XQartz配置
对于macOS下,实测也需要安装专门的X-Server软件才行,MAC上这个程序就是XQartz。在MAC笔记本上安装XQartz后,XQartz是支持X11的,XQartz自己带了一个Xterm的终端了。如果不使用SecureCRT,直接使用XQartz的Xterm也是可以使用ssh以及ssh X11的。
在安装XQartz之前,在MAC终端里输入“printenv”没有看到有“DISPLAY”环境变量,但是安装XQartz并重启MAC后(XQartz或自动启动),再次输入“printenv”时,可以看到“DISPLAY=/private/tmp/com.apple.launchd.kR0DhfImMz/org.xquartz:0” 这个环境变量的值了。说明XQartz启动并起作用了。
然后在Xterm(XQartz里的终端)里通过如下命令连接到服务端:
ssh user@server -Y
在输入登录密码后登入了服务端(树莓派)系统,然后通过“echo $DISPLAY”命令可以看到“localhost:10.0”了,说明客户端MAC的Xterm终端登入树莓派后,自动配置好了树莓派上的DISPLAY环境变量,这里树莓派里调用相关图形化界面显示时,会通过X11 Forwarding将显示投放到客户端MAC电脑上来显示了:
然后在该Xterm里面ssh登录到树莓派上,使用gitk等都可以显示图形界面了,比如我在树莓派上调用Python通过matplotlib来显示相关图形界面,就会在我的MAC上显示出对应的图形了:
3.4 通过MAC OS+SecureCRT配置
在MAC上还是需要先安装XQartz,如果有熟悉使用SecureCRT的,可以通过如下配置来实现X11转发。
SecureCRT的配置:在session的property里面设置,具体如下图所示,如果遇到SecureCRT正确配置且登录到树莓派后,还是未能唤出mac上的X window,可以尝试下重启MAC电脑:
四 PC客户端(X-Server)通过VS Code配置
如果使用VS Code作为代码编辑工具的,VS Code本身就是跨平台的,因此首先介绍这种方式,这样无论你用的Windows还是MAC还是Linux PC机,都能通过VS Code来访问树莓派开发图形化界面。不过VS Code里对X11的支持目前配置起来还是比较麻烦,而且有些bug还需要workaroud,这里仅提供一个参考。
4.1 VS Code的Remote SSH
首先需要了解VS Code的Remote ssh能力,先要远程登录到树莓派系统,VS Code里的“终端”,就能够达到ssh登录后命令行交互的功能,这个Remote ssh功能能够替代SecureCRT、Putty等工具。
- Remote SSH的使用:
- 通过“Command+Shift+P”,使用“Remote-SSH:connect current window to server”,就能新增或者直接打开一个ssh的service配置,并连接上(需要输入密码)。此时底端的Terminal是Server机器上的终端了
- 如果选择"Remote-SSH:disconnect"类似命令,则可以断开server连接,此时底端的Terminal是本地机器(local host)上的终端了
- Remote SSH配置public key/private key:
- 本地机器local host上~/.ssh/下看是否有id_rsa.pub,如有没有就要ssh key-gen来生成,不过一般用过git,就都已经生成过的
- 然后将本地机器local host上~/.ssh/id_rsa.pub,复制/推送到服务端server的 ~/.ssh/下面,并重命名为"authorized_keys",即
~/.ssh/authorized_keys
- 可以在本地机器local host的vs code里面,通过“Remote-SSH:settings”来打开remote ssh的相关配置,不过一般不用改动
- 这样配置后,后续就无需再输入密码,remote ssh就自动连接上
4.2 Remote X11
但是如上面说到的ssh X11显示GUI,需要再安装一个Remote X11 SSH插件。
Remote X11插件,在用Remote ssh登录远端VS Code环境工作时,该插件用于设置DISPLAY环境变量,以便从VS Code运行的X Window应用显示在本地机器上。
要使此插件工作,必须在远端VS Code环境里安装该插件,然后必须在本地计算机上运行X-Server。对于ssh登录到远端服务器的,还必须在本地机器上安装Remote X11 (SSH)插件(一般在安装上面的Remote X11插件时,此插件也会被同时安装):
4.2.1 额外设置
Remote X11需要更改一些设置,以便与SSH一起作用,因为Remote X11插件不能使用VS Code的SSH认证,不支持所有的SSH认证方法。
对于SSH连接,如果远程计算机没有安装Bash,则必须更改remoteX11.SSH.displayCommand设置,并提供打印DISPLAY变量值的命令。如果使用了端口转发,可能还需要更改remoteX11.SSH.port设置。
4.2.2 关于X11 Forwarding
Remote-SSH插件目前不支持X11的转发,因为一些已知的bug。为了解决这个问题,Remote X11 (SSH)插件需要再创建一个到服务端的SSH连接,并在后台启用转发。Remote X11 (SSH)插件目前只支持公钥认证,所以因为这个点,可能会有身份认证的问题出现,后面会提到。
4.2.3 Remote X11的更多扩展设置
对Remote X11插件设置的更改通常会短时间内自动生效。如果设置更改导致DISPLAY变量更改,则可能需要重新启动终端才行,也可以使用F1 > Remote X11: Reconnect Display强制更新。下面是Remote X11的扩展设置的一些说明,不过实际上可能并不需要用到:
remoteX11.display -要连接的display number,如果X-Server使用的显示器不是0,请更改此设置。 remoteX11.screen -要连接的screen number。 remoteX11.container.enable -是否为容器设置DISPLAY环境变量 remoteX11.SSH.enable - 启用X11转发并为SSH目标设置DISPLAY环境变量 remoteX11.SSH.authenticationMethod: keyFile—使用remoteX11.SSH.privateKey指定的私钥文件进行身份验证。不支持密码短语保护的密钥。 agent -使用ssh-agent获取密钥。此方法支持受密码短语保护的密钥。 remoteX11.SSH.agent -用于ssh-agent的Unix套接字或Windows命名管道的名称。设置为pageant以在Windows上使用pageant。如果为空,默认为Windows 10的OpenSSH代理(\\.\pipe\ OpenSSH -ssh-agent)或其他平台上的SSH_AUTH_SOCK环境变量。仅在上面remoteX11.SSH.authenticationMethod为agent时使用。 remoteX11.SSH.privateKey—SSH私钥文件的绝对路径。仅在remoteX11.SSH.authenticationMethod为publicKey。 remoteX11.SSH.XAuthPermissionLevel—在不受信任(ssh - x)和受信任(ssh - y)权限之间选择。 remoteX11.SSH.X11ConnectionType -选择用于X11连接的TCP和Unix套接字。在Windows主机上默认为tcp,否则为unix。 remoteX11.SSH.X11Socket -选择要连接的Unix套接字。screen number被附加到该设置的末尾。 remoteX11.SSH.displayCommand—输出DISPLAY=<DISPLAY>后面跟着换行符的命令,其中<DISPLAY>是DISPLAY变量的值。注意,此文本中不能有任何空格。当连接到不支持默认命令的计算机时,请更改此设置。 remoteX11.SSH.timeout -等待SSH shell响应上述命令的秒数。使用0永远等待。 remoteX11.SSH.host—设置用于连接到SSH服务器的主机名或IP地址。如果Remote X11试图连接到错误的地址,则使用此选项。 remoteX11.SSH.port—设置连接SSH服务器的端口号。如果Remote X11试图连接到错误的端口,则使用此选项。 remoteX11.WSL.enable -是否为WSL目标设置DISPLAY环境变量
4.2.4 身份认证设置
前面提到Remote X11插件目前只支持公钥身份验证,所以必须使用ssh-keygen生成公钥/私钥对,并将公钥添加到服务器的~/.ssh/authorized_keys文件中。
如下配置可以使RemoteX11获取到密钥:remoteX11.SSH.authenticationMethod设置为keyFile, Remote X11将读取remoteX11.SSH提供的文件。privateKey文件作为私钥,默认为~/.ssh/id_rsa,如果生成公/私钥对时的文件名称不同,则必须更改它。
注意:不过我如上面步骤完整配置后,Remote X11还是没有预期正常工作,提示错误是“Failed to get DISPLAY: Error: Error while signing data with privateKey: error:06000066:public key routines: OPENSSL_internal: DECODE_ERROR”,经过查找发现还是因为VS Code本身存在某些bug,可能早期版本的VS Code能正常工作,有不少使用VS Code的都遇到过这个问题,目前的解决方式参考openssl - ssh-keygen does not create RSA private key - Server Fault,就是在使用ssh-keygen生成公钥/私钥对时,需要加上PEM的参数:
ssh-keygen -p -m PEM -f ~/.ssh/id_rsa
其他使用注意
运行Pygame和Tkinter与X11转发
运行Pygame或Tkinter脚本时,可能会出现以下错误(或类似错误):
_tkinter.TclError: couldn't connect to display "localhost:10.0" # 解决 sudo cp ~/.Xauthority ~root/
更多详细内容请关注“技塑未来”公众号。
参考文档: