Webkit 远程调试协议初探

Webkit 远程调试协议初探

任何做过 Web 开发的同学,都避免不了在浏览器内进行调试。而大部分同学的首选工具,就是 Chrome DevTools。DevTools 本身我们无需多说,是一个大家不能再熟悉的工具了。但是埋藏在 DevTools 下面的开放协议以及它赋予的众多可能性,至今仍未见到充分的剖析和应用。

Webkit 的远程调试协议是 Webkit 在 2012 年引入的。目前所有 Webkit 内核的浏览器都支持这一特性。但是我们还是以 DevTools 和 Chrome 为出发点,来做讨论。

为什么我们关注 DevTools:

原因 1:DevTools 是开源项目

DevTools 的源码就在 Google 的 blink 项目 中,高度的开放。目前这么多丰富的功能,正是 Google 和其社区的共同贡献。同时它的 License 也不拒绝任何的二次开发。

原因 2:它足够简单

DevTools 仅仅是简单的由 HTML、JavaScript、CSS、Images 组成的,本质上就是一个 WebApp,纯粹的前端应用。当你去了解、修改它时,你不需要理解 C++ 和任何编译的知识。

原因 3:它的应用架构足够开放,满足任何形式的功能扩展。

事实上 Devtools 是一个充分模块化的 JavaScript 网页应用。它的每个功能你都可以去扩展(仅需要了解 JavaScript)。

原因 4:大部分前端都已经习惯它并且喜欢它。

Webkit 的远程调试特性

谈到远程调试前,有必要先了解各组件之间的关系。

  • 浏览器拥有多个 Tab,并为每个 Tab 单独提供 WebSocket 的 Endpoint URI
  • 每个 DevTool 实例只能检视一个 Tab,即只能与一个 Tab 保持通讯

DevTools 的界面是数据驱动的。数据的来源就是 WebSocket API。Google 对 Webkit 的调试协议做了进一步的封装,提供了以 JSON 为序列化格式的 WebSocket 界面。

大家在本地电脑上就可以体验这个远程调试是怎样一回事。执行如下步骤:

  1. 彻底关闭当前 Chrome 进程
  2. 在 Chrome 的启动参数上加上 --remote-debugging-port=9222,例如 Mac 平台:
    
    
    1. open -a Google\ Chrome –args –remote-debugging-port=9222
  3. 在开启的 Chrome 浏览器里打开任意网页,例如:http://www.taobao.com/
  4. 在其他浏览器或者 Chrome 的新 Tab 打开 http://localhost:9222,你会得到这样的界面:

  5. 点击 “淘宝网” 的方框,就进入页面的调试界面了:

注意看地址栏,我们访问的是一个标准的 HTTP 协议下的网页,不是 Chrome 的私有协议。这里,你可以用 DevTools 再次检视这个页面,即按下 CMD + OPTION + i。你会发现,这真的就是一个 HTML 应用。

再观察一下这个 URL:


  1. http://localhost:9222/devtools/inspector.html?ws=localhost:9222/devtools/page/06D198AC-907F-430C-999C-16CCD7D2D489

通过 QueryString,我们告诉了 DevTools 的前端应用,它应该连接到哪个 WebSocket 服务。

你可以再你刚打开的检视 DevTools 的 DevTools(好绕口)里面,观察整个调试过程中的 WebSocket 通讯。例如:

以前用 WebSocket 做过 RPC 的同学应该看得出来,Google 实现的的确就是一个远程调用的接口。这个接口里面有两种通讯模式:

  1. request/response:就如同一个异步调用,通过请求的信息,获取相应的返回结果。这样的通讯必然有一个 message id,否则两方都无法正确的判断请求和返回的匹配状况。
  2. notification:和第一种不同,这种模式用于由一方单方面的通知另一方某个信息。和 “事件” 的概念类似。

实验一:通过调试协议获取页面加载的 Timeline 数据

通过调试协议来获取页面加载的所有网络请求并打印。为了简单,我们编写一个 Node.js 的应用来实现。大致步骤如下:

  • 用 WebSocket 客户端连接调试服务
  • 分别监听Network.requestWillBeSentNetwork.loadingFailedNetwork.loadingFinishedNetwork.responseReceivedNetwork.requestServedFromCache 的 Notification,并且打印相关的 log。
  • 发送 Page.navigate 的请求,将页面跳转到某个页面,例如:http://www.taobao.com/

这里拿到的数据足以绘制一个非常准确的页面加载的瀑布图。从调试协议里拿到的数据具有以下特点:

  • 准确,这是 Webkit 内核反馈的数据;而不是外层 JavaScript 接口的统计,也不是通过代理监控网络数据拿到的结果。
  • 丰富,有很多数据,别的方法根本拿不到。例如,缓存状况、JavaScript 方法执行情况。
  • 标准,调试协议本身已经定义了大量的 JSON 数据结构,你不需要再次进行抽象设计。

