利用長輪詢實現模仿網頁掃碼登入邏輯生成驗證隨機數
掃碼登入核心邏輯過程:
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請求都是長連線的,即請求之後只有服務端響應了資料,才會關閉連線。
驗證隨機數結果:
前端顯示:
伺服器端日誌: