webSocket長連接實現demo(場景:掃碼自動跳轉登錄,或者替換輪詢)


        我們是要再微信端出示付款二維碼(自己做的付款二維碼),他人掃碼向我們商戶付款後.該二維碼關閉並且做出相應的業務跳轉.

廢話不多說進入正題!

那就簡單說一下HTTP協議,WebSocket協議. (如果對於這些協議和網絡傳輸過程比較模糊的話,請看下這篇文章 http://blog.csdn.net/gordohu/article/details/54097841 )

一、websocket與http

WebSocket是HTML5出的(協議)可以理解爲HTTP的加強版.( 推薦文章: http://blog.csdn.net/frank_good/article/details/50856585 )

   HTTP:鏈接分爲短鏈接,長鏈接,短鏈接是每次請求都要三次握手才能發送自己的信息。即每一個request對應一個response。長鏈接是在一定的期限內保持鏈接。保持TCP連接不斷開。客戶端與服務器通信,必須要有客戶端發起然後服務器返回結果。客戶端是主動的,服務器是被動的。

    WenSocket:建立了WenSocket之後服務器不必在瀏覽器發送request請求之後才能發送信息到瀏覽器。這時的服務器已有主動權想什麼時候發就可以發送信息到服務器。而且信息當中不必在帶有head的部分信息了與http的長鏈接通信來說,這種方式,不僅能降低服務器的壓力。而且信息當中也減少了部分多餘的信息。

websocket特點:

1)建立在 TCP 協議之上,服務器端的實現比較容易。

(2)與 HTTP 協議有着良好的兼容性。默認端口也是80和443,並且握手階段採用 HTTP 協議,因此握手時不容易屏蔽,能通過各種 HTTP 代理服務器。

(3)數據格式比較輕量,性能開銷小,通信高效。

(4)可以發送文本,也可以發送二進制數據。

(5)沒有同源限制,客戶端可以與任意服務器通信。

(6)協議標識符是ws(如果加密,則爲wss),服務器網址就是 URL。

 

二、上代碼Demo 

環境,我們是Grails 框架.需要通過 intellij idea 中的 Terminal 命令框輸入命令:grails install-templates.會在項目目錄下出現web.xml.( 使用Grails 不清楚的 推薦: http://blog.csdn.net/fengbaozonghuiguoqu/article/details/77770999 ) 引入

Servlet配置的基類.

package websocket;

import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;

@WebServlet(urlPatterns = { "/zy" }) //註解簡化
public class SocketService extends WebSocketServlet {

   private static final long serialVersionUID = 1L;

   public String getId(HttpServletRequest request){
      String webSocketId = request.getParameter("webSocketId");
      System.out.println("@@建立連接webSocketId="+webSocketId);
      return webSocketId;
   }
   
   @Override
   protected StreamInbound createWebSocketInbound(String arg0,
         HttpServletRequest request) {
      return new SocketServiceInbound(this.getId(request));
   }

}


配備強大的油條!!!!!

package websocket;

import java.nio.CharBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class SocketServiceUtil {
   // 保存連接的MAP容器
   private static final Map<String, SocketServiceInbound> connections = new HashMap<String, SocketServiceInbound>();

   /**
    * 向連接池中添加連接
    * @param inbound
    */
   public static void addMessageInbound(SocketServiceInbound inbound) {
      System.out.println("id : " + inbound.getId() + " join..");
      connections.put(inbound.getId(), inbound);
   }

   /**
    * 移除連接池中的連接
    * @param id 用戶名
    */
   public static void removeMessageInbound(String id) {
      System.out.println("id : " + id + " exit..");
      connections.remove(id);
   }

   /**
    * 獲取所有的在線用戶
    * @return
    */
   public static Set<String> getOnlineId() {
      return connections.keySet();
   }

   /**
    * 向指定的用戶發送消息
    * @param id 用戶名
    * @param message 消息
    */
   public static void sendMessageToUser(String id, String message) {
      try {
         // 向特定的用戶發送數據
         System.out.println("send message to id : " + id
               + " ,message content : " + message);
         SocketServiceInbound inbound = connections.get(id);
         if (inbound != null) {
            inbound.getWsOutbound().writeTextMessage(
                  CharBuffer.wrap(message));
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }

   /**
    * 向所有的用戶發送消息
    * @param message 消息
    */
   public static void sendMessageToAll(String message) {
      try {
         Set<String> keySet = connections.keySet();
         for (String key : keySet) {
            SocketServiceInbound inbound = connections.get(key);
            if (inbound != null) {
               System.out.println("send message to user : " + key
                     + " ,message content : " + message);
               inbound.getWsOutbound().writeTextMessage(
                     CharBuffer.wrap(message));
            }
         }
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

靜態常量Map<String,SocketServiceInbound > connections 用於保證每個id 對應一個連接.



鏈接消息綁定.繼承MessageInbound 自定義String id ,與Util中參數數據結構一致.


package websocket;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.WsOutbound;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;

public class SocketServiceInbound extends MessageInbound {
   
   // 當前連接的用戶名稱
   private final String id;

   public SocketServiceInbound(String id) {
      this.id = id;
   }

   public String getId() {
      return this.id;
   }

   @Override
   protected void onOpen(WsOutbound outbound) {
      // TODO Auto-generated method stub
      //向連接池添加當前的連接對象
      SocketServiceUtil.addMessageInbound(this);
      //向當前連接發送當前在線用戶的列表
      //SocketServiceUtil.sendMessageToUser(this.user, "uuuuuuuuuu");
   }

   @Override
   protected void onClose(int status) {
      // TODO Auto-generated method stub
      // 觸發關閉事件,在連接池中移除連接
      SocketServiceUtil.removeMessageInbound(this.id);
   }

   @Override
   protected void onTextMessage(CharBuffer message) throws IOException {
      // TODO Auto-generated method stub
      SocketServiceUtil.sendMessageToUser(this.id, message.toString());
   }

   @Override
   protected void onBinaryMessage(ByteBuffer arg0) throws IOException {
      // TODO Auto-generated method stub
      throw new UnsupportedOperationException("Binary message not supported.");
   }
}

繼承後的方法的重寫 可直接在頁面中調用.eg:如下

<%@ page contentType="text/html;charset=UTF-8" %>
<html>
<head>
    <title></title>
</head>

<body>
<H2>WebSocket Demo</H2>
<button id="toAll" onclick="sendToAll()">sendToAll</button>
<button id="tohujunbao" value="hujunbao" onclick="sendToHu()">sendTohujunbao</button>
<div id="helloDiv"></div>
<script src="../js/jquery-1.8.3.min.js"></script>
</body>

<script>
    /**************************************************WebSocket********************************************/
    var websocket = null;
    var id = null
    function sendToHu() {
        $.post("../Testsicket/sendMessageTo",{"id":$("#tohujunbao").val(),"mesg":"testMessageToJunBao"},function (data) {

        })
    }
    function sendToAll() {
        $.post("../Testsicket/sendMessageTo",{"id":id,"mesg":"testMessageToAll"},function (data) {

        })
    }

    // 判斷當前瀏覽器是否支持WebSocket
    if ('WebSocket' in window) {
        websocket = new WebSocket("ws://192.168.31.104:80/zyDoctor/zy?webSocketId=hujunbao");
    } else {
        alert('當前瀏覽器 Not support websocket3')
    }

    // 連接發生錯誤的回調方法
    websocket.onerror = function() {
        alert("連接錯誤");
    };

    // 連接成功建立的回調方法
    websocket.onopen = function() {
        alert("鏈接成功");
    };

    // 接收到消息的回調方法
    websocket.onmessage = function(event) {
        //alert(event.data);
        $("#helloDiv").append(event.data);
        $("#helloDiv").append("<br>------------------</br>");
    };

    // 連接關閉的回調方法
    websocket.onclose = function() {
        //setMessageInnerHTML("WebSocket連接關閉");
    };

    // 監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。
    window.onbeforeunload = function() {
        closeWebSocket();
    };

    // 關閉WebSocket連接
    function closeWebSocket() {
        websocket.close();
    }

    // 發送消息
    function send(message) {
        websocket.send(message);
    }
    </script>

</html>

webSocket的四種狀態:

  • CONNECTING:值爲0,表示正在連接。
  • OPEN:值爲1,表示連接成功,可以通信了。
  • CLOSING:值爲2,表示連接正在關閉。
  • CLOSED:值爲3,表示連接已經關閉,或者打開連接失敗。
在上一個推薦的文章裏都有說明.包括上文中的回調函數.



測試一下.(這裏測試消息的發送,回調)
1:下面就啓動項目初始化Servlet,訪問頁面,創建id爲"hujunbao"的鏈接.

查看回調信息websocket.onopen 和 websocket.onerror反饋!
2:"鏈接成功" or "鏈接失敗"
3:服務器高興的時候就向客戶端發送message:"testMessageToJunBao" or "testMessageToAll" ......根據消息不同,做出相對應的業務!
4:業務完成後可在業務中關閉,或者添加監聽事件,來控制鏈接狀態.


 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章