一、長輪詢
在掃碼登錄的頁面,服務器是如何知道用戶有沒有在手機上掃碼登錄?
通過長輪詢的方式,每隔幾秒向服務器發送一個請求,服務器判斷這個請求中有沒有用戶掃碼。

缺點
1.開銷大
2.浪費資源
3.消耗流量
二、websocket概念
長輪詢消耗太多資源,其中主要原因是客戶端和服務端並沒有一直連接在一起。
websocket起到的作用,就是讓客戶端和服務器一直連接在一起。
(一).websocket官話描述
WebSocket協議是基於TCP的一種新的HTML5網絡協議。它實現了瀏覽器與服務器全雙工(full-duplex)通信——允許服務器主動發送信息給客戶端。WebSocket通信協議於2011年被IETF定為標准RFC 6455,並被RFC7936所補充規范。
(二).簡單理解
客戶端和服務器一直連接在一起
三、websocket服務端編程
(一).導包
import tornado.websocket
(二).編寫一個基類
class BaseWebSocketHandler(tornado.websocket.WebSocketHandler, SessionMixin): def get_current_user(self): current_user = self.session.get('user') if current_user: return current_user return None
(三).跳轉的Handler
class IndexHandler(BaseHandler): @authenticated def get(self): self.render('08websocket.html')
(四).websocket的Handler
class MessageWSHandler(BaseWebSocketHandler): users = set() def open(self): MessageWSHandler.users.add(self) def on_message(self, message): for u in self.users: u.write_message( '%s-%s-說:%s' % ( self.current_user, datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), message )) def on_close(self): if self in MessageWSHandler.users: MessageWSHandler.users.remove(self)
(五).為WebsocketHandler添加路由
(六).總結
Tornado 定義了 tornado.websocket.WebSocketHandler 這個類,用於處理 WebSocket 鏈接的請求。
必須繼承該類並重寫 open()、on_message()、on_close() 這三個函數,Tornado會自動去調用這三個函數。
(1).WebSocketHandler還提供了兩個可以主動操作的WebSocket函數
WebSocketHandler.write_message(message)函數:用於向與本鏈接相對應的客戶端寫消息。
WebSocketHandler.close(code=None,reason=None)函數:主動關閉 WebSocket鏈接。其中的code和reason用於告訴客戶端鏈接被關閉的原因。參數code必須是一個數值,而reason是一個字符串。
四、webscoket客戶端編程
WebSocket是HTML5的標准之一,主流瀏覽器的web客戶端編程語言Javascript已經支持WebSocket的客戶端編程。
(一).初始化WebSocket對象
var socket = new WebSocket(url);
(二).處理函數
WebSocket.onopen:此事件發生在 WebSocket 鏈接建立時
WebSocket.onmessage:此事件發生在收到了來自服務器的消息時
WebSocket.onclose:此事件發生在與服務器的鏈接關閉時
WebSocket.onerror:此事件發生在通信過程中有任何錯誤時
(三).主動操作函數
WebSocket.send(data):向服務器發送消息
WebSocket.close():主動關閉現有鏈接
五、完整代碼
(一).編寫Handler
class BaseHandler(tornado.web.RequestHandler): def get_current_user(self): current_user = self.get_secure_cookie('ID') if current_user: return current_user return None class BaseWebsocketHandler(tornado.websocket.WebSocketHandler): def get_current_user(self): current_user = self.get_secure_cookie("ID") if current_user: return current_user return None class MessageWSHandler(BaseWebsocketHandler): users = set() def show_message(self, message): for u in self.users: u.write_message(message) def on_open(self): print("coming open...") self.show_message("{}進入!".format("")) MessageWSHandler.users.add(self) # self是user的實例 def on_message(self, message): print(message) for u in self.users: self.show_message("{}說:{}".format("", message)) # self.write_message(message) def on_close(self): if self in MessageWSHandler.users: self.show_message("{}退出!".format("")) MessageWSHandler.users.remove(self) class MainHandler(BaseWebsocketHandler): @authenticated def get(self): self.render("08websocket.html")
(二).編寫模板文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title> WebSocket </title> <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet"> <style> * { margin: 0; padding: 0; } .box { width: 800px; margin-left: auto; margin-right: auto; margin-top: 25px; } #text { width: 685px; height: 130px; border: 1px solid skyblue; border-radius: 10px; font-size: 20px; text-indent: 1em; resize: none; outline: none; } #text::placeholder { color: skyblue; } .btn { width: 100px; margin: -27px 0 0px 8px; } #messages { padding-left: 10px; font-size: 25px; } #messages li { list-style: none; color: #000; line-height: 30px; font-size: 18px; } </style> </head> <body> <div class="box"> <div> <textarea id="text" placeholder="請輸入您的內容"></textarea> <a href="javascript:WebSocketSend();" class="btn btn-primary">發送</a> </div> <ul id="messages"> </ul> </div> <script src="{{ static_url('js/jquery-2.2.0.min.js') }}"></script> <script type="text/javascript"> var mes = document.getElementById('messages'); if ("WebSocket" in window) { mes.innerHTML = "發送WebSocket請求成功!"; // var ws = new WebSocket("ws://127.0.0.1:8000/websocket"); var ws = new WebSocket("ws://47.98.139.237:8888/websocket"); ws.onopen = function () { alert('連接已打開請聊天') }; ws.onmessage = function (goudan) { var received_msg = goudan.data; var aLi = $("<li>" + received_msg + "</li>"); // $(mes).append($(aLi)) // 方法一 // $(aLi).appendTo(mes); // 方法二 $(mes).prepend($(aLi)) }; ws.onclose = function () { mes.innerHTML = mes.innerHTML + "<br>連接已經關閉..."; }; } else { mes.innerHTML = "發送WebSocket請求失敗!" } function WebSocketSend() { ws.send($("#text").val()); $("#text").val("") } </script> </body> </html>
前端腳本是jquery寫法,必須引入jquery.js
六、可能會踩到的坑
(一).代理插件引起的websocket失敗
(1).問題描述
我在Chrome上安裝了一個代理插件,進行科學上網。但使用websocket的時候,會報錯。把報錯的英文進行翻譯:通過代理服務器建立隧道失敗。

沒有代理插件的瀏覽器就可以成功發送消息,而且在Chrome上也可以接收到已發送過的數據,但是發送數據就會失敗。

(2).原因
代理是把請求通過代理服務器轉發給真正的服務器。一般代理只做了http和https,基本不會去做websocket,哪怕是專業的代理。
(3).解決辦法
1.退出代理賬號,然后重新訪問,一切都正常了。
2.換個沒有代理插件的瀏覽器。
3.加白名單,不讓插件去代理websocket
(4).補充
尚不清楚藍燈或者其他科學上網方式會不會遇到類似問題。如果有類似問題,一定要直接評論哈~讓別人不要踩坑了
