<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Rei的人参笔记 &#187; XMPP</title>
	<atom:link href="http://chloerei.com/tag/xmpp/feed" rel="self" type="application/rss+xml" />
	<link>http://chloerei.com</link>
	<description>生活、技术、牢骚</description>
	<lastBuildDate>Mon, 30 Aug 2010 15:59:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>XMPP 总结 优缺点 未来发展</title>
		<link>http://chloerei.com/2010/05/16/403</link>
		<comments>http://chloerei.com/2010/05/16/403#comments</comments>
		<pubDate>Sat, 15 May 2010 19:44:39 +0000</pubDate>
		<dc:creator>Rei</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[XMPP]]></category>

		<guid isPermaLink="false">http://chloerei.com/?p=403</guid>
		<description><![CDATA[7. XMPP 优缺点总结 经过实际编写了 XMPP 客户端 QTalk，我对 XMPP 的优缺点有了更深的认识。XMPP的优点和缺点均源自其开放性，其中对 XMPP 缺点的思考，可以解释为何目前 XMPP 应用功能过于单一，多用在轻量级的通信服务中。充分认识 XMPP 的缺点，有助于以后设计开发比 XMPP 更好的即时通信协议。 7.1 优点 XMPP 采取完全开放、可扩展的发展策略，是能成为跨越商业机构 (Google，Facebook 等) 的即时通信协议的重要保障。 7.1.1 完全开放 XMPP 完全开放，不受任何一家商业公司控制，其他商业公司或组织才能放心使用，不必害怕有一天某家公司将 XMPP 协议封闭。 7.1.2 可扩展 XMPP 采用 XML 作为数据传输的格式，是四大开放即时通讯协议 (其他三个为：IMPP、PRIM、SIP ) 中最灵活的。使用 XMPP 的公司或组织才能按需定制自己所需的功能。不少公司利用 XMPP 开发了即时聊天以外的应用，例如微博始祖 Twitter.com 在2006年上线过一个 XMPP 机器人 (twitter@twitter.com) [16]，用户可以经绑定后使用任何 XMPP 帐号添加其为好友，在聊天窗口内向 Twitter.com 发表微博信息 (现在该功能已下线)。 7.2 [...]]]></description>
			<content:encoded><![CDATA[<h3>7. XMPP 优缺点总结</h3>
<p>经过实际编写了 XMPP 客户端 QTalk，我对 XMPP 的优缺点有了更深的认识。XMPP的优点和缺点均源自其开放性，其中对 XMPP 缺点的思考，可以解释为何目前 XMPP 应用功能过于单一，多用在轻量级的通信服务中。充分认识 XMPP 的缺点，有助于以后设计开发比 XMPP 更好的即时通信协议。</p>
<h4>7.1 优点</h4>
<p>XMPP 采取完全开放、可扩展的发展策略，是能成为跨越商业机构 (Google，Facebook 等) 的即时通信协议的重要保障。</p>
<h5>7.1.1 完全开放</h5>
<p>XMPP 完全开放，不受任何一家商业公司控制，其他商业公司或组织才能放心使用，不必害怕有一天某家公司将 XMPP 协议封闭。</p>
<h5>7.1.2 可扩展</h5>
<p>XMPP 采用 XML 作为数据传输的格式，是四大开放即时通讯协议 (其他三个为：IMPP、PRIM、SIP ) 中最灵活的。使用 XMPP 的公司或组织才能按需定制自己所需的功能。不少公司利用 XMPP 开发了即时聊天以外的应用，例如微博始祖 Twitter.com 在2006年上线过一个 XMPP 机器人 (twitter@twitter.com) [16]，用户可以经绑定后使用任何 XMPP 帐号添加其为好友，在聊天窗口内向 Twitter.com 发表微博信息 (现在该功能已下线)。<span id="more-403"></span></p>
<h4>7.2 缺点</h4>
<p>XMPP 的缺点也来源于其开放性和扩展性，概括的说，XMPP 拥有严格的制订方 (IETF)，却没有坚实的执行方。</p>
<h5>7.2.1 各方使用的功能集不同</h5>
<p>XMPP 的核心协议和常用扩展仅包含很有限的功能，如纯文本传输、文件传输。而应用 XMPP 的公司和组织只会实现己方觉得有用的扩展协议。例如，Gtalk 的服务器就没有支持群聊功能 [XEP-0045] Multi-User Chat [17]，Gtalk 用户要使用群聊，需要自行查找第三方的群聊服务器，安全性和便捷性收到影响。</p>
<h5>7.2.2 新功能难以推广</h5>
<p>当一家公司或组织为 XMPP 加入扩展功能时，难于将其推广至整个 XMPP 网络。例如，由GTalk 提出的 Jingle 协议，及其后的一系列基于 Jingle 的文件传输、音视频流传输协议，虽然已经提上 IETF 标准化议程，但目前只有 Google 自家在使用。</p>
<p>新扩展协议从产生到被接受，会经历漫长的推广期。即使新协议非常实用，也不一定能部署到别家公司的产品上。例如 Facebook Chat 定位在轻量级的网页聊天上，其应用 Google Jingle 的需求不大。结果一个新扩展协议很可能只是只会在自家公司的产品上使用。而各家 XMPP 产品添加自己的扩展协议，造成功能集不一致，最终各个 XMPP 产品会形成自己的生态圈，相互之间只会通过 XMPP 核心部分的功能交集沟通。</p>
<h4>7.3 如何约束开放？</h4>
<p>XMPP 跨商业公司的成功是源自开放，而 XMPP  功能贫乏的失败也是源自开放。可见，开放的协议要取得成功，并不是采取多方定制、任其发展就可以达到的。那么，该如何促使一个开放协议获得成功？</p>
<h5>7.3.1 先应用，后开放</h5>
<p>我认为一个开放协议要向走向成功，应该先推出重量级应用，然后开放协议。</p>
<p>先推出重量级应用，即完全由己方制订应用所需的功能集，不采取多方讨论的形式。一个经过多方讨论的协议，很容易变成目前 XMPP 这样核心精简、扩展自选，实际上没有功能集的分裂状态。集权式的开发才能保证协议的核心特性足够丰富。</p>
<p>当一个应用被广泛使用后，再进行协议的开放，让第三方公司或组织自由使用。但是开放后，功能集和协议的制订仍然由己方主导，以免协议走向分裂。如果该应用没有被广泛应用，第三方公司或组织就不会产生应用需求，所以成功的关键仍然是这个应用的功能是否丰富，是否能吸引足够多的用户。</p>
<p>采用先应用、后开放模式取得成功的案例有 Twitter.com。Twitter 被称为 Web 时代开放应用的典范，但是目前的 Twitter API [18]并不是一开始就存在的，而是在发展壮大的过程中逐步发布的。依靠 Twitter API，Twitter 被嵌入到各种图片站、地理信息定位、阅读器分享等应用中。先应用、后开放的模式并不阻碍 Twitter 成为一个广泛接受的开放应用。</p>
<h5>7.3.2 Google Wave 的启发</h5>
<p>Google Wave，按照 Google 2009年5月27日在谷歌 I/O上的说法是“一种个人通信和协作工具”。它是一个Web服务、计算平台和通信协议，旨在合并电子邮件、即时通信、wiki和社交网络。 由悉尼分公司开发，目前已开始小范围公测。它有一个强大的实时协作和强大的拼写检查功能，可以自动翻译40种语言，以及许多其他的扩展。Google 2009年7月20日在官方博客宣布，在2009年9月30日，测试 Google Wave的用户数将扩大到约100,000。[19] Wave 是将来很有潜力的通讯应用。</p>
<p>Google Wave 的开发过程就是使用先应用，后开放的模式。Google Wave 首次发布的时候，大部分功能已经可以使用，同时承诺 Wave 会成为一个完全开源开放协议的应用。随后 Google 通过邀请注册的方式，逐步测试开放 API。目前 Wave 仍在持续开发中，不过开放进度并没有当初发布时那么让人眼前一亮。至2010年5月为止，Wave 的客户-服务端协议仍未发布，Wave 需要加快协议的定制工作。</p>
<p>有意思的是，根据 Google 发布的服务端协议文档[20]，Wave 的服务-服务端通信使用了 XMPP 协议来交换数据包，但是 Wave 本身没有设计用于和现有 XMPP 服务通讯。这对现有 XMPP 使用者是一个启发：不一定要直接使用 XMPP 完成目标功能，而是借助 XMPP 实现即时性数据包交换，在 XMPP 之上搭建功能更完善的应用层，正如 HTTP 协议只是被用作传输协议。</p>
<p>[16] http://blog.twitter.com/2006/10/use-twitter-by-instant-message.html</p>
<p>[17] http://xmpp.org/extensions/xep-0045.html</p>
<p>[18] http://dev.twitter.com/</p>
<p>[19] http://zh.wikipedia.org/zh-cn/Google_Wave</p>
<p>[20] http://www.waveprotocol.org/draft-protocol-specs/draft-protocol-spec</p>
]]></content:encoded>
			<wfw:commentRss>http://chloerei.com/2010/05/16/403/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>XMPP 实践 二 程序架构</title>
		<link>http://chloerei.com/2010/05/15/367</link>
		<comments>http://chloerei.com/2010/05/15/367#comments</comments>
		<pubDate>Sat, 15 May 2010 12:15:23 +0000</pubDate>
		<dc:creator>Rei</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[XMPP]]></category>
		<category><![CDATA[编程]]></category>

		<guid isPermaLink="false">http://chloerei.com/?p=367</guid>
		<description><![CDATA[6. QTalk 客户端的设计 在此，将我编写的 xmpp 客户端命名为 QTalk ( Qt + Gtalk)。 (blog 特有内容： 开源了，放在 http://github.com/chloerei/qtalk) 由于选择了 Gtalk 服务器作为服务端，qxmpp 库作为程序的 XMPP 的通信模块，所以 QTalk 编写的主要工作在于功能与用户界面的实现。 QTalk 的预期功能如下： GTalk 帐号登录 列出联系人名单，获取联系人 VCard 资料、状态信息 向已有联系人收发纯文本消息 向已有联系人收发文件 增删联系人 人性化设置 QTalk 的运行截图见附录 一。 6.1 QTalk 结构 QTalk 的结构可分为通信模块和用户界面两大块，层次结构如图四所示： 图四 QTalk 层次结构 通信模块的 QXmppClient 由 qxmpp 库提供，QXmppClient 一方面通过 XMPP 协议与 GTalk 服务器通信，一方面通过 [...]]]></description>
			<content:encoded><![CDATA[<h3>6. QTalk 客户端的设计</h3>
<p>在此，将我编写的 xmpp 客户端命名为 QTalk ( Qt + Gtalk)。</p>
<p>(blog 特有内容： 开源了，放在 <a href="http://github.com/chloerei/qtalk">http://github.com/chloerei/qtalk</a>)</p>
<p>由于选择了 Gtalk 服务器作为服务端，qxmpp 库作为程序的 XMPP 的通信模块，所以 QTalk 编写的主要工作在于功能与用户界面的实现。</p>
<p>QTalk 的预期功能如下：</p>
<ul>
<li>GTalk 帐号登录</li>
<li>列出联系人名单，获取联系人 VCard 资料、状态信息</li>
<li>向已有联系人收发纯文本消息</li>
<li>向已有联系人收发文件</li>
<li>增删联系人</li>
<li>人性化设置</li>
</ul>
<p>QTalk 的运行截图见附录 一。</p>
<h4>6.1 QTalk 结构</h4>
<p>QTalk 的结构可分为通信模块和用户界面两大块，层次结构如图四所示：<span id="more-367"></span></p>
<p style="text-align: center;"><a href="http://chloerei.com/wp-content/uploads/2010/05/4-qtalk-frame.jpg"><img class="aligncenter size-full wp-image-375" title="4 qtalk-frame" src="http://chloerei.com/wp-content/uploads/2010/05/4-qtalk-frame.jpg" alt="" width="514" height="298" /></a>图四 QTalk 层次结构</p>
<p>通信模块的 QXmppClient 由 qxmpp 库提供，QXmppClient 一方面通过 XMPP 协议与 GTalk 服务器通信，一方面通过 qxmpp API 协助 MainWindow 收发 XMPP 数据包。</p>
<p>MainWindow 是用户界面的中心，管理了登录窗口，联系人列表，聊天窗口和文件传输等窗口部件。MainWindow 通过 qxmpp API 收发 XMPP 数据包，将数据包分类，更新到相应窗口部件上。</p>
<h4>6.2 程序登录、获取联系人列表</h4>
<p>QTalk 的登录流程为：用户输入账户密码，发送登录请求，获取保存在服务端的联系人列表。程序逻辑如图五所示：</p>
<p style="text-align: center;"><a href="http://chloerei.com/wp-content/uploads/2010/05/5-login-logic.jpg"><img class="aligncenter size-full wp-image-378" title="5 login-logic" src="http://chloerei.com/wp-content/uploads/2010/05/5-login-logic.jpg" alt="" width="433" height="358" /></a>图五 登录逻辑</p>
<p>由于使用的是 GTalk 服务器，用户帐号可以通过注册 Gmail 获得。</p>
<p>联系人列表将会以列表视图的方式显示在 QTalk 主窗口中。</p>
<h4>6.3 收发聊天信息</h4>
<p>用户成功登录后，可以通过点击主窗口中的联系人列表，打开聊天窗口，对某个联系人发起对话。发送信息时，在聊天窗口输入的信息会被传递到 qxmpp 中进行发送；收到信息时，如果该联系人的聊天窗口已打开，则直接显示在聊天框内，否则会在系统托盘中提示收到了新消息。收发信息的逻辑如图六所示：</p>
<p style="text-align: center;"><a href="http://chloerei.com/wp-content/uploads/2010/05/6-message-logic.jpg"><img class="aligncenter size-full wp-image-384" title="6 message-logic" src="http://chloerei.com/wp-content/uploads/2010/05/6-message-logic.jpg" alt="" width="523" height="333" /></a>图六 收发信息逻辑</p>
<p>qxmpp 库提供了良好的接口函数用于发送 Message 数据包。当收到服务端发来的 Message 数据包时，qxmpp 使用 Qt 的信号机制将数据包传递到 QTalk MainWindow 进行处理。</p>
<h4>6.4 传输文件</h4>
<p>已登录用户除了可以跟联系人发送纯文本信息外，还可以通过XMPP 的扩展协议 [XEP-0096] SI File Transfer 发送二进制文件 (如图片，PDF 文档)。</p>
<p>用户发送文件时，会首先发送文件传输的请求，联系人确认后开始进入传输，如果联系人拒绝，文件传输会中止。联系人向用户发送文件时也需要经过确认，否则不会开始传输。要求用户确认文件传输时，QTalk 会在系统托盘区和程序主界面显示请求提示。</p>
<p>qxmpp 库提供了 TransferManager 类用于管理文件传输。文件传输的逻辑如图七所示：</p>
<p style="text-align: center;"><a href="http://chloerei.com/wp-content/uploads/2010/05/7-transfer-logic.jpg"><img class="aligncenter size-full wp-image-388" title="7 transfer-logic" src="http://chloerei.com/wp-content/uploads/2010/05/7-transfer-logic.jpg" alt="" width="727" height="379" /></a>图七 文件传输逻辑</p>
<p>Qtalk 提供了统一的文件传输窗口，可以用于查看传输进度和终端传输进程。</p>
<h4>6.5 增删联系人</h4>
<p>用户的联系人列表存放在 GTalk 服务器上，无论从什么客户端登录都可以得到一致的联系人列表。已登录用户可以对联系人列表进行操作，增删联系人。</p>
<p>增删联系人有两个层面，一个是把对方 JID 放入自己的联系人列表，一个是向联系人发送订阅请求。</p>
<p>根据 RFC3920 的定义，订阅，即订阅联系人的 Presence 状态，这个状态包括用户的在线、离线、忙碌、活跃等。</p>
<p>订阅类型分为4种：</p>
<ul>
<li>none : 双方均没有进行订阅</li>
<li>to : 用户订阅了联系人的状态</li>
<li>from: 联系人订阅了用户的状态</li>
<li>both : 相当于 to + from</li>
</ul>
<p>实际操作中，发现订阅状态会影响 Gtalk 服务器是否转发 Message 信息。当状态为 &#8220;to&#8221; 时，只能接收对方的聊天信息；当状态为 &#8220;from“ 时，只能发送聊天信息。只有当状态为 both 时，双方才能正常发起聊天会话。</p>
<p>联系人的订阅操作如图八所示：</p>
<p style="text-align: center;"><a href="http://chloerei.com/wp-content/uploads/2010/05/8-subscribe-logic.jpg"><img class="aligncenter size-full wp-image-392" title="8 subscribe-logic" src="http://chloerei.com/wp-content/uploads/2010/05/8-subscribe-logic.jpg" alt="" width="717" height="398" /></a>图八 联系人订阅逻辑</p>
<p>接受请求时，在确认了对方的订阅请求后，qxmpp 库会自动发送订阅请求，以使订阅状态变为 &#8220;both&#8221;。</p>
<h4>6.6 其他人性化设置</h4>
<p>QTalk 实现了前述所有功能，并包括了一些方便用户使用的人性化功能，包括但不限于：</p>
<ul>
<li>联系人分组，更改联系人名称</li>
<li>联系人 VCard 信息读取，头像显示，头像大小调整</li>
<li>主窗口中隐藏离线联系人</li>
<li>用户状态切换</li>
<li>发送聊天信息的快捷键设置</li>
<li>系统托盘图标、未读信息列表</li>
</ul>
<p>QTalk 的运行截图在附录一。</p>
]]></content:encoded>
			<wfw:commentRss>http://chloerei.com/2010/05/15/367/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>XMPP 实践一 Qt 和 qxmpp 库</title>
		<link>http://chloerei.com/2010/05/14/351</link>
		<comments>http://chloerei.com/2010/05/14/351#comments</comments>
		<pubDate>Fri, 14 May 2010 08:40:32 +0000</pubDate>
		<dc:creator>Rei</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[Qt]]></category>
		<category><![CDATA[XMPP]]></category>
		<category><![CDATA[编程]]></category>

		<guid isPermaLink="false">http://chloerei.com/?p=351</guid>
		<description><![CDATA[5. 选择用于编写 XMPP 客户端的工具 为了深入了解 XMPP 是否足够用于开发一个开放、功能丰富的即时聊天工具，我决定写一个 XMPP 的客户端聊天程序。服务端直接连接 Google Talk 服务器。客户端编程基于 C++/Qt 和 qxmpp 库。最终程序将可以使用 Gtalk 帐号登录，并与其他 Gtalk 用户通信。 用于编写 XMPP 客户端的工具如下： Google Talk 服务器 Qt 编程框架和工具链 qxmpp 程序库 5.1 Google Talk (Gtalk) 服务器的优势 Google Talk 是 Google 公司于2005年8月24日推出的一款IP 电话及即时通讯的服务。Google Talk 使用开放的 XMPP 协议。得益于 XMPP 的开放性，使用 Google Talk 服务不一定要通过官方客户端。Google Talk用户端仅支持 Windows (2000、XP、Server 2003、7)。[13] 实际上从 [...]]]></description>
			<content:encoded><![CDATA[<h3>5. 选择用于编写 XMPP 客户端的工具</h3>
<p>为了深入了解 XMPP 是否足够用于开发一个开放、功能丰富的即时聊天工具，我决定写一个 XMPP 的客户端聊天程序。服务端直接连接 Google Talk 服务器。客户端编程基于 C++/Qt 和 qxmpp 库。最终程序将可以使用 Gtalk 帐号登录，并与其他 Gtalk 用户通信。</p>
<p>用于编写 XMPP 客户端的工具如下：</p>
<ul>
<li>Google Talk 服务器</li>
<li>Qt 编程框架和工具链</li>
<li>qxmpp 程序库</li>
</ul>
<h4>5.1 Google Talk (Gtalk) 服务器的优势</h4>
<p>Google Talk 是 Google 公司于2005年8月24日推出的一款IP 电话及即时通讯的服务。Google Talk 使用开放的 XMPP 协议。得益于 XMPP 的开放性，使用 Google Talk 服务不一定要通过官方客户端。Google Talk用户端仅支持 Windows (2000、XP、Server 2003、7)。[13]</p>
<p>实际上从 Gtalk 发布开始，XMPP 才真正走入大众的视野。以 Google 公司为后盾，Gtalk 的服务器拥有安全和稳定的口碑。</p>
<p>此次编程实践不涉及服务端的编程，而是遵循 XMPP 协议，让自己编写的客户端与 Gtalk 服务器通信，通过 Gtalk 服务器转发客户端的数据包。不涉及服务端编程，也可以使我专注与客户端的功能和操作性的优化上。</p>
<h4>5.2 Qt 编程框架简介</h4>
<p>Qt (发音同cute) 是一个跨平台的C++应用程序开发框架，有时又被称为 C++ 部件工具箱。Qt 被用在 KDE 桌面环境、Opera、OPIE、VoxOx、谷歌 Earth、Skype 和 VirtualBox的 开发中。它是诺基亚 (Nokia) 的 Qt Development Frameworks 部门的产品。[14]<span id="more-351"></span></p>
<p>Qt 的一个优势是跨平台，使用 Qt 写成的软件经过重新编译后可以运行在 Windows，Linux，Mac 平台上，实现了一次编写，随处编译。并且 Qt 拥有 LGPL 授权的版本，即可以用于编写开源软件，也可以编写闭源商业软件。</p>
<p>Qt 的本地语言是 C++，开发框架内包含了图形、网络、XML等多个方面的组件，使用 Qt 框架可以轻松编写跨平台的软件。</p>
<h4>5.3 qxmpp 库简介</h4>
<p>qxmpp 是一个基于 Qt 框架编写的 XMPP 通讯库。其开发者有 manjeetdahiya ，jeremy.laine，ian.geiser，项目源码[15]托管在 google code 上。</p>
<p>qxmpp 目前实现了 XMPP 标准中的核心部分 [RFC3920] [RFC3921]，以及 [XEP-0054] vcard-temp，[XEP-0096] SI File Transfer，必要时可以扩展功能。qxmpp 仅依赖 Qt 库，不需引入其他第3方的程序库，可使程序构建过程更清晰。</p>
<p>在实践初期，我曾尝试自己编写 XMPP 通讯部分的代码，但发现要遵循 XMPP 文档的定义重写一个通讯库是非常繁琐的事情。为了有效利用时间，着重研究 XMPP 协议在即时聊天上的应用效果，决定不重复发明轮子，选用了轻量级，并且依赖关系少的 qxmpp 库。</p>
<p>[13] http://zh.wikipedia.org/zh-cn/Google_Talk</p>
<p>[14] http://zh.wikipedia.org/zh-cn/Qt</p>
<p>[15] http://code.google.com/p/qxmpp/</p>
]]></content:encoded>
			<wfw:commentRss>http://chloerei.com/2010/05/14/351/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>XMPP 协议笔记 三 扩展</title>
		<link>http://chloerei.com/2010/05/12/305</link>
		<comments>http://chloerei.com/2010/05/12/305#comments</comments>
		<pubDate>Wed, 12 May 2010 07:56:05 +0000</pubDate>
		<dc:creator>Rei</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[XMPP]]></category>
		<category><![CDATA[编程]]></category>

		<guid isPermaLink="false">http://chloerei.com/?p=305</guid>
		<description><![CDATA[4. XMPP 扩展 XMPP 的核心部分是一个轻量级的协议，不足以满足一般即时聊天应用的需求。 XMPP 的社区在核心协议的基础上定义了众多的扩展协议，用以实现电子名片、二进制传输等功能。 4.1 通过 vcard-temp 获取电子名片 电子名片 (个人资料) 是聊天程序中常见的功能。通过电子名片，用户可以查看联系人除了 JID 地址外的其他信息，如：昵称，全名，个人网站 URL 等。 XMPP 的扩展之一 [XEP-0054] vcard-temp [9] 实现了电子名片。要在聊天程序中支持电子名片，需要客户端和服务端都实现 vcard-temp 扩展。知名的 XMPP 服务器大都支持 vcard-temp 扩展，如 jabber.org， gtalk 等。 4.1.1 请求电子名片 vcard-temp 扩展规定的电子名片访问规则如下： 客户端： &#60;iq from='stpeter@jabber.org/roundabout'    id='v3'    to='jer@jabber.org'    type='get'&#62;    &#60;vCard xmlns='vcard-temp'/&#62; &#60;/iq&#62; 该查询使用 get 类型的 Iq 包请求 [...]]]></description>
			<content:encoded><![CDATA[<h3>4. XMPP 扩展</h3>
<p>XMPP 的核心部分是一个轻量级的协议，不足以满足一般即时聊天应用的需求。 XMPP 的社区在核心协议的基础上定义了众多的扩展协议，用以实现电子名片、二进制传输等功能。</p>
<h4>4.1 通过 vcard-temp 获取电子名片</h4>
<p>电子名片 (个人资料) 是聊天程序中常见的功能。通过电子名片，用户可以查看联系人除了 JID 地址外的其他信息，如：昵称，全名，个人网站 URL 等。</p>
<p>XMPP 的扩展之一 [<a href="http://xmpp.org/extensions/xep-0054.html">XEP-0054</a>] vcard-temp [9] 实现了电子名片。要在聊天程序中支持电子名片，需要客户端和服务端都实现 vcard-temp 扩展。知名的 XMPP 服务器大都支持 vcard-temp 扩展，如 jabber.org， gtalk 等。</p>
<h5>4.1.1 请求电子名片</h5>
<p>vcard-temp 扩展规定的电子名片访问规则如下：</p>
<p>客户端：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;iq</span> <span style="color: #000066;">from</span>=<span style="color: #ff0000;">'stpeter@jabber.org/roundabout'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">id</span>=<span style="color: #ff0000;">'v3'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">to</span>=<span style="color: #ff0000;">'jer@jabber.org'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">type</span>=<span style="color: #ff0000;">'get'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;vCard</span> <span style="color: #000066;">xmlns</span>=<span style="color: #ff0000;">'vcard-temp'</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/iq<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>该查询使用 get 类型的 Iq 包请求 jabber.org 服务器返回 jer 用户的 vcard 数据。<span id="more-305"></span></p>
<p>接着 jabber.org 服务器返回 jer 用户的 vcard 数据：</p>
<p>服务器：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;iq</span> <span style="color: #000066;">from</span>=<span style="color: #ff0000;">'jer@jabber.org'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">to</span>=<span style="color: #ff0000;">'stpeter@jabber.org/roundabout'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">type</span>=<span style="color: #ff0000;">'result'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">id</span>=<span style="color: #ff0000;">'v3'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;vCard</span> <span style="color: #000066;">xmlns</span>=<span style="color: #ff0000;">'vcard-temp'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;FN<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>JeremieMiller<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/FN<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;N<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;GIVEN<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Jeremie<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/GIVEN<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;FAMILY<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Miller<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/FAMILY<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;MIDDLE</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/N<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;NICKNAME<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>jer<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/NICKNAME<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;EMAIL<span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #000000; font-weight: bold;">&lt;INTERNET</span><span style="color: #000000; font-weight: bold;">/&gt;</span><span style="color: #000000; font-weight: bold;">&lt;PREF</span><span style="color: #000000; font-weight: bold;">/&gt;</span><span style="color: #000000; font-weight: bold;">&lt;USERID<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>jeremie@jabber.org<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/USERID<span style="color: #000000; font-weight: bold;">&gt;</span></span><span style="color: #000000; font-weight: bold;">&lt;/EMAIL<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;JABBERID<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>jer@jabber.org<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/JABBERID<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/vCard<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/iq<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>vcard-temp 中定义了大量数据项，以上的示例只返回了用户已设置的部分数据项。</p>
<h5>4.1.2 隐私问题</h5>
<p>目前为止，vcard-temp 扩展被定义为无须经过授权访问，即非好友用户也可以获取任何一个 XMPP 用户的所有 vcard 信息。因此，vcard-temp 扩展建议用户慎重考虑是否要填入个人地址等隐私信息。</p>
<h4>4.2 通过 In-Band Bytestreams 传输二进制数据</h4>
<p>XMPP 核心部分没有提供二进制数据传输的支持，而 XMPP 扩展中有若干提供二进制传输的协议。应用较广的有 [<a href="http://xmpp.org/extensions/xep-0047.html">XEP-0047</a>] In-Band Bytestreams [10] 和 [<a href="http://xmpp.org/extensions/xep-0065.html">XEP-0065</a>] SOCKS5 Bytestreams [11]。由于实际上被广为应用二进制传输协议就只有这两种，所以客户端能做的选择不多。In-Band Bytestreams 和 SOCKS5 Bytestreams 可被整合为 [<a href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a>] SI File Transfer [12]，这个协议允许接收端选择传输方式。</p>
<p>此外还有 Google 提出的 Jingle 传输协议，Jingle 支持的网络连接方式更多，目前被应用于 Gtalk 客户端上，用于支持二进制文件传输与语音通信的实现，但并未普及。</p>
<h5>4.2.1 In-Band Bytestreams 原理</h5>
<p>所谓 In-Band ，即为 “在 XMPP 流内”的意思，其完整意义为：将二进制数据置入 XMPP 流内传输。但 XMPP 流的内容是纯文本，如何传输二进制数据呢？In-Band Bytestreams 的方案是把二进制数据使用 Base64 编码转换成可打印的纯文本，然后当作纯文本塞入 Iq 或者 Message 数据包中传输。</p>
<h5>4.2.2 In-Band Bytestreams 示例</h5>
<p>In-Band Bytestreams 的流程包括发起、接受/拒绝，分包传输，结束应答这几个过程，详细的流程示例参照 [XEP-0047] [10] 中的定义。</p>
<p>一个发送 In-Band Bytestreams 的 Iq 数据包示例如下：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;iq</span> <span style="color: #000066;">from</span>=<span style="color: #ff0000;">'romeo@montague.net/orchard'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">id</span>=<span style="color: #ff0000;">'kr91n475'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">to</span>=<span style="color: #ff0000;">'juliet@capulet.com/balcony'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">type</span>=<span style="color: #ff0000;">'set'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
  <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;data</span> <span style="color: #000066;">xmlns</span>=<span style="color: #ff0000;">'http://jabber.org/protocol/ibb'</span> <span style="color: #000066;">seq</span>=<span style="color: #ff0000;">'0'</span> <span style="color: #000066;">sid</span>=<span style="color: #ff0000;">'i781hf64'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
    qANQR1DBwU4DX7jmYZnncmUQB/9KuKBddzQH+tZ1ZywKK0yHKnq57kWq+RFtQdCJ
    WpdWpR0uQsuJe7+vh3NWn59/gTc5MDlX8dS9p0ovStmNcyLhxVgmqS8ZKhsblVeu
    IpQ0JgavABqibJolc3BKrVtVV1igKiX/N7Pi8RtY1K18toaMDhdEfhBRzO/XB0+P
    AQhYlRjNacGcslkhXqNjK5Va4tuOAPy2n1Q8UUrHbUd0g+xJ9Bm0G0LZXyvCWyKH
    kuNEHFQiLuCY6Iv0myq6iX6tjuHehZlFSh80b5BVV9tNLwNR5Eqz1klxMhoghJOA
  <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/data<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/iq<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>其中，data 段内包含的字符串是经过 Base64 编码的二进制数据。可见，该二进制数据经过编码后被当作一般纯文本来传输。data 段的 seq 属性标明了此段数据在所有数据分包中的编号。</p>
<h5>4.2.3 In-Band Bytestreams 缺陷</h5>
<p>In-Band Bytestreams 借助 XMPP 流进行二进制传输，优点是可以稳定交付，只要双方客户端仍然连接各自的服务器就能实现二进制传输。但其重大缺陷是速率过慢，每段二进制数据都要经过分包和多次封包解包，并且每个数据包都通过两个服务器的交付处理，会产生很大的延迟，效率很低。后文有对 In-Band Bytestreams 的简单速率测试。*未完成*</p>
<h4>4.3 通过 SOCKS5 Bytestreams 传输二进制数据</h4>
<p>4.3.1 SOCKS5 Bytestreams 原理</p>
<p>有别于 In-Band Bytestreams 流内传输，SOCKS5 Bytestreams 方案使用双方客户端的 socks5 直连传输，这样可以使传输速率达到双方主机直连的速度。</p>
<p>SOCKS5 Bytestreams 在传输过程的起效部分仅是让双方客户端决定传输方案，交换 IP 地址，端口地址等信息。当双方客户端决定可以使用的 IP 地址和端口地址后，就发起 socks5 连接，此时 XMPP 流不再参与数据传输，不增加额外的传输延迟。</p>
<p>整个 SOCKS5 Bytestreams (S5B) 流程如图三所示：</p>
<p style="text-align: center;"><a href="http://chloerei.com/wp-content/uploads/2010/05/3-socks5-stream.jpg"><img class=" size-full wp-image-322 aligncenter" title="3 socks5-stream" src="http://chloerei.com/wp-content/uploads/2010/05/3-socks5-stream.jpg" alt="" width="349" height="375" /></a>图三 XMPP 发起 SOCKS5 Bytestreams 流程</p>
<h5>4.3.2 SOCKS5 Bytestreams 示例</h5>
<p>一个发起 SOCKS5 Bytestreams 的 Iq 数据包示例如下：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;iq</span> <span style="color: #000066;">from</span>=<span style="color: #ff0000;">'requester@example.com/foo'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">id</span>=<span style="color: #ff0000;">'hu3vax16'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">to</span>=<span style="color: #ff0000;">'target@example.org/bar'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">type</span>=<span style="color: #ff0000;">'set'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
  <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;query</span> <span style="color: #000066;">xmlns</span>=<span style="color: #ff0000;">'http://jabber.org/protocol/bytestreams'</span></span>
<span style="color: #009900;">         <span style="color: #000066;">sid</span>=<span style="color: #ff0000;">'vxf9n471bn46'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;streamhost</span></span>
<span style="color: #009900;">        <span style="color: #000066;">jid</span>=<span style="color: #ff0000;">'requester@example.com/foo'</span></span>
<span style="color: #009900;">        <span style="color: #000066;">host</span>=<span style="color: #ff0000;">'192.168.4.1'</span></span>
<span style="color: #009900;">        <span style="color: #000066;">port</span>=<span style="color: #ff0000;">'5086'</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span>
  <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/query<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/iq<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>其中 streamhost 包含了发起人可用的 IP 地址，这个 IP 地址可以是主机地址，也可以是 socks5 代理主机地址 (用于突破防火墙和 NAT 网络的限制)。streamhost 项可以有多个，最终由接收方选择可用的地址，然后发起连接。</p>
<h5>4.3.4 SOCKS5 Bytestreams 局限</h5>
<p>由于互联网连接的复杂性，发起传输请求的 XMPP 客户端可能只拥有内网 IP ，并且没有可用的 socks5 代理服务器可用。这种情况下发起人将无法实现二进制文件的传输。</p>
<p>并且即使有代理服务器，由于经过中转，传输速率可能会大大降低。对此，除了假设更好的 socks5 代理服务器外，没有更好的方法。</p>
<h4>4.4 扩展机制的缺点</h4>
<p>XMPP 的扩展协议相当多，详细列表可以在 http://xmpp.org/extensions/ 找到。</p>
<p>但是通过扩展协议实现的功能，通常需要双方客户端均实现才能使用。有些还需要服务器做额外支持 (如 vcard-temp)，而有名的 XMPP 服务器通常只会选择性实现扩展协议，所以要在现实中实现所需的 XMPP 扩展，就需要搭建全套的服务端、客户端方案，开发成本大。并且造成各方 XMPP 服务器实现不一致，出现功能不同的 XMPP “部落”，只能使用核心协议中已定义的部分进行通信。</p>
<p>[9] http://xmpp.org/extensions/xep-0054.html</p>
<p>[10] http://xmpp.org/extensions/xep-0047.html</p>
<p>[11] http://xmpp.org/extensions/xep-0065.html</p>
<p>[12] http://xmpp.org/extensions/xep-0096.html</p>
]]></content:encoded>
			<wfw:commentRss>http://chloerei.com/2010/05/12/305/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>XMPP 协议笔记 二 XML数据包</title>
		<link>http://chloerei.com/2010/05/10/265</link>
		<comments>http://chloerei.com/2010/05/10/265#comments</comments>
		<pubDate>Mon, 10 May 2010 13:43:40 +0000</pubDate>
		<dc:creator>Rei</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[XMPP]]></category>
		<category><![CDATA[编程]]></category>

		<guid isPermaLink="false">http://chloerei.com/?p=265</guid>
		<description><![CDATA[3. XMPP 核心数据包 XMPP 的核心数据包类型有Precense，Message，Iq ，此外加上初始化 stream 用到的 Stream 数据包。这些数据包是 XMPP 信息传输的载体，被用于 XMPP 核心功能和扩展功能的实现。 该部分仅对 XMPP 中使用的数据包进行概览，用于感受基于 XML 的数据包与其他非 XML 数据包协议的差别，不能替代 IETF 关于 XMPP 协议的 RFC 文档 [3920] [3921][3][4]，以及 XMPP 的扩展协议文档 [extensions][5] 中描述。 3.1 公有属性 在 XML stream 中，每个数据包都是 XML 格式纯文本。而每个 XML 数据包有以下公有属性： to 数据包要发送的目的地址 from 数据包发送的源地址 id 数据包标示符 此三项属性在 XML stanza 中最为常见。 to 和 [...]]]></description>
			<content:encoded><![CDATA[<h3>3. XMPP 核心数据包</h3>
<p>XMPP 的核心数据包类型有Precense，Message，Iq ，此外加上初始化 stream 用到的 Stream 数据包。这些数据包是 XMPP 信息传输的载体，被用于 XMPP 核心功能和扩展功能的实现。</p>
<p>该部分仅对 XMPP 中使用的数据包进行概览，用于感受基于 XML 的数据包与其他非 XML 数据包协议的差别，不能替代 IETF 关于 XMPP 协议的 RFC 文档 [<a href="http://tools.ietf.org/html/rfc3920">3920</a>] [<a href="http://tools.ietf.org/html/rfc3921">3921</a>][3][4]，以及 XMPP 的扩展协议文档 [<a href="http://xmpp.org/extensions/">extensions</a>][5] 中描述。</p>
<h4>3.1 公有属性</h4>
<p>在 XML stream 中，每个数据包都是 XML 格式纯文本。而每个 XML 数据包有以下公有属性：</p>
<ul>
<li>to 数据包要发送的目的地址</li>
<li>from 数据包发送的源地址</li>
<li>id 数据包标示符</li>
</ul>
<p>此三项属性在 XML stanza 中最为常见。</p>
<p>to 和 from 属性用于服务器决定该数据包的路由规则。某些情况下，to 和 from 属性可以只有一个，例如：客户端向服务端发送设置配置的 Iq 包只含有 to (不向外路由)，客户端向联系人发送 Message 只含有 to (from 属性总是被改写为客户端的地址)。</p>
<p>id 用于节点间判断请求和应答数据包的对应状况，大多数情况可以不处理。<span id="more-265"></span></p>
<h4>3.2 初始化 XML stream，身份验证</h4>
<p>在客户端与服务器产生 TCP 连接后，需要与服务器初始化 XML stream，以及进行身份验证。</p>
<p>初始化时，客户端发送 stream 头部 XML：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;?xml</span> <span style="color: #000066;">version</span>=<span style="color: #ff0000;">'1.0'</span><span style="color: #000000; font-weight: bold;">?&gt;</span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;stream:stream</span></span>
<span style="color: #009900;">    <span style="color: #000066;">to</span>=<span style="color: #ff0000;">'example.com'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">xmlns</span>=<span style="color: #ff0000;">'jabber:client'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">xmlns:stream</span>=<span style="color: #ff0000;">'http://etherx.jabber.org/streams'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">version</span>=<span style="color: #ff0000;">'1.0'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span></pre></div></div>

<p>服务器在收到客户端的 stream 头后，回应一个 stream 头：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;?xml</span> <span style="color: #000066;">version</span>=<span style="color: #ff0000;">'1.0'</span><span style="color: #000000; font-weight: bold;">?&gt;</span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;stream:stream</span></span>
<span style="color: #009900;">    <span style="color: #000066;">from</span>=<span style="color: #ff0000;">'example.com'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">id</span>=<span style="color: #ff0000;">'someid'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">xmlns</span>=<span style="color: #ff0000;">'jabber:client'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">xmlns:stream</span>=<span style="color: #ff0000;">'http://etherx.jabber.org/streams'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">version</span>=<span style="color: #ff0000;">'1.0'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span></pre></div></div>

<p>接着服务器向客户端发送服务端支持的身份验证方式列表，常见的方式有基于安全传输 SASL 的 BASE64 编码账户密码验证。身份验证的种类多样，且过程较为繁琐，可以参考 《XMPP: The Definitive Guide》<a href="http://books.google.com/books?id=SG3jayrd41cC&amp;lpg=PP1&amp;pg=PA165#v=onepage&amp;q&amp;f=false">第12章</a>的介绍。</p>
<p>在对话结束时，客户端和服务端要先后发送 stream 尾部 XML，以使整个 XMP stream 闭合。(如果 TCP 异常中断，则服务端直接中断对话)</p>
<p>客户端：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/stream:stream<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>服务端：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/stream:stream<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<h4>3.3 Roster 获取联系人列表</h4>
<p>在即时聊天 (IM) 应用中，客户端登录服务器后做的第一个操作通常是获取联系人列表。获取联系人列表需要发送 get 类型的 Iq 数据包。(Iq数据包将会在3.6节解释)</p>
<p>客户端：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;iq</span> <span style="color: #000066;">from</span>=<span style="color: #ff0000;">'juliet@example.com/balcony'</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">'get'</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">'roster_1'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;query</span> <span style="color: #000066;">xmlns</span>=<span style="color: #ff0000;">'jabber:iq:roster'</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/iq<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>该请求的意义为：名为 juliet 的用户 (登录资源为 balcony) 向 example.com 服务器请求获得 (get) roster 表。</p>
<p>服务器收到请求后，返回 roster 表。</p>
<p>服务端：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;iq</span> <span style="color: #000066;">to</span>=<span style="color: #ff0000;">'juliet@example.com/balcony'</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">'result'</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">'roster_1'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;query</span> <span style="color: #000066;">xmlns</span>=<span style="color: #ff0000;">'jabber:iq:roster'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;item</span> <span style="color: #000066;">jid</span>=<span style="color: #ff0000;">'romeo@example.net'</span></span>
<span style="color: #009900;">            <span style="color: #000066;">name</span>=<span style="color: #ff0000;">'Romeo'</span></span>
<span style="color: #009900;">            <span style="color: #000066;">subscription</span>=<span style="color: #ff0000;">'both'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;group<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Friends<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/group<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/item<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;item</span> <span style="color: #000066;">jid</span>=<span style="color: #ff0000;">'mercutio@example.org'</span></span>
<span style="color: #009900;">            <span style="color: #000066;">name</span>=<span style="color: #ff0000;">'Mercutio'</span></span>
<span style="color: #009900;">            <span style="color: #000066;">subscription</span>=<span style="color: #ff0000;">'from'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;group<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Friends<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/group<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/item<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;item</span> <span style="color: #000066;">jid</span>=<span style="color: #ff0000;">'benvolio@example.org'</span></span>
<span style="color: #009900;">            <span style="color: #000066;">name</span>=<span style="color: #ff0000;">'Benvolio'</span></span>
<span style="color: #009900;">            <span style="color: #000066;">subscription</span>=<span style="color: #ff0000;">'both'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;group<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Friends<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/group<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/item<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/query<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/iq<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>可以看到，juliet 的 roster 表内有3个联系人，分别名为 Romeo，Mercutio，Benvolio，都属于 Friends 分组。Roster 列表中的 JID 信息将会用在稍候客户端发送信息包的目的地址中。</p>
<p>Item 中的 subscription 关系到联系人状态信息的传输，有 none，both，from，to 四种。详细的 subscription 操作在 RFC 3921 <a href="http://tools.ietf.org/html/rfc3921#page-26">Managing Subscriptions</a> 章节[7]中定义。</p>
<h4>3.4 Presence 状态数据包</h4>
<p>Presence 数据包被设计用来发送轻量级的节点状态信息，例如 IM 中用来发送用户的在线、离开、忙碌、离线等简单的状态消息。</p>
<p>一个 presence 数据包范例如下：</p>
<p>客户端：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;presence<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;show<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>away<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/show<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/presence<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>该 presence 向服务端表明，客户端用户处于离线状态。服务端收到 presence 后，会自行填充 from 和 to 属性，发送到订阅 (subscribeb) 了该用户状态信息的联系人服务器上。</p>
<p>Presence 数据包也可以在发送时填上 to 属性，用于指定 presence 的接收方。例如：</p>
<p>客户端：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;presence</span> <span style="color: #000066;">to</span>=<span style="color: #ff0000;">&quot;romeo@example.net&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;show<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>away<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/show<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/presence<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>该 Presence 只会被转发至 romeo@example.net。</p>
<h4>3.5 Message 信息数据包</h4>
<p>Message 是即时聊天应用中最常用的数据包，其功能是发送用户聊天信息。一个 Message 例子如下：</p>
<p>客户端：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;message</span></span>
<span style="color: #009900;">    <span style="color: #000066;">to</span>=<span style="color: #ff0000;">'romeo@example.net'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">from</span>=<span style="color: #ff0000;">'juliet@example.com/balcony'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">type</span>=<span style="color: #ff0000;">'chat'</span></span>
<span style="color: #009900;">    <span style="color: #000066;">xml:lang</span>=<span style="color: #ff0000;">'en'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;body<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Wherefore art thou, Romeo?<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/body<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/message<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>该 message 包将会被服务器转发至 example.net 服务器，随后转交给 romeo 已登录的客户端上(如果该用户没有登录，message 信息会储存在服务端直至用户上线)。</p>
<p>其中，body 标签中包含用户要传输的聊天信息。</p>
<p>要传输格式化的富文本信息，可以通过支持扩展 <a href="http://xmpp.org/extensions/xep-0071.html">XEP-0071</a>[8]，引入 html 标签。</p>
<p>3.6 Iq 数据包</p>
<p>Iq 是一个为其他操作提供 get，result，set，error 动作的数据包，本身并没有限定用途的范围。核心协议中就有大量需要用到 Iq 数据包的操作，例如添加 roster 联系人，需要用到 set 类型的 Iq 数据包：</p>
<p>客户端：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;iq</span> <span style="color: #000066;">from</span>=<span style="color: #ff0000;">'juliet@example.com/balcony'</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">'set'</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">'roster_2'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;query</span> <span style="color: #000066;">xmlns</span>=<span style="color: #ff0000;">'jabber:iq:roster'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;item</span> <span style="color: #000066;">jid</span>=<span style="color: #ff0000;">'nurse@example.com'</span></span>
<span style="color: #009900;">        <span style="color: #000066;">name</span>=<span style="color: #ff0000;">'Nurse'</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;group<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>Servants<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/group<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/item<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/query<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/iq<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

<p>服务器用 result 类型的 Iq 数据包返回操作结果：</p>
<p>服务端：</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;iq</span> <span style="color: #000066;">to</span>=<span style="color: #ff0000;">'juliet@example.com/balcony'</span> <span style="color: #000066;">type</span>=<span style="color: #ff0000;">'result'</span> <span style="color: #000066;">id</span>=<span style="color: #ff0000;">'roster_2'</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span></pre></div></div>

<p>XMPP 扩展协议大都通过扩展 Iq 数据包的元素来添加操作。Iq 数据包的 type 属性就像 HTTP 协议中的 Method 项一样，提供增删改查 (CURD) 动作，具体实现何种操作并不限制。</p>
<p>[3] http://tools.ietf.org/html/rfc3920</p>
<p>[4] http://tools.ietf.org/html/rfc3921</p>
<p>[5] http://xmpp.org/extensions/</p>
<p>[6] http://books.google.com/books?id=SG3jayrd41cC&amp;lpg=PP1&amp;pg=PA165#v=onepage&amp;q&amp;f=false</p>
<p>[7] http://tools.ietf.org/html/rfc3921#page-26</p>
<p>[8] http://xmpp.org/extensions/xep-0071.html</p>
]]></content:encoded>
			<wfw:commentRss>http://chloerei.com/2010/05/10/265/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>XMPP 协议笔记 一 基础篇</title>
		<link>http://chloerei.com/2010/05/09/172</link>
		<comments>http://chloerei.com/2010/05/09/172#comments</comments>
		<pubDate>Sun, 09 May 2010 12:35:02 +0000</pubDate>
		<dc:creator>Rei</dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[XMPP]]></category>
		<category><![CDATA[编程]]></category>

		<guid isPermaLink="false">http://chloerei.com/?p=172</guid>
		<description><![CDATA[1. XMPP 优缺点 XMPP (Extensible Messaging and Presence Protocol) (前称Jabber) 是一种以 XML 为基础的开放式即时通讯协议，是经由互联网工程工作小组 (IETF) 通过的互联网标准。[1] 1.1 XMPP 协议的优点 1.1.1 可扩展性 XMPP 的数据传输基于 XML 格式，可扩展性强。XMPP 的核心协议栈 (Core Stack) 部分只定义了基础的 Presence，Message，Iq 等最主要数据格式和传输逻辑，更多的功能则通过定义扩展 (Extensions) 实现。 1.1.2 受 IETF 组织规范 Internet Engineering Task Force  (IETF) 在2002年开始规范 XMPP 协议，使其协议的修订和扩展的添加都经过严格的流程审核，防止 XMPP 协议因缺乏标准而分裂。并且这也保证了 XMPP 协议是完全开放的。 1.1.3 应用广泛 XMPP 协议的应用比其他开放即时通讯协议更为广泛。较有名的使用 XMPP 协议的聊天服务有 Google [...]]]></description>
			<content:encoded><![CDATA[<h3>1. XMPP 优缺点</h3>
<p>XMPP (Extensible Messaging and Presence Protocol) (前称Jabber) 是一种以 XML 为基础的开放式即时通讯协议，是经由互联网工程工作小组 (IETF) 通过的互联网标准。[1]</p>
<h4>1.1 XMPP 协议的优点</h4>
<h5>1.1.1 可扩展性</h5>
<p>XMPP 的数据传输基于 XML 格式，可扩展性强。XMPP 的核心协议栈 (Core Stack) 部分只定义了基础的 Presence，Message，Iq 等最主要数据格式和传输逻辑，更多的功能则通过定义扩展 (Extensions) 实现。</p>
<h5>1.1.2 受 IETF 组织规范</h5>
<p>Internet Engineering Task Force  (IETF) 在2002年开始规范 XMPP 协议，使其协议的修订和扩展的添加都经过严格的流程审核，防止 XMPP 协议因缺乏标准而分裂。并且这也保证了 XMPP 协议是完全开放的。</p>
<h5>1.1.3 应用广泛</h5>
<p>XMPP 协议的应用比其他开放即时通讯协议更为广泛。较有名的使用 XMPP 协议的聊天服务有 Google Gtalk 和 Facebook Chat 等。此外，XMPP 在各平台下都有若干服务端、客户端和程序库的实现，二次开发时成本较低。</p>
<p>XMPP 协议的可扩展性和开放性是该协议被广泛应用的保证。<span id="more-172"></span></p>
<h4>1.2 XMPP 协议的缺点</h4>
<h5>1.2.1 不内置支持二进制数据的传输</h5>
<p>XMPP 的核心部分没有包含对二进制数据传输的支持，这使得 XMPP 的基本数据限定在文本文件范围内。XMPP 社区认为，XMPP 应该用于传输 meta 信息，辅助其他应用进行协议握手，XMPP 本身不应负担海量信息的传输。</p>
<p>从当前流行的轻量化观点来看，XMPP 把二进制数据传输的协议移入核心栈，是符合了最小核心的需求。但同时却为实际应用中 XMPP 客户端传输二进制数据增加了开发扩展协议的负担。</p>
<h5>1.2.2 缺乏旗舰应用</h5>
<p>XMPP 是开放的，任何个人和组织都可以使用 XMPP。但同时产生的副作用是每个组织使用 XMPP 的目的不同，侧重点不同，导致XMPP 所开发的应用实际上导致了各个厂商各自为政，比如 Cisco 将 XMPP 用于设备通信，游戏厂商用于游戏内的简易聊天。即时通讯中只有 Google Gtalk 和 Fackbook Chat 较出名，但都没有作为这两家企业的核心产品作为推广。 XMPP 的应用中并没有旗舰应用。</p>
<p>XMPP 的缺点归根结底是因为其已经成为开放标准，制定和修改要顾及多方的利益。其核心栈只能包括各种应用的交集部分。各厂商对 XMPP 的利用多会建立一套新的扩展协议以扩展功能，如 Google 用于文件和语音流传输的 Jingle 协议和完全和其他 XMPP 应用不流通的 Google Wave。</p>
<p>但总的来说，XMPP 的问题是一个开放即时通讯协议不可避免遇到的问题。研究 XMPP 协议的实用效果，对将来开放更好的开放协议有重要的参考意义。</p>
<h3>2. XMPP 基础</h3>
<h4>2.1 网络层次和数据包</h4>
<p>XMPP 使用 TCP 连接，并支持安全传输 (TSL/SASL)。</p>
<p>XMPP 的层次结构如下</p>
<p>XMPP<br />
SASL<br />
TSL<br />
TCP</p>
<p>XMPP 层中传输的数据包采用 XML 格式，称为 XML stanzas，XMPP 节点间 XML stanzas 的传输构成的数据流称为 XML stream。</p>
<p>一个 XML stream 的概览[2]如下</p>
<p style="text-align: center;"><a href="http://chloerei.com/wp-content/uploads/2010/05/xmpp-stream.jpg"><img class="size-full wp-image-245 aligncenter" title="xmpp-stream" src="http://chloerei.com/wp-content/uploads/2010/05/xmpp-stream.jpg" alt="" width="211" height="355" /></a>图一 XML Stream</p>
<p>XMPP 核心栈中，XML stanzas 包括 Presence、Message 和 Iq stanzas。Presence 用于传输节点状态，Message 用于传输信息内容，而 Iq 用于传输更复杂的应答。实际应用中，Presence 多限定用于简单的状态传输，而扩展协议多通过扩展 Iq stanzas 元素实现。</p>
<h4>2.2 XMPP 的节点与路由</h4>
<p>XMPP 中的节点大致有两种，一种是服务器，一种是客户端(暂不讨论各种代理)。客户端需要连接服务器，服务器为客户端提供数据包 (XML stanzas) 的路由和转发。</p>
<p style="text-align: center;"><a href="http://chloerei.com/wp-content/uploads/2010/05/2-xmpp-node.png"><img class="size-full wp-image-254 aligncenter" title="2 xmpp-node" src="http://chloerei.com/wp-content/uploads/2010/05/2-xmpp-node.png" alt="" width="500" height="188" /></a>图二 XMPP Server-Client 节点</p>
<p>客户端与客户端的通信需要通过服务器中转。每个到达服务器的数据包，由服务器分析后发往别的服务器或客户端。与 Email 的通信机制不同的是，XMPP 服务器之间不设立中转点，而是直接连接，以提高即时性和安全性。</p>
<h4>2.3 地址标识</h4>
<p>每个客户端需要拥有一个地址标识用于定位，XMPP 中称之为 JID (Jabber ID)。地址标识的格式如下</p>
<p style="text-align: center;"><code>[ node "@" ] domain [ "/" resource ]</code></p>
<p>例如</p>
<p style="text-align: center;"><code>username@gmail.com/pidgin</code></p>
<p>格式与 Email 地址格式类似，但增添了 resource 项，用于支持同一账号的多客户端登录。上述例子可以解释为：在 gmail.com 服务器注册的 username 用户，且使用 pidgin 客户端登录。当一个 JID 不包含 resource 部分时，该 JID 一般称为 BareJID。</p>
<p>用户地址标识的认证由提供 XMPP 服务的服务器执行。例如，注册于 gmail 服务器的账号由 gmail 服务器进行验证。其他服务器发往 gmail.com 域名的数据包均通过域名查询与服务间验证后发往 gmail 服务器，而不用考虑 gmail 服务器与下属账号间的通信。</p>
<p>[1] http://zh.wikipedia.org/zh-cn/XMPP</p>
<p>[2] http://tools.ietf.org/html/rfc3920#page-9</p>
]]></content:encoded>
			<wfw:commentRss>http://chloerei.com/2010/05/09/172/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
