一、簡述:django實現websocket,之前django-websocket退出到3.0之后,被廢棄。官方推薦大家使用channels。
channels通過升級http協議 升級到websocket協議。保證實時通訊。也就是說,我們完全可以用channels實現我們的即時通訊。而不是使用長輪詢和計時器方式來保證偽實時通訊。
他通過改造django框架,使django既支持http協議又支持websocket協議。
官方文檔地址:https://channels.readthedocs.io/en/stable/
二:安裝
python version:2.7 3.4 3.5
安裝channels:
1 pip install -U channels
在安裝在windows機器的時候。需要自信的C++支持,報錯的時候,報錯有地址告訴你下載URL。
配置:
需要在seting.py里配置,將我們的channels加入INSTALLED_APP里。
1 INSTALLED_APPS = ( 2 'django.contrib.auth', 3 'django.contrib.contenttypes', 4 'django.contrib.sessions', 5 'django.contrib.sites', 6 ... 7 'channels', 8 )
這樣django就支持websocket了,接下來我們需要配置一些簡單的配置。
三:概念闡述:
channels: It is an ordered, first-in first-out queue with message expiry and at-most-once delivery to only one listener at a time.
它是先進先出的消息隊列,同一時刻只向一個消費者發送一個沒有過期的消息。這里的消費者類似訂閱者,或者客戶端。
默認的channels是http.request.在這種情況下運行 django 和之前的沒使用websocket來說沒有什么特別。
通過查看源碼我們可以看到其他的channels:
至於我們是否可以自定義channels目前沒有驗證!
介紹下channels結構:
首先需要建立一個django項目。其中在你自己的app下面 生成consumers.py和routing.py配置文件。
consumers.py:相當於django的視圖,也就是說所有的websocket路由過來的執行的函數都在consumers.py類似於django的視圖views.py
routing.py:是websocket中的url和執行函數的對應關系。相當於django的urls.py,根據映射關系,當websocket的請求進來的時候,根據用戶的請求來觸發我們的consumers.py里的方法。
四:代碼示例
consumer.py
1 # In consumers.py 2 3 def ws_message(message): 4 # ASGI WebSocket packet-received and send-packet message types 5 # both have a "text" key for their textual data. 6 message.reply_channel.send({ 7 "text": message.content['text'], 8 })
routing.py
1 # In routing.py 2 from channels.routing import route 3 from myapp.consumers import ws_message 4 5 channel_routing = [ 6 route("websocket.receive", ws_message), 7 ]
websocket.receive表示當用戶請求的時候,自動觸發后面的ws_message.
html code:html5支持websocket。
1 <!DOCTYPE HTML> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>測試websocket</title> 6 7 <script type="text/javascript"> 8 function WebSocketTest() 9 { 10 if ("WebSocket" in window) 11 { 12 alert("您的瀏覽器支持 WebSocket!"); 13 14 // 打開一個 web socket 15 ws = new WebSocket("ws://localhost:8000/path/"); 16 17 ws.onopen = function() 18 { 19 // Web Socket 已連接上,使用 send() 方法發送數據 20 ws.send("發送數據"); 21 alert("數據發送中..."); 22 }; 23 24 ws.onmessage = function (evt) 25 { 26 var received_msg = evt.data; 27 alert("數據已接收..."); 28 alert("數據:"+received_msg) 29 }; 30 31 ws.onclose = function() 32 { 33 // 關閉 websocket 34 alert("連接已關閉..."); 35 }; 36 } 37 38 else 39 { 40 // 瀏覽器不支持 WebSocket 41 alert("您的瀏覽器不支持 WebSocket!"); 42 } 43 } 44 </script> 45 46 </head> 47 <body> 48 49 <div id="sse"> 50 <a href="javascript:WebSocketTest()">運行 WebSocket</a> 51 </div> 52 53 </body> 54 </html>
演示:
1:
2:
3:
4:
5:
五:如上是簡單實現 我們的websocket 例子 ,其中channels來還有如下類型:
1 websocket.connect 剛建立連接。 2 3 websocket.disconnect 連接斷開的時候
可以根據自己的需求來在routing里定義 在觸發websocket各個階段的時候執行函數。
目前實現的是一個客戶端進行操作,也就是說一個consumer的情況,當我們的有多個consumer的時候,怎么保證server端發送消息所有的consumer都能接受到呢?
channels給咱們定義個group概念。也就是說只要consumer和這個組建立的關系,其他的各個consumer都會接受到消息。
consumer.py
1 # In consumers.py 2 from channels import Group 3 4 # Connected to websocket.connect 5 def ws_add(message): 6 message.reply_channel.send({"accept": True}) 7 Group("chat").add(message.reply_channel) 8 9 # Connected to websocket.receive 10 def ws_message(message): 11 Group("chat").send({ 12 "text": "[user] %s" % message.content['text'], 13 }) 14 15 # Connected to websocket.disconnect 16 def ws_disconnect(message): 17 Group("chat").discard(message.reply_channel)
routing.py:
1 from channels.routing import route 2 from myapp.consumers import ws_add, ws_message, ws_disconnect 3 4 channel_routing = [ 5 route("websocket.connect", ws_add), 6 route("websocket.receive", ws_message), 7 route("websocket.disconnect", ws_disconnect), 8 ]
在瀏覽器輸入如下js:
1 // Note that the path doesn't matter right now; any WebSocket 2 // connection gets bumped over to WebSocket consumers 3 socket = new WebSocket("ws://" + window.location.host + "/chat/"); 4 socket.onmessage = function(e) { 5 alert(e.data); 6 } 7 socket.onopen = function() { 8 socket.send("hello world"); 9 } 10 // Call onopen directly if socket is already open 11 if (socket.readyState == WebSocket.OPEN) socket.onopen();
我們打開2個瀏覽器進行測試:
當我們運行窗口二的js的時候窗口也能接受到消息。
這是因為服務端以組來發送消息。
1 Group("chat").send({ 2 "text": "[user] %s" % message.content['text'], 3 })
根據以上特性 我們可以創建我們的聊天室。
其中routing.py中支持正則路徑匹配,我們可以根據我們的需求,由用戶根據路徑不同請求不同的聊天室,想深入了解,請參考官方文檔。
為什么研究websocket?
因為在實際生產中,我們需要有一個即時通訊的,不斷請求后端結果。來反映在頁面。但是,channels測試的過程中,consumer中的函數體不能加入while循環,
測試的結果是:只有當consumer里的函數執行完,才能全部發送到客戶端消息,而不是有消息就能發送。
最后的解決方案:只能前端使用計時器同一個tcp連接不斷發送消息,服務器端自執行函數觸發我們的查詢,造成一個偽實時。不知道是否有更好的方法?