前言
这两天需要帮妹子抢票,但总不能为此一直开着电脑挂机。考虑到VPS服务器24小时在线且带宽神速,何不加以利用呢?于是首先想到,会不会有人写了运行在VPS上面的抢票软件,可惜查了下木有;又将目光投向浏览器抢票插件,发现这个倒是挺热门。
为了用上这些插件,就需要在服务器上面运行浏览器,Linux下吃资源最少的也就Firefox了,实在不行也可以考虑用Chrome凑合一下。但又不愿意为了启用图形界面,装一整套的LXDE桌面环境上去(这个应该是Linux下体积最小的桌面环境了),毕竟VPS上面20G的SSD空间相当吃紧啊。
然后忽然意识到,似乎利用X11 Forwarding
可以实现不安装桌面环境直接在本地启动图形界面呢?于是好好找了下资料,先彻底研究明白了SSH端口转发的原理以及实际操作方法,详见这篇博客。
X Server / Client 基本原理
Linux
的X Window
图形系统是一个经典的Server/Client
架构。在系统中如Firefox等需要显示GUI界面的应用程序,实际上是作为客户端即X Client
,这些程序在编写的过程中自然需要调用与界面显示相关的API
,但这些API
实质上只是一层封装,它真正做的事情是发送大量绘图相关的底层指令。而真正在调用显卡等硬件资源进行窗口绘图等实际操作的,则是另外一个独立的X Server
服务端,它可以看作是一个独立的服务器程序。这两者通过正常的网络协议进行通信,服务端接收到客户端的绘图指令之后,在屏幕上绘制图像。
这个设计的巨大优越之处就在于客户端和服务端是解耦的,两者可以分别运行在不同的机器上!也就是说,可以在一台机器上面运行GUI程序但不启动X Server
,绘图的相关指令通过网络传输到另外一台启动了X Server
的机器上,再由X Server
调用本机硬件资源,在屏幕上显示出界面。这也就意味着我在VPS上面完全可以只安装Firefox而不安装桌面环境,然后在我笔记本上面启动一个X Server
,通过适当的设置就可以使得VPS上运行的浏览器以原生窗口的形式显示在我笔记本上。
问题就在于,如何进行这个设置呢?我们先前提到过,对于X Client
而言,所有调用的图形界面API实际上只是通过网络发送了低级的绘图指令而已,但这些指令发送到了哪里?X Client
要去哪里寻找可用的X Server
呢?其实在Linux系统中,一个重要的环境变量$DISPLAY
,就是用来指定这些绘图指令被发送到的地址和端口。一般而言,这个变量的内容大体会是这样:localhost:10.0
,冒号的前半段表示X Server
所在主机的IP地址,localhost
自然就代表本机了;而后者则表示端口偏移和屏幕编号。
这里需要简单做一下解释,屏幕编号很好理解,一台机器可能连接了多个屏幕,因此将所有屏幕从0开始编号加以区别。而端口偏移则是用来表示X Server
监听的端口号,监听端口的起始值是6000,但是有可能被占用,所以需要加上偏移,直到找到一个没有被占用的可用端口。综上所述,对于上述变量内容,其含义是通知所有当前用户要启动的X Client
程序,有一个能够进行图形显示的X Server
程序正在监听着localhost
的6010
端口,并且它的屏幕编号为0,你们只需要往这个地址发送绘图指令就可以正常绘图了。
理解了上述内容,我们就可以简单地对一个装有桌面环境的Linux系统的启动过程进行猜测,X Server
系统作为硬件资源的实际操作者,应该是具有更高的启动优先级。在它启动的过程中,由于机器上面运行有各种网络服务,因此X Server
的默认端口号6000
可能会被占用,此时X Server
会不断尝试下一个端口号,直到找到一个可用端口。也许是因为大家比较喜欢在6000
端口上面开服务,因此现在的X Server
实现有时会默认从6010
端口开始尝试。等到X Server
成功启动之后,就会根据自己监听的端口号,来设置$DISPLAY
。此后X Client
程序一个一个启动,就会根据这个环境变量来发送绘图指令并顺利执行了。
X11 Forwarding
我们已经描述了X Window
系统大致的绘图流程,接下来的问题就在于,如何能够让远程机器上面运行的X Client
发送指令操作本地的X Server
绘图?正如上文所说,一般而言只要设置正确的$DISPLAY
变量,X Client
就能正确运行,问题是本地电脑比如我的笔记本一般没有公网的IP地址,又该如何设置这个变量?此时就是SSH端口转发大显身手的时刻:我们只需要将本地X Server
监听的端口,转发到远程服务器上面的某个端口,然后再设置$DISPLAY
的IP地址就是localhost
,端口则为转发的对应端口即可。
举个例子,在本地机器(我的笔记本)上面运行:
ssh -N -f -R 6010:127.0.0.1:6010 user@server_ip_address
就可以将本地机器上的6010
端口转发到VPS服务器上的6010
端口,然后再设置环境变量:
export DISPLAY=localhost:10.0
这样,在VPS上面运行的Firefox程序作为一个X Client
,会尝试向VPS服务器的6010
端口发送绘图相关的信息,这些信息又会被SSH转发到本地笔记本的对应端口,而这个端口正被一个实际的X Server
监听着,这样就会在笔记本上实现正确的绘图,实际运行效果如下:
实际操作
Windows系统下X11 Forwarding
的设置方法非常简单,只要在SSH软件中勾选对应的设置项即可。这里推荐一个神器软件:MobaXterm
,它不仅跟Putty
一样都支持X11 Forwarding
,而且还自带X Server
省的从cygwin
里面再装;同时也支持除了SSH之外的N多远程控制协议,比如超好用的VNC、SFTP,并且可以记录密码。
运行X Client
的主机也需要进行简单的设置,首先编辑/etc/ssh/sshd_config
,添加如下三行:
X11Forwarding yes X11DisplayOffset 10 X11UseLocalhost yes
然后还要确保已经安装了xauth
,在Fedora上可以执行如下命令:
sudo yum install xauth
总结
有点可惜的是,尽管折腾半天并且复习了一些网络知识,但是这个方案本身是失败的。虽然是可以把图形界面跑起来,但是一旦断开SSH连接,图形界面也就会跟着挂掉。仔细想想X11转发的原理,就应该知道这是显然的:正是凭借建立的SSH隧道,才使得VPS服务器能够直接与客户端(也就是我的笔记本)的端口通信,从而在客户端上运行的X Server
图形系统上面进行窗口绘制等等。一旦断开这个连接,远程端口转发也就相应断开,导致VPS上运行的X Client
程序根本无法与X Server
通信了,又怎么可能做到后台运行GUI程序呢?
另一方面,相比于VNC这样使用压缩传输的协议,原始的X Server
协议在传输效率上面实在是惨不忍睹。原本在内网使用环境下都会有一定程度的卡顿,这样跨国的远程环境下简直就是惨剧。相比之下,VNC协议的效果简直好得出奇,居然能够做到基本流畅地操作。
最终,我还是自己造了个轮子,用Python
写了个轮询12306网站并检查是否有票的小脚本,如果发现有票的话会调用mutt
自动给指定邮箱发邮件提醒,实际效果还是不错的。使用的时候需要给手机设置好邮件提示铃声,免得错过邮件。但这个脚本美中不足的一点是,一旦发现有票,它会不断地发邮件,所以买到票之后要及时结束这个程序,并且不要将查询间隔设置得太小。
附录
在yum系的包管理器下,一条快速删除无用依赖库的指令:
package-cleanup --leaves | grep -v refresh-packagekit | xargs yum remove -y
这条指令会找出软件包依赖关系树中所有的叶子节点,并调用yum进行删除。
但是注意,当你删除了先前的叶子节点之后,一些新的库就成为了叶子。由于这条指令是非递归的,此时使用package-cleanup --leaves
命令就可以查询新产生的这些叶子节点,然后继续删除就行了。这条指令一般情况下应该不会将系统核心库以及正常的应用软件包列出来,但还是应该小心,最好人工检查一遍再删除。
PS: package-cleanup
命令由yum-utils
软件包提供。