完整代码如下(请先安装好相应的 npm 模块,并且打开 Chrome 本地的 9222 调试端口):


  1. var WebSocketClient = require("websocket").client,
  2. util = require("util"),
  3. EE = require("events").EventEmitter,
  4. request = require("request"),
  5. chalk = require("chalk"),
  6. exec = require("child_process").exec;
  7. // `Commander` class is message handler that talks to debug service exposed by Chrome
  8. var Commander = function(conn) {
  9. EE.call(this);
  10. this.connection = conn;
  11. this.sendCommands = [];
  12. var self = this;
  13. Object.defineProperty(this, "nextMsgId", {
  14. get: function() {
  15. return self.sendCommands.length;
  16. },
  17. enumerable: true,
  18. configurable: false
  19. });
  20. conn.on("message", this.onMessage.bind(this));
  21. };
  22. util.inherits(Commander, EE);
  23. // Send message using websocket connection
  24. Commander.prototype.send = function(method, params, callback) {
  25. this.sendCommands.push([method, params, callback]);
  26. var msg = JSON.stringify({
  27. id: this.nextMsgId,
  28. method: method,
  29. params: params
  30. });
  31. console.log(msg);
  32. this.connection.send(msg);
  33. };
  34. //handler for receiving a message
  35. Commander.prototype.onMessage = function(msg) {
  36. var command, data;
  37. if(msg.type === "utf8") {
  38. data = JSON.parse(msg.utf8Data);
  39. if(data.id) {//it's method request/response invocation
  40. command = this.sendCommands[data.id-1];
  41. if(command) {
  42. if(command.callback) {
  43. command.callback(data.params);
  44. }
  45. } else {
  46. console.warn("unmatched message id %s", data.id);
  47. }
  48. } else {//notifications
  49. this.emit(data.method, data.params);
  50. }
  51. } else {
  52. console.warn("message of unknown encoding");
  53. }
  54. };
  55. //find tab info
  56. request("http://localhost:9222/json", function(e, res, data) {
  57. data = JSON.parse(data);
  58. var url = data[0].webSocketDebuggerUrl;
  59. if(!url) {
  60. throw new Error("no url");
  61. }
  62. var client = new WebSocketClient();
  63. //once it's connect, start our actions
  64. client.on("connect", function(conn) {
  65. console.log("client connceted");
  66. var commander = new Commander(conn);
  67. //Shoud enable this freatures
  68. commander.send("Network.enable",{});
  69. commander.send("Page.enable",{});
  70. //Listen to wanted notifications
  71. commander.on("Network.requestWillBeSent", function(data) {
  72. console.log("[%s] %s %s: %s", chalk.green(data.timestamp), chalk.blue("WillSend"), data.requestId, data.request.url);
  73. });
  74. commander.on("Network.loadingFailed", function(data) {
  75. console.log("[%s] %s %s", chalk.green(data.timestamp), chalk.red("LoadFail"), data.requestId);
  76. });
  77. commander.on("Network.loadingFinished", function(data) {
  78. console.log("[%s] %s %s", chalk.green(data.timestamp), chalk.magenta("LoadDone"), data.requestId);
  79. });
  80. commander.on("Network.responseReceived", function(data) {
  81. console.log("[%s] %s %s: %s Status %s %s", chalk.cyan(data.timestamp), chalk.red("RespRecv"), data.requestId, data.type, data.response.status, data.response.headers["Content-Length"]);
  82. });
  83. commander.on("Network.requestServedFromCache", function(data) {
  84. console.log("%s %s", chalk.gray(data.timestamp), chalk.red("RespCache"), data.requestId);
  85. });
  86. commander.on("Page.domContentEventFired", function() {
  87. console.log(chalk.bgGreen("OnDOMContentLoad\t\t\t\t\t\t\t\t"));
  88. });
  89. commander.on("Page.loadEventFired", function() {
  90. console.log(chalk.bgCyan("OnLoad\t\t\t\t\t\t\t\t"));
  91. });
  92. //Navigate to target page
  93. commander.send("Page.navigate", {url: "http://www.taobao.com"});
  94. });
  95. client.connect(url);
  96. });

运行后的结果如下:

结语

本篇内容仅仅介绍调试协议这个概念,以及它的通讯原理。并且,我们通过一个实验,来展示这套协议的强大特性。后面,我们还会讨论其他浏览器的调试协议,以及移动设备的调试。

本文来自合作伙伴“Linux中国”,原文发表于2013-04-02.

时间: 2024-09-15 09:14:12

Webkit 远程调试协议初探的相关文章

xdebug远程调试原理分析

xdebug可以控制PHP程序的执行,这意味着xdebug可以在任何时候暂停或者恢复正在运行的PHP程序.当PHP程序被暂停的时候,xdebug可以获取到程序的相关信息,比如变量的值等.xdebug也可以修改一个变量的值,然后再恢复暂停的程序,让其继续运行. xdebug配合IDE进行可视化调试的过程(类似于VisualStudio单步调试)被称为"远程调试",是因为调试时有一个Server(xdebug)和一个Client(IDE),所以在调试的时候,被调试的PHP程序和调试PHP程

