什么是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
:
import time
from django.shortcuts import render
from dwebsocket.decorators import accept_websocket
@accept_websocket
def test(request):
if request.is_websocket():
print('websocket connection....')
msg = request.websocket.wait() # 接收前端發來的消息
print(msg, type(msg), json.loads(msg)) # b'["1","2","3"]' <class 'bytes'> ['1', '2', '3']
while 1:
if msg:
# 你要返回的結果
for i in range(10):
request.websocket.send('service message: {}'.format(i).encode()) # 向客戶端發送數據
time.sleep(0.5) # 每0.5秒發一次
request.websocket.close()
else: # 如果是普通的請求返回頁面
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>
歡迎斧正,that's all see also:[websocket知識匯總以及websocket在Django中的實現](