django 實現websocket


一、簡述: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連接不斷發送消息,服務器端自執行函數觸發我們的查詢,造成一個偽實時。不知道是否有更好的方法?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM