Open hanxi opened 5 years ago
流水账的感觉也很不错
我之前一直用的这个,但是似乎在我这里(windows),客户端偶尔会狂吃cpu,然后非常热;过两天试试看解决没有
其实我以前一直想自己写一个,后来懒就一直用Lemonade
我之前一直用的这个,但是似乎在我这里(windows),客户端偶尔会狂吃cpu,然后非常热;过两天试试看解决没有
其实我以前一直想自己写一个,后来懒就一直用Lemonade
我没注意到会疯狂吃cpu,估计是没连上服务器,死循环在连服务器吧。
@TTTPOB 可能你是除了我之外的唯一一个用户吧,我目前也一直在用。
我之前一直用的这个,但是似乎在我这里(windows),客户端偶尔会狂吃cpu,然后非常热;过两天试试看解决没有 其实我以前一直想自己写一个,后来懒就一直用Lemonade
我没注意到会疯狂吃cpu,估计是没连上服务器,死循环在连服务器吧。
确实很有可能!确实是睡眠之后可能会出现的情况
我之前一直用的这个,但是似乎在我这里(windows),客户端偶尔会狂吃cpu,然后非常热;过两天试试看解决没有 其实我以前一直想自己写一个,后来懒就一直用Lemonade
我没注意到会疯狂吃cpu,估计是没连上服务器,死循环在连服务器吧。
确实很有可能!确实是睡眠之后可能会出现的情况
帮忙提个pr修复下?我这里没有重现到。
我之前一直用的这个,但是似乎在我这里(windows),客户端偶尔会狂吃cpu,然后非常热;过两天试试看解决没有 其实我以前一直想自己写一个,后来懒就一直用Lemonade
我没注意到会疯狂吃cpu,估计是没连上服务器,死循环在连服务器吧。
确实很有可能!确实是睡眠之后可能会出现的情况
帮忙提个pr修复下?我这里没有重现到。
嗐,不会lua,啥时候我不懒癌了倒是想实现几个别的语言客户端
@TTTPOB 我也比较懒,自己用着没啥毛病就没更新了。 其他语言也可以的,协议文档在这里:https://oclip.github.io/docs/protocol/
服务器选用了 openresty。 选用服务器考虑了性能和生态,网络协议需要 HTTP 和 Websocket。也想过用 skynet, 虽然 skynet 也已实现了 websocket,但还是选择了在 web 领域应用更广泛的框架。
初版的客户端只是一个 bash 脚本,调用 openssl 本地加密解密数据,并用 curl 推送或者拉取数据到服务器。正式的客户端还是选用了 Lua 语言做主要的开发语言,C 语言作为 Lua 的扩展库开发语言。
服务器最初是先实现了 copy 和 paste 两个协议,copy 采用 POST (把本地数据推送到服务器), paste 采用 GET (从服务器拉取数据)。数据采用文件的形式存盘在服务器。
然后做一个账号系统来验证登录,本来打算自己实现一套账号系统的,但是担心用户注册麻烦,最终还是选用了 GitHub OAuth 的方式来登录。这个实现主要是接入 GitHub 的 API,拿到 token 和用户信息之后保存到服务器,再给用户生成一个 JWT . 之后就客户端使用 JWT 跟服务器交互。
关于使用 GitHub 登录和 JWT 是啥玩意参考这里列出的文章: https://blog.hanxi.info/?p=34
网站的主页页面也就两个,一个主页,一个登录后的个人页面。后端采用的模板库是 bungle/lua-resty-template , 前端采用了 Bootstrap 库。登录成功后就能显示 JWT ,还弄了个自动安装客户端的命令。然后实现点击按钮复制命令的实现可看这里: https://blog.hanxi.info/?p=35
初版的 Linux 客户端的配置文件在
$HOME/.oclip
,安装位置在$HOME/.local/bin
。实现过程中用到的一些 Bash 脚本关键点记录在这里: https://blog.hanxi.info/?p=33到这里就已经实现了一个初步可用的远程剪切板了,使用流程为 进入到 https://oclip.hanxi.info ,然后用 GitHub 登录,再复制安装命令到系统终端执行(不用 root 账户),然后就能使用 oclip 命令了。 oclip 的参数格式参考了 xclip。
然后在本地测试速度还可以 0.0xx 秒:
远程测试速度为,可能慢在 HTTPS 连接上。
最初想用 websocket 不是为了解决速度的问题,而是需要一个长连接的机制来实现监控和修改 Windows 的系统粘贴板。比如我在 Linux 上执行复制命令
echo hello|oclip
,然后数据可以自动推送到 Windows 上并修改系统粘贴板。确定好 Windows 客户端使用 Lua 开发后,需要使用的库大致分为操作系统剪切板,系统图标,websocket。
系统剪切板的操作有现成的 Lua 库 jaslatrix/clipboard,但是好多功能是我不需要的,而且没有监听剪切板的功能,因此我就参考它和 MSDN 自己实现了一个 hanxi/lclipboard
系统图标也找到了一个 C 库 zserge/tray ,对它进行修改调整实现了一个 Lua 库 hanxi/ltray
剩下最后一个大问题了,websocket 库如何选择?服务端用 openresty 的官方自带了 websocket 库,客户端找了好多,最后还是觉得下面两个比较合适:
但是
lua-http
依赖的cqueues
不支持 Windows,自己去适配的话太耗时间了,最终选了lua-websockets
这个库,然后参考 RamiLego4Game/Love-Discord 改成异步的接口,放在这里了 hanxi/lua-websockets为了支持 https 和 wss。 编译 luasec 花了点时间,网上找的 openssl 二进制包都有点问题,最后自己安装 perl 编译了一个 openssl 就没问题了。
还差一个交互协议的选择,最后的决定是参考 JSON-RPC,用 msgpack 格式打包类似的结构进行交互。msgpack 库打算用 Redis 的 antirez/lua-cmsgpack,最后发现 Openresty 安装时由于没装编译环境导致安装失败。所以选用了纯 Lua 实现的 fperrad/lua-MessagePack。
Windows 客户端的零件都准备好了,可以开始组装了。
出现一个本地加密数据的问题, luasec 没有提供 openssl enc 接口,然后找到 luacrypto 和 lua-openssl。最后发现 lua-openssl 兼容了 luacrypto 和 luasec。 这样就可以只用 lua-openssl 替代 luacrypto 和 luasec 了。
在 windows 下编译 lua-openssl 比较顺畅, 它提供了 makefile.win, 在编译 luasec 的基础上参考它就编译成功了。 最后使用 lua-openssl 的时候,遇到了两个问题:
send
和receive
接口的实现跟 luasocket 实现的不一致,不能调用select
函数。crypto.digest
函数漏了raw
参数没有往下面传递。然后我都提了 issues 并修复了。
https://github.com/zhaozg/lua-openssl/issues/179
https://github.com/zhaozg/lua-openssl/issues/180
接下来就是遇到
openssl enc
加密的数据, lua-openssl 解密不了的问题。这里需要去理解-K -iv
参数的生成规则才行。参考这里: https://www.jianshu.com/p/813e184b56bd
AES-128-CBC 的 Key 和 iv 生成规则
等处理完数据加密解密,这个 Windows 客户端基本就基本完工了。
更新: 本地数据加密解密函数封装如下
对应 openssl 命令为:
更新: 遇到 openssl 版本不一致的情况,导致生成的 key 和 iv 的结果不一致,于是改成手动采用 sha256 计算 key 和 iv。加密解密指令如下:
再补充下服务器是如何同步最新的数据到客户端的。
每个账号最新记录的版本号,采用 openresty 的 shared.DICT 存在内存中。每次有数据变化后,把版本加一。 用心跳去检查各个客户端的数据版本号是否已过期,过期则更新版本号再推送数据。
使用 shared.DICT 而不是使用 lua-resty-lru 的原因是:这个数据需要在各个工作进程中共享, 因为不同的客户端连接的可能不是同一个进程。
遇到
ssl.bio
发送很长数据的问题,send
时返回需要重试,还没想到比较好的解决办法,直接写了个死循环一直发送。这个得再想办法改下。还要处理 Lua 读取配置文件的问题, 预计采用类似 ini 文件格式, 跟 Linux 系统一致, 放到用户目录下, 并命名为
.oclip
读取每行配置,用 gmatch 提取 key 和 value
Windows 客户端逻辑写完之后就是剩下打包的工作了,打算用 lua-static 来打包。 打包前还要处理下系统图标的打包工作。
luastatic 在 windows 下打包默认只支持 mingw, 可是我编译用的是 MSVC,主要遇到三个问题:
set NM="dumpbin /EXPORTS"
后解决is_binary_library
需要加入后缀dll
的判断最后又遇到了一个小问题 cacert.pem文件没有打包到 exe 文件,这个比较简单,写个 lua 函数把这个文件从字符串输出到临时文件就行。
生成 src/cacert.lua 的脚本如下:
更新:
文本的编码问题,统一转成 utf8
编码问题进展: 采用下面两个函数对剪切板的内容转换,可以正常处理中文了,但是 emoji 处理不了。还得继续找找原因。
更新:
编码问题终于解决了,我傻逼了,主要原因是剪切板内容读取和设置的格式采用了 ASCII 格式
CF_TEXT
. 所以还是相当于没有使用define UNICODE
. 今天试过 iconv 来转都是一样的效果,最后找到这个才发现需要改为CF_UNICODETEXT
.https://github.com/ocornut/imgui/blob/f5243712ce9fb070370cdc847ccc1b8e655fb1ce/imgui.cpp#L9616-L9658
所以 utf8 和 utf16 互转的代码其实只要这样就行了
当然调用上面的函数后记得 free.
iconv 测试代码找的这个: https://gist.github.com/duedal/1221865 iconv windows 版本找的这个: https://github.com/kiyolee/libiconv-win-build
编码问题已经搞定. 客户端代码在这里 hanxi/oclip-client
打包成 exe 文件:
Windows 客户端打独立 exe 包的工作算完成一个段落了,用的 lua-static 来打包。还需要处理下系统图标的打包工作。
系统图标在 C 代码里实现了把系统图标导出到 icon.h 文件再编译,但是我想在 Lua 里在实现一次,这样修改图标不用再次编译 tray.dll。实现的方法是从 getlantern/systray 里面借鉴来的,先把 icon 文件打包成 16 进制的 bytes 放到源码里,然后在启动的时候加载就行,不过我的实现有点不一样,我把 bytes 存在了临时文件,然后在加载,直接加载直接没成功,得抽个时间把这个改成直接加载 bytes 更合适。
更正:
getlantern/systray 使用的方法也是先把 icon 写入文件,然后再加载的。 https://github.com/getlantern/systray/blob/master/systray_windows.go#L658-L674 并且写入的文件名是根据 bytes 计算 MD5 拼接的。
然后我就把 ltray 的文件名也改成不是固定的文件名了,用临时的文件名。程序结束时删除就行。
更新:
创建快捷方式的方法打算采用 os.execute() 来执行 mklink 命令。 参考这里: https://stackoverflow.com/questions/30028709/how-do-i-create-a-shortcut-via-command-line-in-windows
创建快捷方式遇到个权限问题,win10 需要管理员权限才能 mklink. 于是网上找了个 VBS 脚本调用 UAC 执行。代码有点绕,但是可以实现功能就行。
再更新: 既然已经用了 vbs 来创建快捷方式了,那干脆就不用 mklink 命令,所以也就不需要管理员权限了,直接改成下面的就可以了:
接着又处理了下 exe 的图标问题,修改 makefile 即可。生成 icon.rc,再用 rc.exe 生成 icon.res。最后 link 的时候加上 icon.res 就行了。
处理了下打开配置文件的菜单,直接调用 notepad.exe 来编辑。
还加了个重启菜单,应该不用重启,等补上断线重连的逻辑时,走重连就行。
更新08/23: 今天把经常断线的问题找到了, 粘贴板有新数据的回调函数不能直接把数据发出去,需要缓存起来,让主线程发送。
断线重连也搞定了,起一个任务循环判断网络状态,断线则走连接逻辑,连接失败则等 5 秒。
更新 08/31
现在只支持从 '\r\n' 转 '\n' 和从 '\n' 转 '\r\n' . 单纯的 '\r' 不好转。
在这里下载:
https://github.com/hanxi/oclip-client/releases/tag/v0.0.1
更新0903:
Linux 上的客户端打算做成一个包但是分为客户端和服务端,服务端常驻后台跟远程服务器保持连接来更新本地剪切版数据,客户端 copy 数据时则先把数据发送到本地服务端, 本地服务端再推送到远程服务器a,远程服务器 paste 数据回来时则写到本地文件,同时写到 xclip或者xsel。本地客户端和服务端的通信打算采用 unix domain 或者信号。 本地客户端需要拿数据时只需要是本地临时文件取即可。经过几次尝试,最后还是采用了 UDP 的通信方式,使用 lua-signal 出现了一直发送信号的问题没能解决,而且使用 UDP 不需要额外引入第三方库。
目前已经实现完 Linux 客户端逻辑,剩下打一个 Linux 包的事情。新的 Linux 端速度真的很快了,因为只有第一次使用的时候才启动 master 会发起HTTPS连接请求,后续操作都是通过 websocket 发送数据。
更新0905:
可以去这里下载最新的版本了:
https://github.com/oclip/oclip-client/releases
今天解决了 Windows 设置快捷方式的时候出现黑窗口的问题。
https://stackoverflow.com/questions/12554237/hiding-command-prompt-called-by-system/57798301#57798301
用下面的函数替换 system 函数:
更新完毕。