日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

基于django channel 實(shí)現(xiàn)websocket的聊天室

 看見就非常 2020-04-30

websocket

? 網(wǎng)易聊天室?

? web微信?

? 直播?

假如你工作以后,你的老板讓你來開發(fā)一個(gè)內(nèi)部的微信程序,你需要怎么辦?我們先來分析一下里面的技術(shù)難點(diǎn)

  • 消息的實(shí)時(shí)性?
  • 實(shí)現(xiàn)群聊

現(xiàn)在有這樣一個(gè)需求,老板給到你了,關(guān)乎你是否能轉(zhuǎn)正?你要怎么做?

我們先說消息的實(shí)時(shí)性,按照我們目前的想法是我需要用http協(xié)議來做,那么http協(xié)議怎么來做那?

是不是要一直去訪問我們的服務(wù)器,問服務(wù)器有沒有人給我發(fā)消息,有沒有人給我發(fā)消息?那么大家認(rèn)為我多長(zhǎng)時(shí)間去訪問一次服務(wù)比較合適那? 1分鐘1次?1分鐘60次?那這樣是不是有點(diǎn)問題那?咱們都知道http發(fā)起一次請(qǐng)求就需要三次握手,四次斷開,那么這樣是不是對(duì)我服務(wù)器資源是嚴(yán)重的浪費(fèi)啊?對(duì)我本地的資源是不是也是嚴(yán)重的浪費(fèi)啊?這種方式咱們是不是一直去服務(wù)器問啊?問有沒有我的信息?有我就顯示?這種方式咱們一般稱為輪詢

http協(xié)議:

? 一次請(qǐng)求 一次相應(yīng) 斷開

? 無狀態(tài)的 - 你曾經(jīng)來過 session or cookie

? 在斷開的情況下如果有數(shù)據(jù)只能等下次再訪問的時(shí)候返回

那么我們先來總結(jié)一下,輪詢優(yōu)缺點(diǎn)

輪詢 02年之前使用的都是這種技術(shù)

? 每分鐘訪問60次服務(wù)器

? 優(yōu)點(diǎn):消息就基本實(shí)時(shí)

缺點(diǎn):雙資源浪費(fèi)

長(zhǎng)輪詢 2000-現(xiàn)在一直在使用

客戶端發(fā)送一個(gè)請(qǐng)求- 服務(wù)器接受請(qǐng)求-不返回- 阻塞等待客戶端-如果有消息了-返回給客戶端

然后客戶端立即請(qǐng)求服務(wù)器

? 優(yōu)點(diǎn):節(jié)省了部分資源,數(shù)據(jù)實(shí)時(shí)性略差

? 缺點(diǎn):斷開連接次數(shù)過多

那有沒有一種方法是:我的服務(wù)器知道我的客戶端在哪?有客戶端的消息的時(shí)候我就把數(shù)據(jù)發(fā)給客戶端

websocket是一種基于tcp的新網(wǎng)絡(luò)協(xié)議,它實(shí)現(xiàn)了瀏覽器和服務(wù)器之間的雙全工通信,允許服務(wù)端直接向客戶端發(fā)送數(shù)據(jù)

websocket 是一個(gè)長(zhǎng)連接

現(xiàn)在咱們的前端已經(jīng)支持websocket協(xié)議了,可以直接使用websocket

簡(jiǎn)單應(yīng)用

  1. <body>
  2. <!-- 輸入內(nèi)容-->
  3. <input type="text" id="input">
  4. <!-- 提交數(shù)據(jù)-->
  5. <button> 提交數(shù)據(jù)</button>
  6. <!-- 顯示內(nèi)容-->
  7. <div>
  8. <div ></div>
  9. </div>
  10. <script>
  11. var input=document.getElementById('input');
  12. var button=document.querySelector('button');
  13. var message=document.querySelector('div');
  14. //websocket在瀏覽器端如何使用
  15. //現(xiàn)在html已經(jīng)提供了websocket,我們可以直接使用
  16. var socket= new WebSocket('ws://echo.websocket.org');
  17. socket.onopen=function () {
  18. message.innerHTML='連接成功了'
  19. };
  20. //socket.addEventListener('open',function (data) {
  21. // message.innerHTML='連接成功了'
  22. //});
  23. //點(diǎn)擊事件
  24. button.onclick=function () {
  25. request=input.value;
  26. socket.send(request)
  27. }
  28. //獲取返回?cái)?shù)據(jù)
  29. socket.onmessage=function (data) {
  30. message.innerHTML=data.data
  31. };
  32. socket.onclose=function (data) {
  33. message.innerHTML=data.data
  34. }
  35. </script>
  36. </body>

優(yōu)化前端代碼

  1. button.onclick=function () {
  2. request=input.value;
  3. socket.send(request);
  4. input.value=''
  5. }
  6. //獲取返回?cái)?shù)據(jù)
  7. socket.onmessage = function (data) {
  8. var dv=document.createElement('div');
  9. dv.innerHTML=data.data;
  10. message.appendChild(dv)
  11. };

websocket 事件

事件 事件處理函數(shù) 描述
open socket.onopen 連接建立是觸發(fā)
message socket.onmessage 客戶端收到服務(wù)端數(shù)據(jù)是觸發(fā)
error socket.error 通信發(fā)生錯(cuò)誤時(shí)觸發(fā)
close socket.close 連接關(guān)閉時(shí)觸發(fā)

websocket方法

方法 描述
socket.send() 使用連接發(fā)送數(shù)據(jù)
socket.close() 關(guān)閉連接

websocke treadyState值的狀態(tài)

描述
0 (CONNECTING) 正在鏈接中
1 (OPEN) 已經(jīng)鏈接并且可以通訊
2 (CLOSING) 連接正在關(guān)閉
3 (CLOSED) 連接已關(guān)閉或者沒有鏈接成功

自建websocket服務(wù)端

準(zhǔn)備前端頁面

  1. <!-- chat/templates/chat/index.html -->
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <meta charset="utf-8"/>
  6. <title>Chat Rooms</title>
  7. </head>
  8. <body>
  9. What chat room would you like to enter?<br/>
  10. <input id="room-name-input" type="text" size="100"/><br/>
  11. <input id="room-name-submit" type="button" value="Enter"/>
  12. <script>
  13. document.querySelector('#room-name-input').focus();
  14. document.querySelector('#room-name-input').onkeyup = function(e) {
  15. if (e.keyCode === 13) { // enter, return
  16. document.querySelector('#room-name-submit').click();
  17. }
  18. };
  19. document.querySelector('#room-name-submit').onclick = function(e) {
  20. var roomName = document.querySelector('#room-name-input').value;
  21. window.location.pathname = '/web/' + roomName + '/';
  22. };
  23. </script>
  24. </body>
  25. </html>

編輯django的views,使其返回?cái)?shù)據(jù)

  1. # chat/views.py
  2. from django.shortcuts import render
  3. def index(request):
  4. return render(request, 'chat/index.html', {})

修改url

  1. from django.conf.urls import url
  2. from .views import *
  3. urlpatterns = [
  4. url(r'^$', index, name='index'),
  5. ]

跟settings同級(jí)目錄下創(chuàng)建routing.py 文件

  1. # mysite/routing.py
  2. from channels.routing import ProtocolTypeRouter
  3. application = ProtocolTypeRouter({
  4. # (http->django views is added by default)
  5. })

編輯settings文件,將channels添加到installed_apps里面

  1. INSTALLED_APPS = [
  2. 'channels',
  3. 'chat',
  4. 'django.contrib.admin',
  5. 'django.contrib.auth',
  6. 'django.contrib.contenttypes',
  7. 'django.contrib.sessions',
  8. 'django.contrib.messages',
  9. 'django.contrib.staticfiles',
  10. ]

并添加channel的配置信息

ASGI_APPLICATION = 'mysite.routing.application'

準(zhǔn)備聊天室的頁面

  1. <!-- chat/templates/chat/room.html -->
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <meta charset="utf-8"/>
  6. <title>Chat Room</title>
  7. </head>
  8. <body>
  9. <textarea id="chat-log" cols="100" rows="20"></textarea><br/>
  10. <input id="chat-message-input" type="text" size="100"/><br/>
  11. <input id="chat-message-submit" type="button" value="Send"/>
  12. </body>
  13. <script>
  14. var roomName = {{ room_name_json|safe }};
  15. var chatSocket = new WebSocket(
  16. 'ws://' + window.location.host +
  17. '/ws/chat/' + roomName + '/');
  18. chatSocket.onmessage = function(e) {
  19. var data = JSON.parse(e.data);
  20. var message = data['message'];
  21. document.querySelector('#chat-log').value += (message + '\n');
  22. };
  23. chatSocket.onclose = function(e) {
  24. console.error('Chat socket closed unexpectedly');
  25. };
  26. document.querySelector('#chat-message-input').focus();
  27. document.querySelector('#chat-message-input').onkeyup = function(e) {
  28. if (e.keyCode === 13) { // enter, return
  29. document.querySelector('#chat-message-submit').click();
  30. }
  31. };
  32. document.querySelector('#chat-message-submit').onclick = function(e) {
  33. var messageInputDom = document.querySelector('#chat-message-input');
  34. var message = messageInputDom.value;
  35. chatSocket.send(JSON.stringify({
  36. 'message': message
  37. }));
  38. messageInputDom.value = '';
  39. };
  40. </script>
  41. </html>

