(轉)用java實現comet,基于HTTP長連接的實現,用于從服務端實時發送信息到客戶端

netloser 14年前發布 | 5K 次閱讀 Forth SourceForge 云計算 JStock WP7

 comet是HTTP長連接,就是在HTTP發送請求時,服務器不立刻發送響應信息給客戶端, 而是保持著連接,等待一定情況發生后才把數據發送回去給客戶端。所以用comet可以實現服務器端的數據實時地發送給客戶端。

        本文主要是用java和js來簡單地實現comet,最后附上源碼和使用例子。

        在客戶端用XMLRequest發送請求到服務器,在服務器端用一個servlet來接收XMLRequest的請求,當接收到請求時,并不立刻響應客戶端,而是把該servlet線程阻塞,等到一定事件發生后,再響應客戶端。當客戶端接收到服務端的響應后,調用自定義的回調函數來處理服務器發送回來的數據,處理完成后,再發送一個XMLRequest請求到服務端,這樣循環下去,就可以實現數據的實時更新,又不必要在客戶端不斷地輪循(polling)。

         利用該comet的實現(以后簡稱為keeper)時,只要在客戶端注冊事件和寫一個處理返回數據的回調函數,然后在服務端實現keeper中的EventListener接口,調用Controller.action(eventListener,eventType)就可以了。

        keeper分成兩大部分,第一部分為客戶端的javascript,第二部分是服務端的servlet和事件處理。

        一.客戶端

        建立一個XMLRequest對象池,每發送一次請求,從對象池中取一個XMLRequest對象,如果沒有可用的對象,則創建一個,把它加入到對象池中。這部分的代碼來自于網絡。

        為了使用方便,再添加一些方法,用來注冊事件。這樣只要調用注冊函數來注冊事件,并且把回調函數傳給注冊事件函數就行了,處理數據的事情,交給回調函數,并由用戶來實現。

        keeper為了方便使用,把客戶端的javascript代碼集成在servlet中,當配置好keeper的servlet,啟動HTTP服務器時,keeper會根據用戶的配置,在相應的目錄下生成客戶端的javascript代碼。

    二.服務端

        服務端的servlet初始化時,根據配置來生成相應的客戶端javascript代碼。

        servlet的入口由keeper.servlet.Keeper.java中的doGet進入。在Keeper的doGet中,從請求中獲取用戶注冊事件的名稱(字符串類型),然后根據事件的名稱,構造一個事件(Event類型),再把它注冊到NameRegister中,注冊完成后,該servlet線程調用wait(),把自已停止。等待該servlet線程被喚醒后,從Event中調用事件的EventListener接口的process(request,response)來處理客戶端的請求。

        

  1.     protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2.         String eventName = request.getParameter("event");
  3.         NameRegister reg = NameRegister.getInstance();
  4.         Event event = null;
  5.         try {
  6.             event = reg.getEvent(eventName);
  7.             if(event == null) {
  8.                 event = new Event(eventName,this);
  9.                 reg.registeEvent(eventName, event);
  10.             }
  11.             if(event.getServlet() == null) {
  12.                 event.setServlet(this);
  13.             }
  14.             
  15.         } catch (RegistException e1) {
  16.             e1.printStackTrace();
  17.         }
  18.         synchronized(this) {
  19.             while(!event.isProcess()) {
  20.                 try {
  21.                     wait();
  22.                 } catch (InterruptedException e) {
  23.                     e.printStackTrace();
  24.                 }
  25.             }
  26.         }
  27.         EventListener listener = event.getListener();
  28.         if(listener != null) {
  29.            listener.process(request,response);
  30.         }   
  31.     }

        在服務端處理事件時,調用了keeper.control.Controller中的靜態方法action(EventListener listener,String eventName)來處理。如下所示。

 

 

  1.     public static boolean action(EventListener listener,String eventName){
  2.         NameRegister reg = NameRegister.getInstance();
  3.         HttpServlet servlet = null;
  4.         Event e = null;
  5.         try {
  6.             e = reg.getEvent(eventName,true);
  7.             if(e == null) {
  8.                 return false;
  9.             }
  10.             e.setListener(listener);
  11.             servlet = e.getServlet();
  12.             e.setProcess(true);
  13.             synchronized(servlet) {
  14.                 servlet.notifyAll();
  15.             }
  16.         } catch (RegistException ex) {
  17.             ex.printStackTrace();
  18.             }
  19.         if(servlet != null && e != null) {
  20.             e = null;
  21.             return true;
  22.         } else {
  23.             return false;
  24.         }
  25.     }

下面開始用keeper來寫一個簡單的網頁聊天程序和基于服務端的時間。

 

 

    1.客戶端設置

        注冊兩個事件,一個用于是時間事件,一個是消息事件。同時還要寫兩個回調函數,用于處理服務端返回的時間和聊天消息。如下所于:

  1. <script type="text/javascript">
  2.     Keeper.addListener('timer',showTime);//注冊時間事件
  3.     function showTime(obj){ //時間處理回調函數
  4.         var sp = document.getElementById("dateTime");
  5.         if(sp){
  6.             sp.innerHTML = obj.responseText;
  7.         }
  8.     }
  9.     function startOrStop(obj){
  10.         var btn = document.getElementById("controlBtn")
  11.         btn.value=obj.responseText;
  12.     }
  13.     Keeper.addListener('msg',showMsg,"GBK");//注冊消息事件,最后一個參數是
  14. //字符串編碼
  15.     function showMsg(obj){//處理消息的回調函數
  16.         var msg = document.getElementById("msg");
  17.         if(msg){
  18.             
  19.                 msg.value = obj.responseText+"\n"+msg.value;
  20.             
  21.         }
  22.     }
  23.     function sendMsg() {
  24.         var msg = document.getElementById("sendMsg");
  25.         if(msg){
  26.             var d = "msg="+msg.value;
  27.             sendReq('POST','./demo',d,startOrStop);
  28.             msg.value = "";
  29.         }
  30.     }
  31.     
  32. </script>

    2.配置服務端

    服務端的配置在web.xml文件中,如下所示

  1.   <servlet>
  2.     <servlet-name>keeper</servlet-name>
  3.     <servlet-class>keeper.servlet.Keeper</servlet-class>
  4.     <init-param>
  5. <!--可選項,設置生成客戶端的JavaScript路徑和名字,默認置為/keeper.js-->
  6.       <param-name>ScriptName</param-name>
  7.       <param-value>/keeperScript.js</param-value>
  8.     </init-param>
  9. <!--這個一定要設置,否則不能生成客戶端代碼-->
  10.     <load-on-startup>1</load-on-startup>
  11.   </servlet>
  12.   <servlet-mapping>
  13.     <servlet-name>keeper</servlet-name>
  14.     <url-pattern>/keeper</url-pattern>
  15.   </servlet-mapping>

 

        用<script type="text/javascript" src="./keeperScript.js"></script>在頁面包含JavaScript時,這里的src一定要和上面配置的一至。上面的設置除了<init-param></init-param>為可選的設置外,其他的都是必要的,而且不能改變。

 

 

        3.編寫事件處理代碼,消息的處理代碼如下:

    

  1.     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2.         System.out.println("Post..");
  3.         String msg = request.getParameter("msg");
  4.         Controller.action(new SendMsg(msg),"msg");
  5.     }

  1. class SendMsg implements EventListener{
  2.     private String msg;
  3.     public SendMsg(String msg) {
  4.         this.msg = msg;
  5.     }
  6.     @Override
  7.     public void process(HttpServletRequest request, HttpServletResponse response) {
  8.         response.setCharacterEncoding("UTF-8");
  9.         PrintWriter out = null;
  10.         try {
  11.             out = response.getWriter();
  12.             if(msg!=null){
  13.                 out.write(msg);
  14.             }
  15.         } catch (IOException e) {   
  16.             e.printStackTrace();
  17.         }
  18.         finally{
  19.             if(out != null) {
  20.                 out.close();
  21.             }
  22.         }       
  23.     }
  24. }

 

 本文由用戶 netloser 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!