1. 程式人生 > >利用長輪詢實現模仿網頁掃碼登入邏輯生成驗證隨機數

利用長輪詢實現模仿網頁掃碼登入邏輯生成驗證隨機數

掃碼登入核心邏輯過程:

1.頁面首先向伺服器請求一個URL地址+唯一隨機數
2.伺服器在資料庫記錄這條隨機數
3.頁面通過URL+隨機數資料生成二維碼,並持續詢問伺服器該隨機數狀態(PS:這是最關鍵的步驟)
4.手機通過掃描二維碼訪問伺服器,伺服器獲得隨機數引數,在資料庫中將這條引數的狀態進行更改
5.頁面獲得知伺服器中該隨機數狀態變更後,進行登入

長輪詢:客戶端向伺服器傳送Ajax請求,伺服器接到請求後hold住連線,直到有新訊息才返回響應資訊並關閉連線,客戶端處理完響應資訊後再向伺服器傳送新的請求。
優點:在無訊息的情況下不會頻繁的請求,耗費資源小。
缺點:伺服器hold連線會消耗資源,返回資料順序無保證,難於管理維護。

長輪詢和短輪詢的區別

1.短輪詢就是常規的請求,頁面傳送一個http請求,伺服器不管獲沒獲得到想要的資料,都要立刻返回一個值,如果頁面獲知沒有獲得想要的資料將會再一次發起一個http請求,時間間隔短,缺點是頻繁傳送和接受垃圾資料。

2.長輪詢的意思就是同樣發起請求,頁面不需要進行修改,伺服器端變更邏輯,如果伺服器端沒有獲取到想要的值,那麼進行迴圈查詢,直到找到想要的值並返回給頁面,這樣的頁面與伺服器端交換的資料都是有用的資料,但是伺服器資源開銷大。

下面我用隨機數生成和獲取狀態展示一下長輪詢和短輪詢:

前端程式碼:

//生成隨機數,隨機數生成後將隨機數傳入longPolling()方法
//timestamp為時間戳,ie環境下如果請求相同,則不會執行ajax方法,所以加個時間戳 function codeKey(){ $.ajax({ url: "http://localhost:8080/LongCon/servlet/CodeKey?timestamp="+new Date(), dataType: "text", async: true, error: function (XMLHttpRequest, textStatus, errorThrown) { //服務端隨機數生成失敗,重新生成
codeKey(); }, success: function (data, textStatus) { if (textStatus == "success") { // 請求成功 $("#state").append("隨機數生成了!隨機數為:"+data+"<br>"); codeKey(); } } }); }

服務端程式碼:

public static String getRandomString() { //length表示生成字串的長度  
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";     
        Random random = new Random();     
        StringBuffer sb = new StringBuffer();     
        for (int i = 0; i < 10; i++) {     
            int number = random.nextInt(base.length());     
            sb.append(base.charAt(number));     
        }     
        return sb.toString();     
     }  

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        PrintWriter writer = response.getWriter();
        String codeKey = getRandomString();
        System.out.println("CodeKeySessionID:"+request.getSession().getId());
        System.out.println("隨機數生成:"+codeKey);
        writer.print(codeKey);
    }

這其實就是個短輪詢的例子,我做了一點修改,前端不停地從服務端獲取隨機數,服務端會立馬生成一個隨機數並返回,但是生成的十個隨機數裡可能只有一個是你想要的資料,那麼其他九個就是垃圾資料。

長輪詢前端程式碼:

function longPolling(CodeKey) {
                    $.ajax({
                        url: "http://localhost:8080/LongCon/servlet/CodeConfirm?timestamp"+new Date(),
                        data: {"CodeKey": CodeKey},
                        dataType: "text",
                        async: true,
                        error: function (XMLHttpRequest, errorThrown) {
                                  $("#state").append("狀態異常!請重新生成二維碼</br>");
                            //if (textStatus == "timeout") { // 請求超時
                            //       $("#state").append("二維碼被確認了,使用者資訊:"+data);
                            //    } else { 
                            //       $("#state").append("二維碼被確認了,使用者資訊:"+data);
                            //    }
                            },
                        success: function (data) {
                            if(data=="timeout")
                            {
                                $("#state").append("隨機數失效了!請重新生成隨機數</br>");
                                longPolling(CodeKey) ;
                            }
                            else
                            {
                                $("#state").append("隨機數被確認了!使用者資訊:"+data+"</br>");
                                longPolling(CodeKey) ;
                            }


                        }
                    });
                };

長輪詢服務端程式碼:

public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        PrintWriter writer = response.getWriter();
        String codeKey = request.getParameter("CodeKey");
        HttpSession session = request.getSession(true);
        System.out.println("CodeConfirmSessionId:"+request.getSession().getId());
        Random rand = new Random();
        String userName = "wangs"+codeKey;
        int count=0;//查詢計數器,
        while (true) {
            if(count>=5){//大於5次的話,輪詢終止,返回給頁面超時狀態
                writer.print("timeout");
                break;
            }
            try {
                Thread.sleep(300);// 模仿資料查詢任務
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } 

            int i = rand.nextInt(100); // 產生一個0-100之間的隨機數

            if (i > 50 && i < 60) { // 模仿等待隨機數確認,隨機數在區間內的則認定確認隨機數,區間外則認定尚未確認隨機數
                writer.print(userName);

                break; // 跳出迴圈,返回資料

            } else { // 模擬沒有資料變化,將休眠 hold住連線

                try {
                    count++;
                    System.out.println("CodeKey未確認"+count);
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }

        }
    }

其實可以看出前端並沒有什麼變化,主要是後臺邏輯變了,長輪詢這裡會迴圈查詢正確資料,查詢到了才會返回,若長時間查詢不到會返回一個超時資訊,跳出迴圈,防止出現死迴圈出現,我這裡做了一個計數器,迴圈5次後如果還沒查出正確資料便會終止迴圈。

所謂的長連線,其實可以F12中看到,每一條http請求中的header
這裡寫圖片描述
Connection:keep-alive便是長連線,也就是說目前http1.1協議下的每一個http請求都是長連線的,即請求之後只有服務端響應了資料,才會關閉連線。

驗證隨機數結果:

前端顯示:
這裡寫圖片描述

伺服器端日誌:
這裡寫圖片描述