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

掃碼登錄核心邏輯過程:

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請求都是長連接的,即請求之後只有服務端響應了數據,纔會關閉連接。

驗證隨機數結果:

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

服務器端日誌:
這裏寫圖片描述

資源下載:http://download.csdn.net/download/u010800201/10123055

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