準(zhǔn)備views文件,使其返回頁面

  1. def room(request, room_name):
  2. return render(request, 'chat/room.html', {
  3. 'room_name_json':json.dumps(room_name)
  4. })

修改url

  1. from django.conf.urls import url
  2. from . import views
  3. urlpatterns = [
  4. url(r'^$', views.index, name='index'),
  5. url(r'^(?P<room_name>[^/]+)/$', views.room, name='room'),
  6. ]

實(shí)現(xiàn)簡(jiǎn)單的發(fā)送返回

  1. from channels.generic.websocket import WebsocketConsumer
  2. import json
  3. class ChatConsumer(WebsocketConsumer):
  4. def connect(self):
  5. self.accept()
  6. def disconnect(self, close_code):
  7. pass
  8. def receive(self, text_data):
  9. text_data_json = json.loads(text_data)
  10. message = text_data_json['message']
  11. self.send(text_data=json.dumps({
  12. 'message': message
  13. }))

創(chuàng)建ws的路由

  1. # chat/routing.py
  2. from django.conf.urls import url
  3. from . import consumers
  4. websocket_urlpatterns = [
  5. url(r'^ws/chat/(?P<room_name>[^/]+)/$', consumers.ChatConsumer),
  6. ]

修改application的信息

  1. # mysite/routing.py
  2. from channels.auth import AuthMiddlewareStack
  3. from channels.routing import ProtocolTypeRouter, URLRouter
  4. import chat.routing
  5. application = ProtocolTypeRouter({
  6. # (http->django views is added by default)
  7. 'websocket': AuthMiddlewareStack(
  8. URLRouter(
  9. chat.routing.websocket_urlpatterns
  10. )
  11. ),
  12. })

執(zhí)行數(shù)據(jù)庫的遷移命令

python manage.py migrate

要實(shí)現(xiàn)群聊功能,還需要準(zhǔn)備redis

  1. docker run -p 6379:6379 -d redis:2.8
  2. pip3 install channels_redis

將redis添加到settings的配置文件中

  1. # mysite/settings.py
  2. # Channels
  3. ASGI_APPLICATION = 'mysite.routing.application'
  4. CHANNEL_LAYERS = {
  5. 'default': {
  6. 'BACKEND': 'channels_redis.core.RedisChannelLayer',
  7. 'CONFIG': {
  8. "hosts": [('127.0.0.1', 6379)],
  9. },
  10. },
  11. }

修改consumer.py文件

  1. from asgiref.sync import async_to_sync
  2. from channels.generic.websocket import WebsocketConsumer
  3. import json
  4. class ChatConsumer(WebsocketConsumer):
  5. def connect(self):
  6. self.room_name = self.scope['url_route']['kwargs']['room_name']
  7. self.room_group_name = 'chat_%s' % self.room_name
  8. # Join room group
  9. async_to_sync(self.channel_layer.group_add)(
  10. self.room_group_name,
  11. self.channel_name
  12. )
  13. self.accept()
  14. def disconnect(self, close_code):
  15. # Leave room group
  16. async_to_sync(self.channel_layer.group_discard)(
  17. self.room_group_name,
  18. self.channel_name
  19. )
  20. # Receive message from WebSocket
  21. def receive(self, text_data):
  22. text_data_json = json.loads(text_data)
  23. message = text_data_json['message']
  24. # Send message to room group
  25. async_to_sync(self.channel_layer.group_send)(
  26. self.room_group_name,
  27. {
  28. 'type': 'chat_message',
  29. 'message': message
  30. }
  31. )
  32. # Receive message from room group
  33. def chat_message(self, event):
  34. message = event['message']
  35. # Send message to WebSocket
  36. self.send(text_data=json.dumps({
  37. 'message': message
  38. }))

 

歡迎關(guān)注公眾號(hào)獲取源碼:南城故夢(mèng) 

一個(gè)程序媛的后花園

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多