研究这个事情的起因是产品的一个需求,要求对远程的接口抓包进行实时展示。一般做法都是通过 tcpdump 进行抓包,然后使用 Wireshark 进行可视化展示,但是产品要求是要“实时”,也就不能通过生成 pcap 文件下载让用户手动打开 Wireshark 的方式了。
1. 技术选型
接到这个需求后,我们就开始了技术选型。主要就两个方向,一个是前端做数据展示,另一个是通过调用 Wireshark
做数据展示。
前端实现的话,单单靠 JavaScript
是没法达到要求的,首先没有现成的 pcap
解析库,从头开始造轮子太耗时间,而且 JavaScript
的性能瓶颈在那,首先就排除了 JavaScript
实现的可能。
虽然 JavaScript 可能达不到性能要求,但是现代浏览器已经支持 WebAssembly
(简称 WASM
),可以使用别的语言实现最消耗性能的部分。经过一番搜索,找到了一个开源的工具库:@goodtools/wiregasm,一个将 Wireshark 的包分析器编译成 WASM 的库,NICE!
如果是调用 Wireshark
做展示的话,则完全是另一套逻辑,是与操作系统强相关的路线。
使用过 迅雷
的童鞋一定对 thunder:
协议不会陌生,在你安装了迅雷的情况下,直接点击页面的 thunder:
连接,浏览器就会弹出打开迅雷的提示,点击打开后,迅雷软件会自动打开并开始下载点击的链接。包括现在的很多网盘,都采用了相同的方式从浏览器端唤醒客户端。
虽然在此之前我没做过相关需求,但是这个功能实现我一直记得,也是让我知道了从页面唤起应用的可能。
2. 技术实现
前端做可视化的话,就按照库作者提供的示例进行使用就行,中途也没什么大问题,性能也很强,测试打开 14w
个包的 pcap
文件除了解析耗时久一点,解析完成后流畅度非常好。这里有我提供的示例项目
。
虽然前端实现看起来很棒,但是缺点也很明显:解析文件完全放在内存中,解析的文件越大,内存占用越高;其次是作者只提供了一次性 load 数据的接口,并不支持动态 add 的接口,这就导致无法直接实现产品要的 实时
展示。虽然也想过通过浏览器提供的 indexedDB 来间接实现 实时解析
,但这个复杂度过高,在有更好的方案前作为备选方案。
如果是通过调用 Wireshark 客户端做数据可视化,那么就简单很多,我们只需要参照上面提到的唤起迅雷的方案,实现自定义协议,通过自定义协议调用我们写的程序做数据包获取处理,再将处理后的数据 “转发”
给 Wireshark
即可。因为之前做过调研,Wireshark
支持管道输入,所以技术上没有障碍。
2.1 数据获取程序
获取 pcap
数据本质上就是从远程接收二进制数据,能支持传输二进制数据的方式主要是基于 HTTP
的文件下载,另一个就是 websocket
的二进制消息。
文件下载因为文件流不知道什么时候会结束,会产生内存积压从而导致程序崩溃,而 websocket
是基于消息,不会有这个问题,故 tcpdump
的结果传输就基于 websocket
传输。
代码实现上,虽然我们很熟悉 nodejs
,但是考虑到目前 nodejs
构建出的可执行程序的体积,我们还是换用了其它编译语言:golang,GUI 框架使用了 lxn/walk。
2.2 自定义协议实现
浏览器能处理的自定义协议完全就跟操作系统强相关了,通过查询学习,这里分别列出 Windows
和 MacOS
下的注册方式。
2.2.1 Windows
Windows
下是通过添加注册表的方式进行注册,下面是注册表内容,需根据实际情况做修改。
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\custom.protocol]
@="URL:custom.protocol"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\custom.protocol\DefaultIcon]
@="C:\\WINDOWS\\system32\\cmd.exe,1"
[HKEY_CLASSES_ROOT\calc\shell]
[HKEY_CLASSES_ROOT\calc\shell\open]
[HKEY_CLASSES_ROOT\calc\shell\open\command]
@="\"C:\\WINDOWS\\system32\\calc.exe\" \"%1\""
将上面内容另存为 reg 文件并执行后,我们就在系统中注册了“custom.protocol:”协议了。
2.2.2 MacOS
MacOS 下是通过修改自定义程序的"Contents/Info.plist"文件来实现,在最外层的 Dict 下的任意位置,添加如下内容即可。
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>CalcURLHandler</string>
<key>CFBundleURLSchemes</key>
<array>
<string>calc</string>
</array>
</dict>
</array>
注意,如果是修改已有的程序,程勋签名会失效,需要重新签名并打开一次程序才能注册自定义协议。
3. 安装包构建
目前只打包了 Windows 下的安装包,使用的打包工具是 Inno Setup
,程序自带向导,构建一个简单的安装包还是很容易的。
因为我们程序依赖 Wireshark
,所以最好是在安装包中自带 Wireshark
,如果用户环境没有安装 Wireshark
,那我们可以提示用户安装 Wireshark
,这部分 iss
代码如下:
[Files]
Source: "files\{#WiresharkInstaller}"; DestDir: "{tmp}"; Flags: dontcopy
[Tasks]
Name: installWireshark; Description: "Install Wireshark"; Check: ShouldInstallWireshark
[Code]
const
ERROR_SUCCESS = 0;
function ShouldInstallWireshark: Boolean;
var
InstallPath: string;
begin
Result := not RegQueryStringValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\Wireshark.exe', '', InstallPath);
if Result then
Result := not RegKeyExists(HKEY_CURRENT_USER, 'Software\Wireshark');
end;
procedure CurStepChanged(CurStep: TSetupStep);
var
ResultCode: Integer;
begin
if CurStep = ssPostInstall then
begin
if WizardIsTaskSelected('installWireshark') then
begin
ExtractTemporaryFile('{#WiresharkInstaller}');
Exec(ExpandConstant('{tmp}\{#WiresharkInstaller}'), '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode);
end;
end;
end;
4. 总结
整个功能下来,复杂度感觉没有多少,完全就是看研发人员是否对涉及到的东西是否有一定的了解,知识点很杂,考验的就是一个知识广度。