云打印实现流程与细节
1、云打印组成结构
云打印机分为windows端的服务端和Linux下的客户端。服务端和客户端都是多进程架构,分别由两个和四个进程组成。客户端和服务器通信采用thrift的rpc框架,通过定义统一的IDL接口,实现接口统一。
1.1客户端组成
客户端由四个进程组成,分别是:
deepin-cloud-print-agent
deepin-cloud-print-configurator
dcp
deepin-cloud-print-daemon
deepin-cloud-print-agent是一个开机自启dbus服务,用户权限启动。这个dbus服务提供三个接口,用来获取配置文件存储的加密认证码,输入框的加密认证码和打印完成的系统通知。deepin-cloud-print-daemon也是一个开机自启的dbus服务进程,但是是root权限启动,这个dbus服务就是封装deepin-cloud-print-agent里面的接口(个人感觉这两个进程功能有些重复)。dcp是最重要的一个部分,作为backend后端,在程序安装的时候拷贝到/usr/lib/cups/backend/目录。使用云打印的时候cups就会调用这个后端去处理打印。deepin-cloud-print-configurator是Qt界面交互程序,提供用户输入ip和授权码的入口,展示和添加云打印机。
1.2客户端工作流程
安装
安装的时候除了会执行一些常规的拷贝操作,postinst脚本会在安装完成之后,修改dcp的权限以及日志目录的权限、用户、组,保证cups可以正常访问。重启之后agent和daemon就会在后台常驻提供dbus服务。
启动配置界面
配置界面启动之后,用户输入远程ip和授权码,点击下一步,程序通过thrift调用windows上的verify接口验证,验证完成之后通过ListPrinter接口获取远程打印机列表,然后获取本地cups的打印机列表进行对比,对已经添加过的云打印机区分不同的图标进行展示。用户点击添加按钮,程序调用lpadmin进行打印机添加,添加打印机的ppd是提前固定写死的,后续可以扩展为实时获取windows打印机的支持属性,动态生成ppd文件。
打印
用户通过软件选择云打印机进行打印时,cups会调用dcp后端程序,并将打印参数option传递给它。cups的job模块会向stdin的标准输入里面写入输出文件的内容,dcp会读取stdin的内容在/var/spool/dcp/SPOOL目录生成中间文件。然后使用gs将ps文件翻译为pdf文件,临时目录在/var/cache/cups/dcp/,然后通过print接口调用远程服务器上的打印机功能。
打印机通知
dcp等待服务器端处理完打印任务之后获取打印机结果,然后调用daemon的dbus接口弹出打印结果通知。
2.1服务端组成
服务端由两个进程组成:
PrintAgent
CloudPrint
PrintAgent是一个后台常驻UI程序,由Qt和thrift cpp实现,主要是四个功能,界面部分展示当前机器的ip和授权码,数据通过rpc接口从CloudPrint获取,用户可以手动更新授权码。业务部分实现了一个PrinterAgentServer服务实例,里面提供了打印的相关操作的具体实现接口,比如DefaultPrinter获取默认打印机,StartJob调用第三方工具SumatraPDF进行打印等等。这些接口供CloudPrint调用。然后实现了一个PrintNotifyClient客户端接口,去调用CloudPrint提供的对应的接口。这些接口主要用来通知CloudPrint服务器打印任务状态。PrintNotify是一个在子线程死循环监听打印机任务状态的实例,是通过winSpool接口获取相关的通知信息,然后转发给PrintNotifyClient。
CloudPrint是go实现的后台服务,主要功能类似于一个中转站,连接Linux的客户端和windows端的PrintAgent。CloudPrint启动了三个服务实例,configServer是和PrintAgent之间传输配置信息的服务接口,默认启动会创建一个默认授权码,PrintAgent获取和更新授权码都是通过这个服务。cloudPrintServer提供打印相关的中间接口,接受客户端dcp传输过来的打印数据和打印指令,处理完成之后调用go里面的serverPrinter客户端实例去调用PrintAgent服务端接口进行真实打印任务。printerNotifyServer作为通知服务的实例,接受PrintAgent发来的打印机任务通知请求。CloudPrint还调用svc库实现了windows服务的注册、启动、删除等接口。
2.2服务端工作流程
安装
安装完成之后,PrintAgent常驻后台,成为一个托盘程序,CloudPrint注册为一个windows服务。
启动界面程序
PrintAgent启动之后会在界面上展示ip列表和默认的授权码,这些信息是rpc接口从CloudPrint获取,用户重新输入授权码,更新也是调用CloudPrint接口,CloudPrint的config实例会将新的配置信息存储到本地。
打印流程
Linux客户端通过rpc接口调用CloudPrint的服务接口,然后CloudPrint通过rpc接口调用PrintAgent的打印服务端接口,所以CloudPrint本质上就是个中转程序,理论上可以将它的功能合并到c++实现的PrintAgent程序中,这里估计是考虑到go的高并发性,提高打印效率(大量打印任务的情况下),所以采用go作为中转程序。
2、thrift的使用技巧
thrift作为rpc架构,实现远程进程通信,有tcp和http两种协议选项。优点在于,只要定义了一个IDL接口,可以自动编译成常见语言的通信框架代码,编译工具thrift.exe可以去官网下载指定版本,不同版本编译的代码会有很大不同,就拿cpp来说,毕竟cpp的标准库也在不停添加新特性。然后用户只需要继承虚基类实现对应的虚函数(就cpp而言)就行,想要启动一个thrift的服务,只需要将该类作为一个handle传递给server,server内部维护的map就会将接口和实现对应起来,客户端只需要使用自动生成的客户端实例就可以连接服务器,直接调用服务器的接口。
IDL示例
User.thrift
1 |
|
Service.thrift
1 |
|
User.thrift里面定义的是公用的数据结构,namespace表示命名空间,cpp表示对应的语言,Service.thrift定义的是提供的服务接口,编译完成会生成服务端的虚基类和客户端的直接调用接口。编译命令如下:
1 |
|
-r参数可以自动编译include进去的User.thrift文件,–gen 后面接你的目标语言。