1. 程式人生 > 實用技巧 >Django - WebSocket:dwebsocket

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>

測試

測試網站:
http://www.websocket-test.com/

原文:https://www.cnblogs.com/Neeo/articles/11551731.html