使用Eclipse远程调试Java应用程序

远程调试对应用程序开发十分有用.例如,为不能托管开发平台的低端机器开发程序,或 在专用的机器上(比如服务不能中断的 Web 服务器)调试程序.其他情况包括:运行在内存 小或 CUP 性能低的设备上的 Java 应用程序(比如移动设备),或者开发人员想要将应用程 序和开发环境分开,等等. 先决条件 启动配置类型 启动配置 保存一 组用于启动程序的属性.启动配置类型是一种可以在 Eclipse 平台上启动的独特程序. 如果您还没安装该程序,请下载 Eclipse V3.4(Ganymede).在 G

远程调试Hadoop各组件

远程调试对应用程序开发十分有用.例如,为不能托管开发平台的低端机器开发程序,或在专用的机器上(比如服务不能中断的 Web 服务器)调试程序.其他情况包括:运行在内存小或 CUP 性能低的设备上的 Java 应用程序(比如移动设备),或者开发人员想要将应用程序和开发环境分开,等等. 为了进行远程调试,必须使用 Java Virtual Machine (JVM) V5.0 或更新版本. JPDA 简介 Sun Microsystem 的 Java Platform Debugger Archite

Android* 操作系统上的应用程序远程调试

Android* 操作系统上的应用程序远程调试 robert-mueller-... 于 星期日, 16/02/2014 - 20:00 提交 Android* 操作系统应用程序远程调试 Android* Debug Bridge Android Debug Bridge (ADB) 是一种命令行工具,可处理主机上的调试程序(通常为 GDB* 或 DDMS*(Dalvik* 调试监测程序服务器)以及 ADT)和目标上运行的 Android* 映像之间的调试通信. 目标映像可运行在模拟设备上或实体

使用phpstorm和xdebug实现远程调试的方法_php实例

vs的断点调试功能很强大有木有,能查看所有变量有木有.php调试很麻烦有木有,echo,var_dump写得你想吐了有木有.想体验一下ide调试的快感吗?那就来使用xdebug吧. 1.原理:xdebug是一款php调试插件,支持远程调试,就是在php文件运行的时候,能通过tcp协议,来发送调试信息到远程端口,ide在收到调试信息的时候,可以向xdebug发送单步运行,中止运行,运行等命令.这样就实现了vs那样强大的调试功能. 2.需要的东东:一款支持xdebug远程调试的ide,这里使用php

eclipse/intellij idea 远程调试hadoop 2.6.0

很多hadoop初学者估计都我一样,由于没有足够的机器资源,只能在虚拟机里弄一个linux安装hadoop的伪分布,然后在host机上win7里使用eclipse或Intellj idea来写代码测试,那么问题来了,win7下的eclipse或intellij idea如何远程提交map/reduce任务到远程hadoop,并断点调试? 一.准备工作 1.1 在win7中,找一个目录,解压hadoop-2.6.0,本文中是D:\yangjm\Code\study\hadoop\hadoop-2.

Eclipse远程调试Weblogic运行的源代码

web|源代码 看过许多远程调试的例子,大多数都是针对tomcat或者jboss服务器的,很少能搜索到weblogic服务器的远程调试例子和文章,前些天在项目开发的时候尝试了一下,感觉十分得不错,拿出来跟大家分享一下,不过我要多罗嗦几句. 远程调试的好处:我们现在开发的模式大多是本机用eclipse集成source管理环境,集成ant的编译环境,用weblogic插件集成运行环境,基本上一个eclipse又当爹又当妈的,这样的好处就是开发环境配置集中,普通的程序员只要按步骤做就可以了,开发步骤简

对 ASP.NET 应用程序启动调试应如何设置(包括远程调试)

asp.net|程序|asp.net 对 ASP.NET 应用程序启动调试应如何设置(包括远程调试) 请根据你的调试类型与操作系统选择,以下步骤请勿颠倒(不行的话,从头开始设置) PS:因本人撰写本稿时,用的是Win 2003 .Win2000 繁体版 + Visual Studio.Net 2003 英文版,故抓下来的图与说明不大一样,但这些"属性"的位置是不变的 一.本地调试 A. Windows 2000 操作系统 1.打开VS.Net工具选项 → 项目Web设置 → Web服务

VSS控制存储过程,及其asp.net的远程调试

asp.net|存储过程|控制 VSS控制存储过程,及其asp.net的远程调试 服务器安装: 一.VSS 6d 二.VS2003.net,服务器至少安装如图内容: 客户端: 一.可远对 ASP.net服务器进行远端 服务器安装完成,本机不用作特别的设置. 就可以实现在本机,对远端服务器的单步调试,并可进入到自定义类库中进行调试. 二.存储过程的VSS控制: 1.客户端安装VSS 6d 2.在菜单"工具"-->"选项"按图示设置 3.用VS.net的"