<?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>JerryQu的小站 &#187; WebIM</title>
	<atom:link href="http://www.qgy18.com/tag/webim/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.qgy18.com</link>
	<description></description>
	<lastBuildDate>Tue, 16 Mar 2010 03:42:13 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>WebIM开发之多页面数据同步</title>
		<link>http://www.qgy18.com/2009/08/webim_data_sync/</link>
		<comments>http://www.qgy18.com/2009/08/webim_data_sync/#comments</comments>
		<pubDate>Tue, 18 Aug 2009 08:01:05 +0000</pubDate>
		<dc:creator>Jerry Qu</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[WebIM]]></category>

		<guid isPermaLink="false">http://www.qgy18.com/?p=137</guid>
		<description><![CDATA[呃，好久没更新博客了，一来最近确实比较忙，二来人也变懒了，这样很不好～要改变！ 今天要讨论的话题是多页面数据同步。顾名思义，就是更新A页面的内容，B页面也要及时反映出改变，当然A、B页面至少要是在同域下才有意义。放在WebIM这个应用场景中来，就是如果有多个页面开着webim，其中任何一个页面上发送或接收到新消息，要在其他所有的页面中同步显示出来。 使... ]]></description>
			<content:encoded><![CDATA[<p>呃，好久没更新博客了，一来最近确实比较忙，二来人也变懒了，这样很不好～要改变！</p>
<p>今天要讨论的话题是多页面数据同步。顾名思义，就是更新A页面的内容，B页面也要及时反映出改变，当然A、B页面至少要是在同域下才有意义。放在WebIM这个应用场景中来，就是如果有多个页面开着webim，其中任何一个页面上发送或接收到新消息，要在其他所有的页面中同步显示出来。</p>
<p>使用短连接的WebIM要轮询服务器来获取最新的消息，如果多个页面一起轮询，那么对服务器的消耗还是很大的，使用页面数据同步能减少消耗：无论同时存在多少个聊天页面，同时仅有一个主页面A负责与服务器轮询；在主页面A轮询到服务器消息后，分发给其他页面B、C、D；如果A被关掉了，检测程序会马上检测到，并从剩下的页面中挑选新的页面充当主页面负责通讯；客户端向服务端提交数据不必通过主页面，直接向服务器提交即可，只是提交的数据也需要分发给其他页面，便于同步UI。</p>
<p>多页面数据同步的实现细化起来，大概就是下面这个流程：</p>
<p>1.有一套本地化存储方案，要求能本域下任何页面都可以读写数据，至少有set和get两个方法；</p>
<p>2.页面加载时实例化sync对象，可以指定页面代号(name)方便进行页面分组，系统自动给每个页面分类一个唯一标识符(clientId)，并将这个页面client的相关信息加入clients列表，放进本地存储；</p>
<p>3.sync提供sendMsg方法，可根据clientID发给单一页面，或根据name发给一组页面，传递的数据(data)是一个JSON，sendMsg方法不关心data的实际结构。将消息的发送者clientID、接收者clientID以及data拼成一个消息JSON，并将多条JSON组成一个消息数组，序列化后放本地存储；</p>
<p>4.sync通过定时检查并解析本地存储获知是否有新消息，在收到发给自己(根据clientID来判断)的消息后，将这条消息置为已读，并调用onNewMsg方法，将data作为参数传递给它；</p>
<p>5.页面unload的时候，需要调用remove方法将自己从clients列表中去除。</p>
<p>另外，每个页面都必须有一个定时器，用来更新clients列表，检查新消息以及删掉已读消息。</p>
<p>新版本的webim应该会采用这种方案来实现多页面聊天，具体代码我也没写完，不过从下面的demo已经可以看到基本思想：打开多个页面，在文本框写些文字，点击同步按钮，其它页面会同步更新文本框。</p>
<div class="hl-surround"><div class="hl-main"><span style="color: Green;">var</span><span style="color: Gray;"> </span><span style="color: Blue;">sync</span><span style="color: Gray;"> = </span><span style="color: Green;">new</span><span style="color: Gray;"> </span><span style="color: Blue;">MsgSync</span><span style="color: Olive;">(</span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">test2</span><span style="color: #8b0000;">&quot;</span><span style="color: Olive;">)</span><span style="color: Gray;">; </span><span style="color: #ffa500;">//test2是页面名称(name)</span><span style="color: Gray;"><br /></span><span style="color: Blue;">sync</span><span style="color: Gray;">.</span><span style="color: Blue;">onNewMsg</span><span style="color: Gray;"> = </span><span style="color: Green;">function</span><span style="color: Olive;">(</span><span style="color: Blue;">e</span><span style="color: Olive;">){</span><span style="color: Gray;">    </span><span style="color: #ffa500;">//处理其它页面发过来的消息</span><span style="color: Gray;"><br />&nbsp; $</span><span style="color: Olive;">(</span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">txt</span><span style="color: #8b0000;">&quot;</span><span style="color: Olive;">)</span><span style="color: Gray;">.</span><span style="color: Blue;">value</span><span style="color: Gray;"> = </span><span style="color: Blue;">e</span><span style="color: Gray;">.</span><span style="color: Blue;">message</span><span style="color: Gray;">;<br /></span><span style="color: Olive;">}</span><span style="color: Gray;">;<br />$</span><span style="color: Olive;">(</span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">send</span><span style="color: #8b0000;">&quot;</span><span style="color: Olive;">)</span><span style="color: Gray;">.</span><span style="color: Blue;">onclick</span><span style="color: Gray;"> = </span><span style="color: Green;">function</span><span style="color: Olive;">(){</span><span style="color: Gray;"> </span><span style="color: #ffa500;">//向页面名称同为test2的其它页面发送消息</span><span style="color: Gray;"><br />&nbsp; </span><span style="color: Blue;">sync</span><span style="color: Gray;">.</span><span style="color: Blue;">sendMsg</span><span style="color: Olive;">({</span><span style="color: Gray;"><br />&nbsp;&nbsp; &nbsp;</span><span style="color: Blue;">name</span><span style="color: Gray;">:</span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">test2</span><span style="color: #8b0000;">&quot;</span><span style="color: Gray;">,<br />&nbsp;&nbsp; &nbsp;</span><span style="color: Blue;">data</span><span style="color: Gray;">:</span><span style="color: Olive;">{</span><span style="color: Gray;"><br />&nbsp;&nbsp; &nbsp; &nbsp;</span><span style="color: Blue;">message</span><span style="color: Gray;">:$</span><span style="color: Olive;">(</span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">txt</span><span style="color: #8b0000;">&quot;</span><span style="color: Olive;">)</span><span style="color: Gray;">.</span><span style="color: Blue;">value</span><span style="color: Gray;"><br />&nbsp;&nbsp; &nbsp;</span><span style="color: Olive;">}</span><span style="color: Gray;"><br />&nbsp; </span><span style="color: Olive;">})</span><span style="color: Gray;">;<br />&nbsp; </span><span style="color: Green;">return</span><span style="color: Gray;"> </span><span style="color: Green;">false</span><span style="color: Gray;">;<br /></span><span style="color: Olive;">}</span><span style="color: Gray;">;</span></div></div>
<p><a href="http://www.qgy18.com/lab/sync/">点这里打开Demo</a>。</p>
<p>该方案的让人头疼的是用什么做本地存储：userData需要页面完全一致才能使用；globalStorage只有firefox和ie8才支持；flash、google gear需要装插件，database storage是html5里的内容也没几家支持。这样看来似乎只有session cookie比较靠谱，但众所周知每次与服务器的交互都会带上cookie，cookie本身的容量也很有限。但是，相比较每个页面都轮询服务器而言，就算用cookie存也还是值得的。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.qgy18.com/2009/08/webim_data_sync/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>WebIM开发之通讯模式介绍</title>
		<link>http://www.qgy18.com/2008/08/webim-design-transport/</link>
		<comments>http://www.qgy18.com/2008/08/webim-design-transport/#comments</comments>
		<pubDate>Tue, 26 Aug 2008 16:25:47 +0000</pubDate>
		<dc:creator>Jerry Qu</dc:creator>
				<category><![CDATA[前端开发]]></category>
		<category><![CDATA[Server Push]]></category>
		<category><![CDATA[WebIM]]></category>

		<guid isPermaLink="false">http://www.qgy18.com/?p=41</guid>
		<description><![CDATA[我打算写一个系列文章，介绍webim的方方面面，今天开始第一篇。 我之前发布了一个webim，那个im设计了前端UI、交互，后端程序和通讯只是随便写了一下。作为一个交互很多的web应用，良好的后端设计可以减少数据库访问、减轻服务器负载；良好的通讯模式更是可以较少服务器连接数、节省流量。这篇文章主要讲web即时通讯中常用的技术。 HTTP是无连接的，HTTP通讯过程基本就... ]]></description>
			<content:encoded><![CDATA[<p>我打算写一个系列文章，介绍webim的方方面面，今天开始第一篇。</p>
<p>我之前<a href="http://www.qgy18.com/2008/08/qgywebim_php_edition/">发布了一个webim</a>，那个im设计了前端UI、交互，后端程序和通讯只是随便写了一下。作为一个交互很多的web应用，良好的后端设计可以减少数据库访问、减轻服务器负载；良好的通讯模式更是可以较少服务器连接数、节省流量。这篇文章主要讲web即时通讯中常用的技术。</p>
<p>HTTP是无连接的，HTTP通讯过程基本就是：客户端发送请求给服务器，服务器接收请求给出响应信息，客户端接收响应信息显示在用户的显示器上，客户端断开连接。由此可知，要实现即时聊天中的&#8221;即时&#8221;，我们有两个办法：服务器抓住连接不断开和客户端不断的向服务器发起请求实现伪即时。当然用Flash XMLSocket可以实现真正的即时通讯，但这样已经不是使用HTTP协议了，HTTP天生的优势（无需另外开端口、自动穿越防火墙）也就无法体现。<span id="more-41"></span></p>
<p>1.短轮询(polling)：核心思想是客户端定时去服务器取消息。为了实现即时效果，轮询的间隔必须设计得足够短，另外为了操作的流畅，需要使用Ajax来发送请求。本人的QGYWebIM就是采用的此方案。这种方案的优点是：后端程序编写比较容易，发送完响应信息马上断开连接，不会占用太多服务器资源。缺点是一般情况下，频繁的请求中有大半是无用，这些冗余请求无形中浪费了带宽和服务器资源。我们可以通过判断用户的活跃程度来决策请求服务器的间隔，我在<a href="http://bbs.51js.com/thread-76104-1-1.html">51的一个帖子</a>提到过这种方法，但是间隔一旦长了，消息的传送就有延时，违背了即时聊天的初衷了。</p>
<p>2.长轮询(long-polling)：基本原理是客户端向服务器发送请求，服务器接到请求后hold住连接，直到有新消息才返回响应信息并关闭连接，连接被断开期间用户的新信息会被服务器缓存起来。客户端处理完响应信息后再向服务器发送新的请求。这种做法的优势是如果用户一直没新消息，客户端不会频繁的轮询去服务器取消息，节省了流量，但是服务器维持长连接是很消耗资源的。具体实现起来，前端这边基本不需要什么改动，依然是用Ajax轮询取信息，后端需要在没有新消息时处理一下。</p>
<p>3.长连接(streaming)：其实很早以前就有人使用这种技术来实现聊天室的通讯。以前在页面中嵌入一个iframe，iframe里放一个使用长连接页面，服务器有新消息就会及时的在iframe里反映出来，再依靠客户端的脚本解析出来就OK了。这样做一个比较严重的问题是：使用iframe请求长连接时，无论是IE还是firefox都会认为页面没有加载完而显示进度条，很难看。不过这个问题是可以解决的。firefox支持了<a href="http://ajaxpatterns.org/HTTP_Streaming">Streaming Ajax</a>，在readyState为3的时候就能接受数据，所以问题不大；IE则只能在readyState为4，即连接断开时才能得到返回值。但是伟大的Google工程师使用了一个hack成功的解决了这个问题：使用一个<a href="http://alex.dojotoolkit.org/2006/02/what-else-is-burried-down-in-the-depths-of-googles-amazing-javascript/">被称为“htmlfile”的ActiveX</a>，把iframe放在这个ActiveX里就OK了。</p>
<div class="hl-surround"><div class="hl-main"><span style="color: #ffa500;">// we were served from child.example.com but</span><span style="color: Gray;"><br /></span><span style="color: #ffa500;">// have already set document.domain to example.com</span><span style="color: Gray;"><br /></span><span style="color: Green;">var</span><span style="color: Gray;"> </span><span style="color: Blue;">currentDomain</span><span style="color: Gray;"> = </span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">http://exmaple.com/</span><span style="color: #8b0000;">&quot;</span><span style="color: Gray;">;<br /></span><span style="color: Green;">var</span><span style="color: Gray;"> </span><span style="color: Blue;">dataStreamUrl</span><span style="color: Gray;"> = </span><span style="color: Blue;">currentDomain</span><span style="color: Gray;">+</span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">path/to/server.cgi</span><span style="color: #8b0000;">&quot;</span><span style="color: Gray;">;<br /></span><span style="color: Green;">var</span><span style="color: Gray;"> </span><span style="color: Blue;">transferDoc</span><span style="color: Gray;"> = </span><span style="color: Green;">new</span><span style="color: Gray;"> </span><span style="color: Blue;">ActiveXObject</span><span style="color: Olive;">(</span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">htmlfile</span><span style="color: #8b0000;">&quot;</span><span style="color: Olive;">)</span><span style="color: Gray;">; </span><span style="color: #ffa500;">// !?!</span><span style="color: Gray;"><br /></span><span style="color: #ffa500;">// make sure it's really scriptable</span><span style="color: Gray;"><br /></span><span style="color: Blue;">transferDoc</span><span style="color: Gray;">.</span><span style="color: Blue;">open</span><span style="color: Olive;">()</span><span style="color: Gray;">;<br /></span><span style="color: Blue;">transferDoc</span><span style="color: Gray;">.</span><span style="color: Blue;">write</span><span style="color: Olive;">(</span><span style="color: #8b0000;">&quot;&quot;</span><span style="color: Olive;">)</span><span style="color: Gray;">;<br /></span><span style="color: Blue;">transferDoc</span><span style="color: Gray;">.</span><span style="color: Blue;">write</span><span style="color: Olive;">(</span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">&lt;script type=</span><span style="color: #8b0000;">&quot;</span><span style="color: Blue;">text</span><span style="color: Gray;">/</span><span style="color: Blue;">javascript</span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">&gt;&lt;!--<br />document.domain='</span><span style="color: #8b0000;">&quot;</span><span style="color: Gray;">+</span><span style="color: Blue;">currentDomain</span><span style="color: Gray;">+</span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">';<br />// --&gt;&lt;/script&gt;</span><span style="color: #8b0000;">&quot;</span><span style="color: Olive;">)</span><span style="color: Gray;">;<br /></span><span style="color: Blue;">transferDoc</span><span style="color: Gray;">.</span><span style="color: Blue;">write</span><span style="color: Olive;">(</span><span style="color: #8b0000;">&quot;&quot;</span><span style="color: Olive;">)</span><span style="color: Gray;">;<br /></span><span style="color: Blue;">transferDoc</span><span style="color: Gray;">.</span><span style="color: Blue;">close</span><span style="color: Olive;">()</span><span style="color: Gray;">;<br /></span><span style="color: #ffa500;">// set the iframe up to call the server for data</span><span style="color: Gray;"><br /></span><span style="color: Green;">var</span><span style="color: Gray;"> </span><span style="color: Blue;">ifrDiv</span><span style="color: Gray;"> = </span><span style="color: Blue;">transferDoc</span><span style="color: Gray;">.</span><span style="color: Blue;">createElement</span><span style="color: Olive;">(</span><span style="color: #8b0000;">&quot;</span><span style="color: Red;">div</span><span style="color: #8b0000;">&quot;</span><span style="color: Olive;">)</span><span style="color: Gray;">;<br /></span><span style="color: Blue;">transferDoc</span><span style="color: Gray;">.</span><span style="color: Blue;">appendChild</span><span style="color: Olive;">(</span><span style="color: Blue;">ifrDiv</span><span style="color: Olive;">)</span><span style="color: Gray;">;<br /></span><span style="color: #ffa500;">// start communicating</span><span style="color: Gray;"><br /></span><span style="color: Blue;">ifrDiv</span><span style="color: Gray;">.</span><span style="color: Blue;">innerHTML</span><span style="color: Gray;"> = </span><span style="color: #8b0000;">&quot;&quot;</span><span style="color: Gray;">;</span></div></div>
<p>无疑，使用长连接对于用户来说是最好的方案，用户体验最好（消息能及时的到达）、占用用户带宽最少（不会发送无用的请求），但是会增加服务器的开销；长轮询是折中方案，<a href="http://www.facebook.com">Facebook </a>IM就是采用这种方案，不过做了一点改动：客户端发起的每个连接服务器都hold10S，这10S中新消息会源源不断的返回给客户端，10s后连接关闭，客户端发起下一个连接。这样做是因为Facebook的用户会不断的打开、关闭新页面，如果每个页面都建立一个永久的长连接，会阻塞浏览器其他请求，服务器也会吃不消的；短轮询因为实现起来简单，适用于小型应用。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.qgy18.com/2008/08/webim-design-transport/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
