Django - WebSocket:dwebsocket
Django - WebSocket:dwebsocket
什麼是WebSocket
WebSocket是一種在單個TCP連線上進行全雙工通訊的協議
WebSocket使得客戶端和伺服器之間的資料交換變得更加簡單,允許服務端主動向客戶端推送資料。在WebSocket API中,瀏覽器和伺服器只需要完成一次握手,兩者之間就直接可以建立永續性的連線,並進行雙向資料傳輸
現在,很多網站為了實現推送技術,所用的技術都是輪詢。輪詢是在特定的的時間間隔(如每1秒),由瀏覽器對伺服器發出HTTP請求,然後由伺服器返回最新的資料給客戶端的瀏覽器。這種傳統的模式帶來很明顯的缺點,即瀏覽器需要不斷的向伺服器發出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的資料可能只是很小的一部分,顯然這樣會浪費很多的頻寬等資源。
而比較新的技術去做輪詢的效果是Comet。這種技術雖然可以雙向通訊,但依然需要反覆發出請求。而且在Comet中,普遍採用的長連結,也會消耗伺服器資源。
在這種情況下,HTML5定義了WebSocket協議,能更好的節省伺服器資源和頻寬,並且能夠更實時地進行通訊。
很可能用不到的判斷
WebSocket 協議在2008年誕生,2011年成為國際標準,所有瀏覽器都已經支援了。你可以這麼判斷瀏覽器是否支援:
<script> if ('WebSocket' in window) { console.log('你的瀏覽器支援 WebSocket') } </script>
WebSocket for Django
django實現websocket大致上有兩種方式,一種channels,一種是dwebsocket。channels依賴於redis,twisted等,相比之下使用dwebsocket要更為方便一些。
Install dwebsocket
pip install dwebsocket # 最新版
# 網上貌似說最新的不好用,我們可以下載大家使用較多的老版本
pip install dwebsocket==0.4.2
我開始就下的預設版本,然後報錯:
AttributeError: 'WSGIRequest' object has no attribute 'is_websocket'
後來下載老版本就好了。
服務端常用方法或者屬性
名稱 | 描述 | 備註 |
---|---|---|
@accept_websocket | 處理websocket和HTTP請求 | 該裝飾器用的較多 |
@require_websocket | 僅處理websocket請求,拒絕HTTP請求 | |
request.is_websocket() | 如果請求型別是websocket,返回True,否則返回False | 通常與@accept_websocket裝飾器搭配 |
request.websocket | 當websocket請求建立後,該請求具有一個websocket屬性,可以通過該屬性進行通訊, | 如果request.is_websocket()是False,則這個屬性為None。 |
request.websocket.wait() | 阻塞接收訊息 | |
request.websocket.read() | 非阻塞接收訊息 | |
request.websocket.count_messages() | 返回佇列中的訊息數量 | |
request.websocket.has_messages() | 如果有新訊息返回True,否則返回False | |
request.websocket.send() | 向客戶端傳送bytes型別的資料 | |
request.websocket.close() | 伺服器端主動關閉websocket服務 | |
request.websocket.iter() | websocket迭代器 |
客戶端的屬性和方法
名稱 | 型別 | 描述 |
---|---|---|
WebSocket | 物件 | 提供到服務端的雙向通道 |
onopen | 屬性 | 當websocket連線時呼叫的事件處理程式 |
onmessage | 屬性 | 通知接收到訊息的事件處理程式 |
onerror | 屬性 | 當出現錯誤時呼叫的事件處理程式 |
onclose | 屬性 | 當套接字關閉時呼叫的事件處理程式 |
readState | 屬性 | 報告websocket連線狀態 |
close | 方法 | 關閉websocket |
send | 方法 | 使用websocket向服務端傳送資料 |
url | 屬性 | 報告套接字的當前URL |
protocol | 屬性 | 報告伺服器所選中的協議 |
binaryType | 屬性 | 由onmessage接收的二進位制資料格式 |
bufferedAmount | 屬性 | 使用send的已排隊的資料位元組數 |
extensions | 屬性 | 包括伺服器所選中的副檔名 |
關於readState
,根據readState
屬性可以判斷websocket的連線狀態,該屬性的值可以是以下幾種:
屬性值 | 對應常量 | 描述 | 備註 |
---|---|---|---|
0 | CONNECTING | 正在建立連線 | 但還沒有建立完畢 |
1 | OPEN | 連線成功建立,可以進行通訊 | |
2 | CLOSING | 連線正在關閉 | 即將關閉 |
3 | CLOSED | 連線已關閉 | 或者根本沒有建立連線 |
根據bufferedAmount
可以知道有多少位元組的資料等待發送,若websocket已經呼叫了close方法該屬性將會一直增長。
必要的settings配置
# settings.py
MIDDLEWARE_CLASSES = [
'dwebsocket.middleware.WebSocketMiddleware'
]
WEBSOCKET_ACCEPT_ALL=True # 可以允許每一個單獨的檢視實用websocket
新增上這個中介軟體,就會拒絕單獨的檢視使用websocket,不過我們一般都是使用檢視搭配websocket,所以,這個配置忘掉吧,順便把第二個配置也忘掉,除非你要搞複雜的操作......
示例
環境
django1.11 + Python3.6 + PyCharm2018.1 + win10
Django中的配置
settings中保持預設即可
# urls.py
from django.conf.urls import url
from django.contrib import admin
from web import views # web是我的APP名稱
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^test/', views.test, name='test'),
]
views.py
:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
# from django.shortcuts import render
# Create your views here.
import json
import time
from django.shortcuts import render
from dwebsocket.decorators import accept_websocket
@accept_websocket
def test(request):
if request.is_websocket():
print "ws flag"
print('websocket connection....')
# msg = request.websocket.wait() # 接收前端發來的訊息
# print help(request.websocket)
# msg = request.websocket
# print(msg, type(msg), json.loads(msg)) # b'["1","2","3"]' <class 'bytes'> ['1', '2', '3']
i = 0
while 1:
msg = request.websocket.wait() # 接收前端發來的訊息
if msg:
# 你要返回的結果
# for i in range(10):
# request.websocket.send('service message: {}'.format(i).encode()) # 向客戶端傳送資料
request.websocket.send(raw_input("Please Enter Informaiton :").encode())
# time.sleep(0.5) # 每0.5秒發一次
i += 1
else:
request.websocket.close()
else: # 如果是普通的請求返回頁面
print "http flag"
return render(request, 'test.html')
test.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>test</title>
</head>
<body>
<div></div>
</body>
<!-- 首先引入 jQuery -->
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script>
// 判斷瀏覽器是否支援WebSocket,目前應該所有的瀏覽器都支援了.....
if ('WebSocket' in window) {
console.log('你的瀏覽器支援 WebSocket')
}
// 建立一個WebSocket物件:sk,並且建立與服務端的連線(服務端程式要跑著哦)
var sk = new WebSocket('ws://' + window.location.host + '/test/');
// 向服務端傳送訊息
sk.onopen = function () {
console.log('websocket connection successful...');
var l = ['1', '2', '3'];
sk.send(JSON.stringify(l));
};
// 接收服務端的訊息,主要的業務邏輯也在這裡完成
sk.onmessage = function (msg) {
// 業務邏輯
html = "<p>" + msg.data + "</p>";
$("div").append(html);
console.log('from service message: ', msg.data);
// 由於服務端主動斷開連線,這裡也斷開WebSocket連線
if (sk.readyState == WebSocket.CLOSED) sk.close();
};
// 完事就關閉WebSocket連線
sk.onclose = function (msg) {
console.log('websocket connection close...');
sk.close()
};
// 當WebSocket連線建立成功後,我們就可以向服務端傳送資料了
if (sk.readyState == WebSocket.OPEN) sk.onopen();
</script>
</html>