長輪詢javascript
長久以來, 建立實現客戶端和用戶端之間雙工通信的web app
都會形成HTTP
輪詢的濫用: java
客戶端向主機不斷髮送不一樣的HTTP
呼叫來進行詢問。jquery
這會致使一系列的問題:web
服務器被迫爲每一個客戶端使用許多不一樣的底層TCP
鏈接:一個用於向客戶端發送信息,其它用於接收每一個傳入消息。編程
有線協議有很高的開銷,每個客戶端和服務器之間都有HTTP
頭。bootstrap
客戶端腳本被迫維護從傳出鏈接到傳入鏈接的映射來追蹤回覆。瀏覽器
一個更簡單的解決方案是使用單個TCP
鏈接雙向通訊。 這就是WebSocket
協議所提供的功能。 結合WebSocket API
,WebSocket
協議提供了一個用來替代HTTP
輪詢實現網頁到遠程主機的雙向通訊的方法。服務器
webscoket介紹websocket
WebSocket
協議是基於TCP
的一種新的網絡協議。它實現了瀏覽器與服務器全雙工(full-duplex)
通訊——容許服務器主動發送信息給客戶端。WebSocket
通訊協議於2011年被IETF
定爲標準RFC 6455
,並被RFC7936
所補充規範。cookie
在實現websocket
連線過程當中,須要經過瀏覽器發出websocket
連線請求,而後服務器發出迴應,這個過程一般稱爲「握手」 。在 WebSocket API
,瀏覽器和服務器只須要作一個握手的動做,而後,瀏覽器和服務器之間就造成了一條快速通道。二者之間就直接能夠數據互相傳送。在此WebSocket
協議中,爲咱們實現即時服務帶來了兩大好處:
Header
互相溝通的Header
是很小的,大概只有 2 Bytes
Server Push
服務器的推送,服務器再也不被動的接收到瀏覽器的請求以後才返回數據,而是在有新數據時就主動推送給瀏覽器。
websocket的創建過程
瀏覽器請求:
GET /webfin/websocket/ HTTP/1.1 # HTTP請求方式
Host: localhost #
Origin: http://服務器地址 #
Cookie:_aaa # cookie
#如下是創建webscoket鏈路的核心。
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg== # 密鑰
Sec-WebSocket-Version: 13 # websocket版本
服務器迴應
HTTP/1.1 101 Switching Protocols
...
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: K7D JLdLooIwIG/MOpvWFB3y3FE8=
WebSocket借用http請求進行握手,相比正常的http請求,多了一些內容。其中:
Upgrade: websocket
Connection: Upgrade
表示但願將http協議升級到Websocket協議。
Sec-WebSocket-Key是瀏覽器隨機生成的base64 encode的值,用來詢問服務器是不是支持WebSocket。
服務器返回:
Upgrade: websocket
Connection: Upgrade
告訴瀏覽器即將升級的是Websocket協議.
Sec-WebSocket-Accept是將請求包「Sec-WebSocket-Key」的值,與」258EAFA5-E914-47DA-95CA-C5AB0DC85B11″這個字符串進行拼接,而後對拼接後的字符串進行sha-1運算,再進行base64編碼獲得的。用來講明本身是WebSocket助理服務器。
Sec-WebSocket-Version是WebSocket協議版本號。RFC6455要求使用的版本是13,以前草案的版本均應當被棄用。
更多握手規範詳見RFC6455。
websocket的編程分爲服務端編程和客戶端編程。
服務端編程
Tornado定義了 tornado.websocket.WebSocketHandler 類用於處理 WebSocket 連接的請求,應用開發者應該繼承該類並實現其中的open()、on_message()、on_close() 函數。
除了這3個 Tornado 框架自動調用的入口函數,WebSocketHandler 還提供了兩個開發者主動操做 WebSocket的函數。
WebSocketHandler.write_message(message)函數:用於向與本連接相對應的客戶端寫消息
WebSocketHandler.close(code=None,reason=None)函數:主動關閉 WebSocket連接。其中的code和reason用於告訴客戶端連接被關閉的緣由。參數code必須是一個數值,而reason是一個字符串。
class BaseWebSocketHandler(tornado.websocket.WebSocketHandler,SessionMixin):
def get_current_user(self):
# current_user = self.get_secure_cookie('ID')
current_user = self.session.get('user')
if current_user:
return current_user
return None
class MessageWSHandler(BaseWebSocketHandler):
users = set()
def open(self):
# 有新的websocket連接時調用這個函數
MessageWSHandler.users.add(self)
print('-------------------open-----------------')
def on_message(self, message):
print(message,self.current_user)
for u in self.users:
u.write_message('%s-%s-說:%s'%(self.current_user.username,datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),message))
def on_close(self):
print('-------------------on_close-----------------')
# 當websocket連接關閉時調用這個函數
if self in MessageWSHandler.users:
MessageWSHandler.users.remove(self)
print(MessageWSHandler.users)
客戶端編程
因爲 WebSocket 是 HTML5 的標準之一,因此主流瀏覽器的 web 客戶端編程語言 Javascript 已經支持 WebSocket 的客戶端編程。
客戶短編程圍繞着 WebSocket 對象展開,在 Javascript 中能夠經過以下代碼初始化 WebSocket 對象:
var socket = new WebSocket(url ):
在代碼中只需給 WebSocket構造函數傳入服務器的URL地址,能夠爲該對象的以下事件指定處理函數以響應它們。
WebSocket.onopen :此事件發生在 WebSocket 連接創建時
WebSocket.onmessage :此事件發生在收到了來自服務器的消息時
WebSocket.onclose :此事件發生在與服務器的連接關閉時
WebSocket.onerror :此事件發生在通訊過程當中有任何錯誤時
除了這些事件處理函數,還能夠經過 WebSocket 對象的兩個方法進行主動操做
WebSocket.send(data) :向服務器發送消息
WebSocket.close() :主動關閉現有連接
參考代碼以下:
<body>
<div>
<textarea id="text"></textarea>
<a href="javascript:WebSocketTest();">發送</a>
</div>
<div id="messages" style="height:500px;overflow: auto;"></div>
<script src="{{static_url('js/jquery-2.2.0.min.js')}}"></script>
<!--<script src="{{static_url('js/bootstrap.min.js')}}"></script>-->
<script type="text/javascript">
var mes = document.getElementById('messages');
function WebSocketTest() {
if("WebSocket" in window){
mes.innerHTML = "發送Websocket請求成功!";
var ws = new WebSocket("ws://127.0.0.1:8000/websocket");
ws.onopen = function () {
ws.send($("#text").val()) ;
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
mes.innerHTML = mes.innerHTML +
"<br>服務器已收到信息:<br>" + received_msg;
};
ws.onclose = function () {
mes.innerHTML = mes.innerHTML + "<br>鏈接已經關閉...";
};
} else {
mes.innerHTML = "發送Websocket請求失敗!";
}
}
</script>
</body>