2007 年 6 月
简介
开发与网络服务交互的应用会有一系列独特的问题。令人沮丧的一个常见原因是无法确切地知道向服务器发送了什么消息,或者收到了哪些响应。有些最难跟踪的 bug 是由于我们认为要发送到服务器的信息与实际传输的内容之间断开所造成的。
本文介绍几种有助于使数据在网上更显眼且更实用的工具。这些工具通常称为“数据包嗅探器”,用于捕获在您的网络接口间移动的所有网络数据包。检查这些数据包的内容以及收发数据包的顺序可能是一种有用的调试方法。
示例:检索公开 Feed
我为骑自行车慈善机构组建了一个自行车团队,还创建了一个活动来组织信息会话、团队筹款和培训等活动。我已公开此日历,以便团队成员和其他乘客可以查看日历并参加活动。我还想发送一份简报来介绍即将举办的活动,因此您可以使用 Google Calendar data API 查询此日历并检索活动,而不是复制 Google 日历网站上的信息。
Google Calendar API 文档说明了如何使用 RESTful Google Data API 以编程方式与我的日历互动。(编者注:从 v3 开始,Google Calendar API 不再使用 Google 数据格式。)首先,请点击日历设置页面上的 按钮,以获取日历的活动 Feed 网址:
http://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic
通过使用 Google 日历文档,我可以检索和显示此类日历活动,其中 PUBLIC_FEED_URL
包含活动 Feed 网址。
CalendarService myService = new CalendarService("exampleCo-fiddlerExample-1"); final String PUBLIC_FEED_URL = "http://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic"; URL feedUrl = new URL(PUBLIC_FEED_URL); CalendarEventFeed resultFeed = myService.getFeed(feedUrl, CalendarEventFeed.class); System.out.println("All events on your calendar:"); for (int i = 0; i < resultFeed.getEntries().size(); i++) { CalendarEventEntry entry = resultFeed.getEntries().get(i); System.out.println("\t" + entry.getTitle().getPlainText()); } System.out.println();
此操作会生成我的日历中基本活动的列表:
All events on your calendar: MS150 Training ride Meeting with Nicole MS150 Information session
上述代码段显示了日历活动的标题,但我们从服务器收到的其余数据呢?Java 客户端库无法以 XML 的形式轻松输出 Feed 或条目,即使这样,XML 也不是完整的。在该请求中附带的 HTTP 标头会怎样?查询是经过代理还是重定向?随着操作越来越复杂,这些问题变得越来越重要,特别是在出现问题和出错时。数据包嗅探软件可以通过显示网络流量来回答这些问题。
tcpdump
tcpdump 是一款命令行工具,可在类似于 Unix 的平台上使用,但也有一个名为 WinDump 的 Windows 端口。与大多数数据包嗅探器一样,TCP 会将您的网卡置于混乱模式(需要超级用户权限)。如需使用 tcpdump,只需指定要监听的网络接口,网络流量就会发送到 stdout:
sudo tcpdump -i eth0
如果您运行此命令,则会收到各种网络流量的冲击,其中部分网络流量可能不会识别。您可以稍后将输出转发到某个文件,然后再执行 grep 操作,但这可能会导致一些文件非常大。大多数数据包捕获软件都内置了一些过滤机制,因此您只捕获到所需的内容。
tcpdump 支持根据网络流量的各种特征进行过滤。例如,您可以告知 tcpdump 仅通过端口 80(HTTP 消息)捕获进出服务器的流量,只需将服务器的主机名插入以下表达式即可:
dst or src host <hostname> and port 80
对于与过滤条件表达式匹配的每个数据包,tcpdump 将显示时间戳、数据包的来源和目标,以及多个 TCP 标志。此信息显示数据包的发送和接收顺序,因此可能很有价值。
此外,查看数据包的内容通常也很有用。“-A”标志指示 tcpdump 以 ASCII 格式输出每个数据包,并公开 HTTP 标头和消息正文。“-s”标志用于指定要显示的字节数(其中“-s 0”表示不截断邮件正文)。
综上,我们可获取以下命令:
sudo tcpdump -A -s 0 -i eth0 dst or src host <hostname> and port 80
如果您运行此命令,然后执行上述简短的 .Java 示例,则会看到此操作涉及的所有网络通信。在该流量中,您将看到 HTTP GET
请求:
22:22:30.870771 IP dellalicious.mshome.net.4520 > po-in-f99.google.com.80: P 1:360(359) ack 1 win 65535 E.....@....\...eH..c...P.=.....zP......GET /calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic HTTP/1.1 User-Agent: exampleCo-fiddlerExample-1 GCalendar-Java/1.0.6 GData-Java/1.0.10(gzip) Accept-Encoding: gzip Cache-Control: no-cache Pragma: no-cache Host: www.google.com Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 Connection: keep-alive
您还会看到包含 Google 数据 Feed 的 200 OK
响应消息。请注意,该 Feed 拆分为 4 个数据包:
22:22:31.148789 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: . 1:1431(1430) ack 360 win 6432 E...1 ..2.I.H..c...e.P.....z.=.:P..M...HTTP/1.1 200 OK Content-Type: application/atom+xml; charset=UTF-8 Cache-Control: max-age=0, must-revalidate, private Last-Modified: Mon, 11 Jun 2007 15:11:40 GMT Transfer-Encoding: chunked Date: Sun, 24 Jun 2007 02:22:10 GMT Server: GFE/1.3 13da <?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:gCal='http://sc hemas.google.com/gCal/2005' xmlns:gd='http://schemas.google.com/g/2005'><id>http ://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.goo gle.com/public/basic</id><updated>2007-06-11T15:11:40.000Z</updated><category sc heme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2 005#event'></category><title type='text'>MS150 Training Schedule</title><subtitl e type='text'>This calendar is public</subtitle><link rel='http://schemas.google .com/g/2005#feed' type='application/atom+xml' href='http://www.google.com/calend ar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic'></ link><link rel='self' type='application/atom+xml' href='http://www.google.com/ca lendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic ?max-results=25'></link><author><name>Lane LiaBraaten</name><email>api.lliabraa@ gmail.com</email></author><generator version='1.0' uri='http://www.google.com/ca lendar'>Google Calendar</generator><openSearch:totalRe 22:22:31.151501 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: . 1431:2861(1430) ack 360 win 6432 E...1!..2.I.H..c...e.P.......=.:P.. 2...sults>3</openSearch:totalResults><openSe arch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch :itemsPerPage><gd:where valueString=''></gd:where><gCal:timezone value='America/ Los_Angeles'></gCal:timezone><entry><id>http://www.google.com/calendar/feeds/24v j3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic/dgt40022cui2k3j 740hnj46744</id><published>2007-06-11T15:11:05.000Z</published><updated>2007-06- 11T15:11:05.000Z</updated><category scheme='http://schemas.google.com/g/2005#kin d' term='http://schemas.google.com/g/2005#event'></category><title type='text'>M S150 Training ride</title><summary type='html'>When: Sat Jun 9, 2007 7am to 10am &nbsp; PDT<br> <br>Event Status: confirmed</summary><conte nt type='text'>When: Sat Jun 9, 2007 7am to 10am&nbsp; PDT<br> <b r>Event Status: confirmed</content><link rel='alternate' type='text/html' href='http://www.google.com/calendar/event?eid=ZGd0NDAwMjJjdWkyazNqNzQwaG5qNDY3 NDQgMjR2ajNtNXBsMTI1YmgyaWpiYm5laDk1M3NAZw' title='alternate'></link><link rel=' self' type='application/atom+xml' href='http://www.google.com/calendar/feeds/24v j3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic/dgt40022cui2k3j 740hnj46744'></link><author><name>MS150 Training Schedule</name></author><gCal:s endEventNotifications value='false'></gCal:sendEventNotifications></entry><entry ><id>http://www.google.com/cal 22:22:31.153097 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: . 2861:4291(1430) ack 360 win 6432 E...1#..2.I.H..c...e.P.......=.:P.. ....endar/feeds/24vj3m5pl125bh2ijbbneh953s%4 0group.calendar.google.com/public/basic/51d8kh4s3bplqnbf1lp6p0kjp8</id><publishe d>2007-06-11T15:08:23.000Z</published><updated>2007-06-11T15:10:39.000Z</updated ><category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.g oogle.com/g/2005#event'></category><title type='text'>Meeting with Nicole</title ><summary type='html'>When: Mon Jun 4, 2007 10am to 11am&nbsp; PDT<br> <br>Where: Conference Room B <br>Event Status: confirmed</summ ary><content type='text'>When: Mon Jun 4, 2007 10am to 11am&nbsp; PDT<br& gt; <br>Where: Conference Room B <br>Event Status: confirmed <br>Event Description: Discuss building cycling team for MS150</content><l ink rel='alternate' type='text/html' href='http://www.google.com/calendar/event? eid=NTFkOGtoNHMzYnBscW5iZjFscDZwMGtqcDggMjR2ajNtNXBsMTI1YmgyaWpiYm5laDk1M3NAZw' title='alternate'></link><link rel='self' type='application/atom+xml' href='http ://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.calendar.goo gle.com/public/basic/51d8kh4s3bplqnbf1lp6p0kjp8'></link><author><name>MS150 Trai ning Schedule</name></author><gCal:sendEventNotifications value='false'></gCal:s endEventNotifications></entry><entry><id>http://www.google.com/calendar/feeds/24 vj3m5pl125bh2ijbbneh953s%40group.calendar.google.com/public/basic/va41amq3r08dhh kpm3lc1abs2o</id><published>20 22:22:31.190244 IP po-in-f99.google.com.80 > dellalicious.mshome.net.4520: P 4291:5346(1055) ack 360 win 6432 E..G1$..2.K.H..c...e.P.....<.=.:P.. ....07-06-11T15:10:08.000Z</published><updat ed>2007-06-11T15:10:08.000Z</updated><category scheme='http://schemas.google.com /g/2005#kind' term='http://schemas.google.com/g/2005#event'></category><title ty pe='text'>MS150 Information session</title><summary type='html'>When: Wed Jun 6, 2007 4pm to Wed Jun 6, 2007 5pm&nbsp; PDT<br> <br>Event Statu s: confirmed</summary><content type='text'>When: Wed Jun 6, 2007 4pm to Wed Jun 6, 2007 5pm&nbsp; PDT<br> <br>Event Status: confirmed< /content><link rel='alternate' type='text/html' href='http://www.google.com/cale ndar/event?eid=dmE0MWFtcTNyMDhkaGhrcG0zbGMxYWJzMm8gMjR2ajNtNXBsMTI1YmgyaWpiYm5la Dk1M3NAZw' title='alternate'></link><link rel='self' type='application/atom+xml' href='http://www.google.com/calendar/feeds/24vj3m5pl125bh2ijbbneh953s%40group.c alendar.google.com/public/basic/va41amq3r08dhhkpm3lc1abs2o'></link><author><name >MS150 Training Schedule</name></author><gCal:sendEventNotifications value='fals e'></gCal:sendEventNotifications></entry></feed>
此输出包括所有 HTTP 标头和内容,以及多个加密 TCP 标志。所有数据都显示在这里,但是很难理解。有多种图形工具可让您更轻松地查看这些数据。
WireShark(原 Ethereal)
WireShark 以多种方式显示网络流量。
WireShark 是一种使用 libpcap(一款 tcpdump 正是基于此库构建的)构建的图形工具,在 Linux、Mac OS X 和 Windows 上均有提供。WireShark 的 GUI 提供了多种解释和捕获数据包捕获数据的新方式。例如,在从您的网络接口捕获数据包时,它们会根据它们使用的协议以不同的颜色显示。您还可以按时间戳、来源、目的地和协议对流量进行排序。
如果您在数据包列表中选择了一行,Wireshark 会在人类可读树中的数据包标头中显示 IP、TCP 和其他具体协议的信息。此类数据也以十六进制和 ASCII 格式显示在屏幕底部。
WireShark 的可视化特性让网络流量更易于理解,但在大多数情况下,您仍需过滤网络流量。WireShark 拥有强大的过滤功能,包括对数百种协议的支持。
提示:如需查看可用协议和构建复杂过滤器,请点击 WireShark 窗口顶部附近的 按钮。
要重新创建上述 tcpdump 示例中使用的过滤器,您可以将以下表达式插入 WireShark 过滤器框中:
ip.addr==<your IP address> && tcp.port==80
或利用 WireShark 的 HTTP 知识:
ip.addr==<your IP address> && http
这样就能过滤捕获结果,仅显示与 Google 日历服务器交互时涉及的数据包。您可以点击每个数据包以查看其内容,并将其拼成处理记录。
提示:您可以右键点击其中一个数据包,然后选择“遵循 TCP 数据流”,以便在单个窗口中依序显示请求和响应。
WireShark 提供了多种方法来保存您的拍摄信息。您可以保存其中一个、部分或所有数据包。如果您正在查看某个 TCP 流,则只需点击“另存为”按钮即可仅保存相关数据包。您也可以导入 tcpdump 捕获的输出并在 WireShark 中查看。
问题:SSL 和加密
数据包捕获工具的一个常见缺点是无法查看通过 SSL 连接加密的数据。以上示例访问了一个公开 Feed,因此不需要使用 SSL。但是,如果该示例访问了不公开的 Feed,则客户端将需要通过 Google 身份验证服务进行身份验证,因为该服务需要 SSL 连接。
下面这段代码与上例类似,但这里的 CalendarService
请求获取用户的日历元 Feed,这是一种需要进行身份验证的私有 Feed。如需进行身份验证,只需调用 setUserCredentials
方法即可。此方法会触发对 StreetView 服务的 HTTPS 请求,并从响应中获取身份验证令牌。然后,CalendarService
对象会在所有后续请求中添加身份验证令牌。
CalendarService myService = new CalendarService("exampleCo-fiddlerSslExample-1"); myService.setUserCredentials(username, userPassword); final String METAFEED_URL = "http://www.google.com/calendar/feeds/default"; URL feedUrl = new URL(METAFEED_URL); CalendarFeed resultFeed = myService.getFeed(feedUrl, CalendarFeed.class); System.out.println("Your calendars:"); for (int i = 0; i < resultFeed.getEntries().size(); i++) { CalendarEntry entry = resultFeed.getEntries().get(i); System.out.println("\t" + entry.getTitle().getPlainText()); } System.out.println();
请考虑对身份验证和访问专用 Google 数据 API Feed 所需的网络流量:
- 向 Paging 服务提交用户凭据
- 将 HTTP
POST
发送到 https://www.google.com/accounts/SafeFrame,邮件正文中包含以下参数:- 电子邮件 - 用户的电子邮件地址。
- Passwd - 用户的密码。
- source - 用于标识您的客户端应用。应采用以下格式:companyName-applicationName-versionID。这些示例使用名称 ExampleCo-FiddlerSSLExample-1。
- service - Google 日历服务名称为“cl”。
- 将 HTTP
- 接收授权令牌
- 如果身份验证请求失败,您会收到 HTTP 403“禁止访问”状态代码。
- 如果成功,服务的响应是 HTTP 200 OK 状态代码,以及响应正文中的三个长字母数字代码:
SID
、LSID
和Auth
。Auth
值是授权令牌。
- 请求私人日历元 Feed
- 使用下面的标头将 HTTP
GET
发送到 http://www.google.com/calendar/feed/default:
Authorization: GoogleLogin auth=<yourAuthToken>
- 使用下面的标头将 HTTP
请尝试运行此代码段,并查看 WireShark 中的网络流量(使用“http || ssl”作为过滤条件)。您将看到该事务涉及的 SSL 和 TLS 数据包,但 Paging 请求和响应数据包在“应用数据”数据包中经过加密。别担心,接下来我们将介绍一款能够真正揭示这类加密信息的工具。
Fiddler
Fiddler 是另一个图形数据包嗅探工具,但它的行为方式与目前所介绍工具截然不同。Fiddler 是您的应用与您正在交互的远程服务之间的一种代理,可有效成为中间人。Fiddler 会与您的应用和远程 Web 服务建立 SSL 连接,对来自一个端点的流量进行解密、捕获明文,并在发送流量之前重新加密流量。很抱歉,Fiddler 只能让您所有 Mac 和 Linux 用户使用 Windows 中的操作系统。
注意:SSL 支持需要 Fiddler 版本 2 和 .NET Framework 版本 2.0。
在 Fiddler 中查看网络流量主要通过会话检查器标签页完成。以下调试子标签页最有助于调试 Google Data API 的问题:
- 标头 - 以可收起的树格式显示 HTTP 标头。
- Auth - 显示 Authentication 标头。
- Raw - 以 ASCII 文本显示网络数据包的内容
提示:点击 Fiddler 窗口左下角的 图标可开启和关闭拍摄功能。
Fiddler 使用 .NET Framework 将网络连接配置为使用 Fiddler 作为代理。这意味着,您使用 Internet Explorer 或使用 .NET 代码建立的任何连接都将默认显示在 Fiddler 中。但是,由于 Java 采用不同的方式来设置 HTTP 代理,因此系统不会显示上述 Java 示例的流量。
在 Java 中,您可以使用系统属性设置 HTTP 代理。Fiddler 会在端口 8888 上运行,因此对于本地安装,您可以添加以下几行代码,以使 Java 代码使用 Fiddler 作为 HTTP 和 HTTPS 的代理:
System.setProperty("http.proxyHost", "localhost"); System.setProperty("http.proxyPort", "8888"); System.setProperty("https.proxyHost", "localhost"); System.setProperty("https.proxyPort", "8888");
如果使用以下行运行示例,您会实际从 Java 安全软件包中获取令人反感的堆栈轨迹:
[java] Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
Fiddler 可以解密和显示 SSL 流量。
如果无法验证通过 SSL 连接从服务器返回的证书,就会出现此错误。在这种情况下,Fiddler 只是充当中间人,而不良证书却来自此。Fiddler 会即时生成证书,但由于 Fiddler 不是受信任的颁发者,因此这些证书会导致 Java 无法设置 SSL 连接。
注意:当 Fiddler 运行时,您在 Internet Explorer 中建立的任何 SSL 连接都将触发“安全提醒”,询问您是否想继续接受粗略证书。您可以点击“查看证书”,查看 Fiddler 生成的证书。
那么,如何才能解决这种安全异常呢?基本上,您需要重新配置 Java 的安全框架才能信任所有证书。幸运的是,您无需焕然一新。请查看 Francis Labrie 的解决方案,并在上面的示例中添加 SSLUtilities.trustAllHttpsCertificates()
方法。
将 Java 配置为使用 Fiddler 作为代理并停用默认证书验证后,您可以运行该示例,查看通过网络发送的所有明文流量。不要窃取我的密码!
请注意,此身份验证事务只是 SSL 流量的一个小示例。某些 Web 应用仅使用 SSL 连接,因此调试 HTTP 流量是不可能的,因为无法解密数据。
总结
tcpdump 可在 Linux、Mac OS X 和 Windows 上运行,当您知道需要的内容并且只需快速截取快照时,就可以使用此工具。不过,也有一些图形工具能够以更易于理解的格式显示网络流量。tcpdump 具有比本文中介绍的选项更多的选项和过滤功能。如需 tcpdump 功能的完整说明,请输入“man tcpdump”或访问 tcpdump man page(在线)。
WireShark 也可用于 Linux、Mac OS X 和 Windows。我们对数百种协议的内置支持使得 WireShark 成为许多应用(而不仅仅是 HTTP 调试)的实用工具。这种介绍几乎没有触及 WireShark 的许多功能。如需了解详情,请输入“man Wireshark”或访问 WireShark 网站。
Fiddler 还有许多很棒的功能,但与众不同的是它能够解密 SSL 流量。如需了解详情,请访问 Fiddler2 网站。
这些数据包嗅探应用是你工具区中的必备工具,而且观察者会注意到它们全部都免费!下次使用 Google API 时,如果您发现可疑内容,请提取其中一个网络分析器,并仔细研究在线上的内容。如果您找不到问题,可以随时将问题发布到我们的论坛。添加相关的网络信息可以帮助其他人了解和诊断您的具体问题。
祝您好运,嗅到快乐!