掃碼登錄核心邏輯過程:
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請求都是長連接的,即請求之後只有服務端響應了數據,纔會關閉連接。
驗證隨機數結果:
前端顯示:
服務器端日誌: