成都心情

            BlogJava :: 首頁 ::  :: 聯系 :: 聚合  :: 管理 ::
            98 隨筆 :: 2 文章 :: 501 評論 :: 1 Trackbacks

          公告

          Creative Commons License
          本作品采用知識共享署名-相同方式共享 2.5 中國大陸許可協議進行許可。 Locations of visitors to this page(15)

          隨筆分類(91)

          隨筆檔案(99)

          文章分類(2)

          友情鏈接

          積分與排名

          • 積分 - 634728
          • 排名 - 74

          最新評論

          閱讀排行榜

          評論排行榜

          2008 年的夏天,偶然在網上閑逛的時候發現了 Comet 技術,人云亦云間,姑且認為它是由 Dojo Alex Russell 2006 年提出。在閱讀了大量的資料后,萌發出寫篇 blog 來說明什么是 Comet 的想法。哪知道這個想法到了半年后的今天才提筆,除了繁忙的工作拖延外,還有 Comet 本身帶來的困惑。

          Comet 能帶來生產力的提升是有目共睹的。現在假設有 1000 個用戶在使用某軟件,輪詢 (polling) Comet 的設定都是 1s 10s 100s 的潛伏期,那么在相同的潛伏期內, Comet 所需要的帶寬更小,如下圖:

          不僅僅是在帶寬上的優勢,每個用戶所真正感受到的響應時間(潛伏期)更短,給人的感覺也就更加的實時,如下圖:

          再引用一篇 IBMDW 上的譯文《使用 Jetty Direct Web Remoting 編寫可擴展的 Comet 應用程序》,其中說到:吸引人們使用 Comet 策略的其中一個優點是其顯而易見的高效性。客戶機不會像使用輪詢方法那樣生成煩人的通信量,并且事件發生后可立即發布給客戶機。

          上面一遍一遍的說到 Comet 技術的優勢,那么我們可以替換現有的技術結構了?不幸的是,近半年的擦邊球式的關注使我對 Comet 的理解越發的糊涂,甚至有人說 Comet 這個名詞已被濫用。去年的一篇博文, The definition of Comet? 使 Comet 更加撲朔迷離,甚至在維基百科上大家也對準確的 Comet 定義產生爭論。還是等牛人們爭論清楚再修改維基百科吧,在這里我想還是引用維基百科對 Comet 的定義:服務器推模式 (HTTP server push streaming) 以及長輪詢 (long polling) ,這兩種模式都是 Comet 的實現。

          除了對 Comet 的準確定義尚缺乏有效的定論外, Comet 還存在不少技術難題,隨著 Tomcat 6 Jetty 6 的發布,他們基于 NIO 各自實現了異步 Servlet 機制。有興趣的看官可以分別實現這兩個容器的 Comet ,至少我還沒玩轉。

          在編寫服務器端的代碼上面,我很困惑, http://tomcat.apache.org/tomcat-6.0-doc/aio.html 這里演示了如何在 Tomcat 6 中實現異步 Servlet ;我們再把目光換到 Jetty 6 上,還是前面提到的那篇 IBMDW 譯文,如果你和我一樣無聊,可以下載那邊文章的 sample 代碼。我驚奇的發現每個廠商對異步 Servlet 的封裝是不同的,一個傻傻的問題:我的 Comet 服務器端的代碼可移植么?至今我還在問這個問題!好吧,業界有規范么?有當然有,不過看起來有些爭論會發生——那就是 Servlet 3.0 規范 (JSR-315) Servlet 3.0 正在公開預覽,它明確的支持了異步 Servlet Servlet 3.0 公開預覽版引發爭論》,又讓我高興不起來了:“來自 RedHat Bill Burke 寫的一篇博文,其中他批評了 Jetty 6 中的異步 servlet 實現 ......Greg Wilkins 宣布他致力于 Servlet 3.0 異步 servlet 的一個實現 ...... 雖然還需要更多測試,但是這個代碼已經實現了基本的異步行為,不需要很復雜的重新分發請求或者前遞方法。我相信這代表了 3.0 的合理折中方案。在我們從 3.0 的簡單子集里獲得經驗之后,如果需要更多的特性,可以添加到 3.1 ........” 。牛人們還在做最佳范例,口水仗也還要繼續打,看來要嘗到 Comet 的甜頭是很困難的。 STOP !我已經不想再分析如何寫客戶端的代碼了,什么 dojo extJs DWR ZK....... 都有自己的實現。我認為這一切都要等 Servelt 3.0 正式發布以后,如何編寫客戶端代碼才能明朗點。

          現在拋開繞來繞去的爭執吧,既然 Ajax+Servlet 實現 Comet 很困難,何不換個思維呢。我這里倒是有個小小的 sample ,說明如何在 Adobe BlazeDS 中實現長輪詢模式。關于 BlazeDS ,可以在這里找到些信息。為了說明什么是長輪詢,首先來看看什么是輪詢,既在一定間隔期內由 web 客戶端發起請求到服務器端取回數據,如下圖所示:

          ?????????????????????????

          至于輪詢的缺點,在前面的論述中已有覆蓋,至于優點大家可以 google 一把,我覺得最大的優點就是技術上很好實現,下面是個 Ajax 輪詢的例子,這是一個簡單的聊天室,首先是 chat.html 代碼,想必這些代碼網上一抓就一大把,支持至少 IE6 IE7 FF3 瀏覽器,讓人煩心的是亂碼問題,在傳遞到 Servlet 之前要 encodeURI 一下


          <! DOCTYPE?html?PUBLIC?"-//W3C//DTD?HTML?4.01?Transitional//EN"?"http://www.w3.org/TR/html4/loose.dtd" >
          <!--
          ????chat?page
          ????author?rosen?jiang
          ????since?2008/07/29
          -->
          < html >
          ??
          < head >
          ???
          < meta? http-equiv ="content-type" ?content ="text/html;?charset=utf-8" >
          ????
          < script? type ="text/javascript" >
          ????
          // servlets?url
          ???? var ?url? = ? " http://127.0.0.1:8080/ajaxTest/Ajax " ;
          ????
          // bs?version
          ???? var ?version? = ?navigator.appName + " ? " + navigator.appVersion;
          ????
          // if?is?IE
          ???? var ?isIE? = ? false ;

          ????
          if (version.indexOf( " MSIE?6 " ) > 0 ? || ?version.indexOf( " MSIE?7 " ) > 0 ){
          ????????isIE?
          = ? true ;
          ????}

          ????
          // Httprequest?object
          ???? var ?Httprequest? = ? function ()?{}
          ????
          // creatHttprequest?function?of?Httprequest
          ????Httprequest.prototype.creatHttprequest = function (){
          ????????
          var ?request? = ? false ;
          ????????
          // init?XMLHTTP?or?XMLHttpRequest
          ???????? if ?(isIE)?{
          ????????????
          try ?{
          ????????????????request?
          = ? new ?ActiveXObject( " Msxml2.XMLHTTP " );
          ????????????}?
          catch ?(e)?{
          ????????????????
          try ?{
          ????????????????????request?
          = ? new ?ActiveXObject( " Microsoft.XMLHTTP " );
          ????????????????}?
          catch ?(e)?{}
          ????????????}
          ????????}
          else ?{? // Mozilla?bs?etc.
          ????????????request? = ? new ?XMLHttpRequest();
          ????????}
          ????????
          if ?( ! request)?{
          ????????????
          return ? false ;
          ????????}
          ????????
          return ?request;
          ????}
          ????
          // sendMsg?function?of?Httprequest
          ????Httprequest.prototype.sendMsg = function (msg){
          ????????
          var ?http_request? = ???? this .creatHttprequest();
          ????????
          var ?reslult? = ? "" ;
          ????????
          var ?methed? = ? false ;
          ????????
          if ?(http_request)?{????
          ????????????
          if ?(isIE)?{????????????????
          ????????????????http_request.onreadystatechange?
          =
          ????????????????????????
          function ?(){ // callBack?function
          ???????????????????????????? if ?(http_request.readyState? == ? 4 )?{
          ????????????????????????????????
          if ?(http_request.status? == ? 200 )?{
          ????????????????????????????????????reslult?
          = ?http_request.responseText;
          ????????????????????????????????}?
          else ?{
          ????????????????????????????????????alert(
          " 您所請求的頁面有異常。 " );
          ????????????????????????????????}
          ????????????????????????????}
          ????????????????????????};
          ????????????}?
          else ?{
          ????????????????http_request.onload?
          = ?
          ????????????????????????
          function ?(){ // ?callBack?function?of?Mozilla?bs?etc.
          ???????????????????????????? if ?(http_request.readyState? == ? 4 )?{
          ????????????????????????????????
          if ?(http_request.status? == ? 200 )?{
          ????????????????????????????????????reslult?
          = ?http_request.responseText;
          ????????????????????????????????}?
          else ?{
          ????????????????????????????????????alert(
          " 您所請求的頁面有異常。 " );
          ????????????????????????????????}
          ????????????????????????????}
          ????????????????????????};
          ????????????}
          ????????????
          // send?msg
          ???????????? if (msg != null ? && ?msg != "" ){
          ????????????????request_url?
          = ?url + " ? " + Math.random() + " &msg= " + msg;
          ????????????????
          // encodeing?utf-8?Character
          ????????????????request_url? = ?encodeURI(request_url);
          ????????????????http_request.open(
          " GET " ,?request_url,? false );
          ????????????}
          else {
          ????????????????http_request.open(
          " GET " ,?url + " ? " + Math.random(),? false );
          ????????????}
          ????????????http_request.setRequestHeader(
          " Content-type " , " charset=utf-8; " );
          ????????????http_request.send(
          null );
          ????????}
          ????????
          return ?reslult;????
          ????}
          </ script >
          </ head >
          < body >
          ??
          < div >
          ??????
          < input? type ="text" ?id ="sendMsg" ></ input >
          ??????
          < input? type ="button" ?value ="發送消息" ?onclick ="send()" />
          ??????
          < br />< br />
          ??????
          < div? style ="width:470px;overflow:auto;height:413px;border-style:solid;border-width:1px;font-size:12pt;" >
          ?????????????
          < div? id ="msg_content" ></ div >
          ??????????
          < div? id ="msg_end" ?style ="height:0px;?overflow:hidden" > ? </ div >
          ??????
          </ div >
          ??
          </ div >
          </ body >
          < script? type ="text/javascript" >
          ????
          var ?data_comp? = ? "" ;
          ????
          // send?button?click
          ???? function ?send(){
          ????????
          var ?sendMsg? = ?document.getElementById( " sendMsg " );
          ????????
          var ?hq? = ? new ?Httprequest();
          ????????hq.sendMsg(sendMsg.value);
          ????????sendMsg.value
          = "" ;
          ????}
          ????
          // processing?wnen?message?recevied
          ???? function ?writeData(){
          ????????
          var ?msg_content? = ?document.getElementById( " msg_content " );
          ????????
          var ?msg_end? = ?document.getElementById( " msg_end " );
          ????????
          var ?hq? = ? new ?Httprequest();
          ????????
          var ?value? = ?hq.sendMsg();
          ????????
          if (data_comp? != ?value){
          ????????????data_comp?
          = ?value;
          ????????????msg_content.innerHTML?
          = ?value;
          ????????????msg_end.scrollIntoView();
          ????????}
          ????????setTimeout(
          " writeData() " ,? 1000 );
          ????}
          ????
          // init?load?writeData?
          ????onload? = ?writeData;
          </ script >
          </ html >

          接下來是 Servlet ,如果你是用的 Tomcat ,在這里注意下編碼問題,否則又是亂碼,另外我使用 LinkedList 實現了一個隊列,該隊列的最大長度是 30 ,也就是最多能保存 30 條聊天信息,舊的將被丟棄,另外新的客戶端進來后能讀取到最近的信息:

          package ?org.rosenjiang.ajax;

          import ?java.io.IOException;
          import ?java.io.PrintWriter;
          import ?java.text.SimpleDateFormat;
          import ?java.util.Date;
          import ?java.util.LinkedList;

          import ?javax.servlet.ServletException;
          import ?javax.servlet.http.HttpServlet;
          import ?javax.servlet.http.HttpServletRequest;
          import ?javax.servlet.http.HttpServletResponse;

          /**
          ?*?
          ?*?
          @author ?rosen?jiang
          ?*?
          @since ?2009/02/06
          ?*?
          ?
          */
          public ? class ?Ajax? extends ?HttpServlet?{
          ????
          private ? static ? final ? long ?serialVersionUID? = ? 1L ;
          ????
          // ?the?length?of?queue
          ???? private ? static ? final ? int ?QUEUE_LENGTH? = ? 30 ;
          ????
          // ?queue?body
          ???? private ? static ?LinkedList < String > ?queue? = ? new ?LinkedList < String > ();
          ????
          ????
          /**
          ?????*?response?chat?content
          ?????*?
          ?????*?
          @param ?request
          ?????*?
          @param ?response
          ?????*?
          @throws ?ServletException
          ?????*?
          @throws ?IOException
          ?????
          */
          ????
          public ? void ?doGet(HttpServletRequest?request,?HttpServletResponse?response)
          ????????????
          throws ?ServletException,?IOException?{
          ????????
          // parse?msg?content
          ????????String?msg? = ?request.getParameter( " msg " );
          ????????SimpleDateFormat?sdf?
          = ? new ?SimpleDateFormat( " yyyy-MM-dd?HH:mm:ss " );
          ????????
          // push?to?the?queue
          ???????? if ?(msg? != ? null ? && ? ! msg.equals( "" ))?{
          ????????????
          byte []?b? = ?msg.getBytes( " ISO_8859_1 " );
          ????????????msg?
          = ?sdf.format( new ?Date())? + " ?? " + new ?String(b,? " utf-8 " ) + " <br> " ;
          ????????????
          if (queue.size()? == ?QUEUE_LENGTH){
          ????????????????queue.removeFirst();
          ????????????}
          ????????????queue.addLast(msg);
          ????????}
          ????????
          // response?client
          ????????response.setContentType( " text/html " );
          ????????response.setCharacterEncoding(
          " utf-8 " );
          ????????PrintWriter?out?
          = ?response.getWriter();
          ????????msg?
          = ? "" ;
          ????????
          // loop?queue
          ???????? for ( int ?i = 0 ;?i < queue.size();?i ++ ){
          ????????????msg?
          = ?queue.get(i);
          ????????????out.println(msg
          == null ? ? ? "" ?:?msg);
          ????????}
          ????????out.flush();
          ????????out.close();
          ????}

          ????
          /**
          ?????*?The?doPost?method?of?the?servlet.
          ?????*
          ?????*?
          @param ?request
          ?????*?
          @param ?response
          ?????*?
          @throws ?ServletException
          ?????*?
          @throws ?IOException
          ?????
          */
          ????
          public ? void ?doPost(HttpServletRequest?request,?HttpServletResponse?response)
          ????????????
          throws ?ServletException,?IOException?{
          ????????
          this .doGet(request,?response);
          ????}
          }

          打開瀏覽器,實驗下效果,將就用吧,稍微有些延遲。還是看看長輪詢吧,長輪詢有三個顯著的特征:

          1. 服務器端會阻塞請求直到有數據傳遞或超時才返回。

          2. 客戶端響應處理函數會在處理完服務器返回的信息后,再次發出請求,重新建立連接。

          3. 當客戶端處理接收的數據、重新建立連接時,服務器端可能有新的數據到達;這些信息會被服務器端保存直到客戶端重新建立連接,客戶端會一次把當前服務器端所有的信息取回。

          下圖很好的說明了以上特征:

          ?????????????????????????????

          既然關注的是 BlazeDS 如何實現長輪詢,那么有必要稍微了解下。 BlazeDS 包含了兩個重要的服務,進行遠端方法調用的 RPC service 和傳遞異步消息的 Messaging Service ,我們即將探討的長輪詢屬于 Messaging Service Messaging Service 使用 producer consumer 模式來分別定義消息的發送者 (producer) 和消費者 (consumer) ,具體到 Flex 代碼,有 Producer Consumer 兩個組件對應。在廣闊的互聯網上有很多 BlazeDS 入門的中文教材,我就不再廢話了。假設你已經裝好 BlazeDS ,打開 WEB-INF/flex/services-config.xml 文件,在 channels 節點內加一個 channel 聲明長輪詢頻道,關于 channel endpoint 請參閱 About channels and endpoints 章節:


          ???????? < channel-definition? id ="long-polling-amf" ?class ="mx.messaging.channels.AMFChannel" >
          ????????????
          < endpoint? url ="http://{server.name}:{server.port}/{context.root}/messagebroker/longamfpolling" ?class ="flex.messaging.endpoints.AMFEndpoint" />
          ????????????
          < properties >
          ????????????????
          < polling-enabled > true </ polling-enabled >
          ????????????????
          < wait-interval-millis > 60000 </ wait-interval-millis >
          ????????????????
          < polling-interval-millis > 0 </ polling-interval-millis >
          ????????????????
          < max-waiting-poll-requests > 150 </ max-waiting-poll-requests >
          ????????????
          </ properties >
          ????
          </ channel-definition >

          如何實現長輪詢的玄機就在上面的 properties 節點內, polling-enabled = true ,打開輪詢模式; wait-interval-millis = 6000 服務器端的潛伏期,也就是服務器會保持與客戶端的連接,直到超時或有新消息返回(恩,看來這就是長輪詢了); polling-interval-millis = 0 表示客戶端請求服務器端的間隔期, 0 表示沒有任何的延遲; max-waiting-poll-requests = 150 表示服務器能承受的最大長連接用戶數,超過這個限制,新的客戶端就會轉變為普通的輪詢方式(至于這個數值最大能有多大,這和你的 web 服務器設置有關了,而 web 服務器的最大連接數就和操作系統有關了,這方面的話題不在本文內探討)。

          其實這樣設置之后,長輪詢的代碼已經實現了一半了。恩,不錯!看起來比異步 Servlet 實現起來簡單多了。不過要實現和之前 Ajax 輪詢一樣的效果,還得實現自己的 ServiceAdapter ,這就是 Adapter 的用處:


          package ?org.rosenjiang.flex;

          import ?java.text.SimpleDateFormat;
          import ?java.util.Date;
          import ?java.util.LinkedList;

          import ?flex.messaging.io.amf.ASObject;
          import ?flex.messaging.messages.Message;
          import ?flex.messaging.services.MessageService;
          import ?flex.messaging.services.ServiceAdapter;

          /**
          ?*?
          ?*?
          @author ?rosen?jiang
          ?*?
          @since ?2009/02/06
          ?*?
          ?
          */
          public ? class ?MyMessageAdapter? extends ?ServiceAdapter?{

          ????
          // ?the?length?of?queue
          ???? private ? static ? final ? int ?QUEUE_LENGTH? = ? 30 ;
          ????
          // ?queue?body
          ???? private ? static ?LinkedList < String > ?queue? = ? new ?LinkedList < String > ();

          ????
          /**
          ?????*?invoke?method
          ?????*?
          ?????*?
          @param ?message?Message
          ?????*?
          @return ?Object
          ?????
          */
          ????
          public ?Object?invoke(Message?message)?{
          ????????SimpleDateFormat?sdf?
          = ? new ?SimpleDateFormat( " yyyy-MM-dd?HH:mm:ss " );
          ????????MessageService?msgService?
          = ?(MessageService)?getDestination()
          ????????????.getService();
          ????????
          // message?Object
          ????????ASObject?ao? = ?(ASObject)?message.getBody();
          ????????
          // chat?message
          ????????String?msg? = ?(String)?ao.get( " chatMessage " );
          ????????
          if ?(msg? != ? null ? && ? ! msg.equals( "" ))?{
          ????????????msg?
          = ?sdf.format( new ?Date())? + ? " ?? " ? + ?msg? + ? " \r " ;
          ????????????
          if (queue.size()? == ?QUEUE_LENGTH){
          ????????????????queue.removeFirst();
          ????????????}
          ????????????queue.addLast(msg);
          ????????}
          ????????msg?
          = ? "" ;
          ????????
          // loop?queue
          ???????? for ( int ?i = 0 ;?i < queue.size();?i ++ ){
          ????????????String?chatData?
          = ?queue.get(i);
          ????????????
          if ?(chatData? != ? null )?{
          ????????????????msg?
          += ?chatData;
          ????????????}
          ????????}
          ????????ao.put(
          " chatMessage " ,?msg);
          ????????message.setBody(ao);
          ????????msgService.pushMessageToClients(message,?
          false );
          ????????
          return ? null ;
          ????}
          }

          接下來注冊該 Adapter ,打開 WEB-INF/flex/messaging-config.xml 文件,在 adapters 節點內加入一個 adapter-definition 來聲明自定義 Adapter


          < adapter-definition? id ="myad" ?class ="org.rosenjiang.flex.MyMessageAdapter" />

          接著定義一個 destination ,以便 Flex 客戶端能訂閱聊天室,組裝好之前定義的長輪詢頻道和 adapter


          ???? < destination? id ="chat" >
          ????????
          < channels >
          ????????????
          < channel? ref ="long-polling-amf" />
          ????????
          </ channels >
          ????????
          < adapter? ref ="myad" />
          ????
          </ destination >

          服務器端就算搞定了,接著搞定 Flex 那邊的代碼吧,灰常灰常的簡單。先到 Building your client-side application 學習如何創建和 BlazeDS 通訊的 Flex 項目。然后在 chat.mxml 中寫下:


          <? xml?version="1.0"?encoding="utf-8" ?>
          < mx:Application? xmlns:mx ="http://www.adobe.com/2006/mxml" ?creationComplete ="consumer.subscribe();send()" >
          ????
          ????
          < mx:Script >
          ????????
          <![CDATA[
          ????????
          ????????????import?mx.messaging.messages.AsyncMessage;
          ????????????import?mx.messaging.messages.IMessage;
          ????????????
          ????????????private?function?send():void
          ????????????{
          ????????????????var?message:IMessage?=?new?AsyncMessage();
          ????????????????message.body.chatMessage?=?msg.text;
          ????????????????producer.send(message);
          ????????????????msg.text?=?"";
          ????????????}
          ????????????????????????
          ????????????private?function?messageHandler(message:IMessage):void
          ????????????{
          ????????????????log.text?=?message.body.chatMessage?+?"\n";
          ????????????}
          ????????????
          ????????
          ]]>
          ????
          </ mx:Script >
          ????
          ????
          < mx:Producer? id ="producer" ?destination ="chat" />
          ????
          < mx:Consumer? id ="consumer" ?destination ="chat" ?message ="messageHandler(event.message)" />
          ????
          ????
          < mx:Panel? title ="Chat" ?width ="100%" ?height ="100%" >
          ????????
          < mx:TextArea? id ="log" ?width ="100%" ?height ="100%" />
          ????????
          < mx:ControlBar >
          ?????????????
          < mx:TextInput? id ="msg" ?width ="100%" ?enter ="send()" />
          ?????????????
          < mx:Button? label ="Send" ?click ="send()" /> ?
          ????????
          </ mx:ControlBar >
          ????
          </ mx:Panel >
          ????
          </ mx:Application >

          之前我們說到的 Producer Consumer 組件在這里出現了,由于我們要訂閱的是同一個聊天室,所以 destination="chat" ,而 Consumer 組件則注冊回調函數 messageHandler() ,處理異步消息的到來。當打開這個聊天客戶端的時候,在 creationComplete 初始化完成后,立即進行 consumer.subscribe() ,其實接下來應該就能直接收到服務器端回饋的聊天記錄了,但是我沒仔細學習如何監聽客戶端的訂閱,所以在這里我直接 send() 了一個空消息以便服務器端能回饋已有的聊天記錄,接下來我就不用再講解了,都能看懂。

          現在打開瀏覽器,感受下長輪詢的效果吧。不過遇到個問題,如果 FF 同時開兩個聊天窗口,第二個打開的會有延遲感, IE 也是,按照牛人們的說法,當一個瀏覽器開兩個以上長連接的時候才會有延遲感,不解。 BlazeDS 的長輪詢也不是十全十美,有人說它不是真正的“實時” The Truth About BlazeDS and Push Messaging ,隨即引發出口水仗,里面提到的 RTMP 協議在 2009 1 月已開源,相信以后 BlazeDS 會更“實時”;接著又有人說 BlazeDS 不是非阻塞式的,這個問題后來也沒人來對應。罷了,畢竟BlazeDS才開源不久,容忍一下吧。最后,我想說的是,不論 BlazeDS 到底有什么問題,至少實現起來是輕松的,在 Servlet 3.0 沒發布之前,是個不錯的選擇。


          請注意!引用、轉貼本文應注明原作者:Rosen Jiang 以及出處: http://www.aygfsteel.com/rosen

          posted on 2009-02-11 21:34 Rosen 閱讀(15645) 評論(8)  編輯  收藏 所屬分類: 模式與策略

          評論

          # re: Ajax輪詢以及Comet模式—寫在Servlet 3.0發布之前 2009-02-12 09:19 HiMagic!
          看了半天,也沒看出新鮮東西。本來挺簡單的東西被冠上一個個花里胡哨的帽子。  回復  更多評論
            

          # re: Ajax輪詢以及Comet模式—寫在Servlet 3.0發布之前 2009-02-12 09:56 rmn190
          謝謝樓主分享.

          很欣賞樓主的技術觸覺與文采!  回復  更多評論
            

          # re: Ajax輪詢以及Comet模式—寫在Servlet 3.0發布之前 2009-02-12 09:59 Rosen
          @HiMagic!
          沒什么新鮮可言,技術一直在發展中,Comet早有很多實現,google上一大把。我還有繼續寫這些范例的必要么?花里胡哨的帽子并不是我強加上去的,比如Ajax這個名詞04年才初見端倪,但技術很早很早就有,不知道你怎么評價,或者很鄙視這種換湯不換藥的做法。  回復  更多評論
            

          # re: Ajax輪詢以及Comet模式—寫在Servlet 3.0發布之前 2009-07-30 14:30 jorke
          ajax 實現comet,冒似服務器端開發難度不小,socket的會話要自己做特殊處理,http協議。。。。。。  回復  更多評論
            

          # re: Ajax輪詢以及Comet模式—寫在Servlet 3.0發布之前 2010-02-27 21:48 凌宏立
          寫的不錯,學習了  回復  更多評論
            

          # re: Ajax輪詢以及Comet模式—寫在Servlet 3.0發布之前 2010-09-28 18:07 lurenjia
          樓主出現過comet導致內存報錯的問題么?  回復  更多評論
            

          # re: Ajax輪詢以及Comet模式—寫在Servlet 3.0發布之前 2010-09-28 20:15 Rosen
          @lurenjia
          之前倒是在DWR中遇到過,后來DWR自己修復了。請關注首頁《使用Memory Analyzer tool(MAT)分析內存泄漏》系列文章,相信你的內存泄漏問題能找到原因。  回復  更多評論
            

          # re: Ajax輪詢以及Comet模式—寫在Servlet 3.0發布之前 2012-10-30 17:07 soup
          感謝樓主,正好需要這個東西!  回復  更多評論
            

          主站蜘蛛池模板: 宝坻区| 元氏县| 孟连| 中卫市| 南开区| 来宾市| 新丰县| 卢龙县| 张家界市| 巴林左旗| 肃北| 淳安县| 志丹县| 曲阳县| 淄博市| 贵定县| 雅江县| 东乡族自治县| 缙云县| 高唐县| 鄱阳县| 会昌县| 措美县| 铜梁县| 开化县| 姜堰市| 库伦旗| 康马县| 石首市| 灵川县| 汤阴县| 乌审旗| 万州区| 开鲁县| 宁乡县| 巴塘县| 石河子市| 新干县| 松潘县| 黄陵县| 澳门|