基于django channel实现websocket的聊天室的方法示例


Posted in Python onApril 11, 2019

websocket

  • 网易聊天室?
  • ​ web微信?
  • ​ 直播?

假如你工作以后,你的老板让你来开发一个内部的微信程序,你需要怎么办?我们先来分析一下里面的技术难点

  • 消息的实时性?
  • 实现群聊

现在有这样一个需求,老板给到你了,关乎你是否能转正?你要怎么做?

我们先说消息的实时性,按照我们目前的想法是我需要用http协议来做,那么http协议怎么来做那?

是不是要一直去访问我们的服务器,问服务器有没有人给我发消息,有没有人给我发消息?那么大家认为我多长时间去访问一次服务比较合适那? 1分钟1次?1分钟60次?那这样是不是有点问题那?咱们都知道http发起一次请求就需要三次握手,四次断开,那么这样是不是对我服务器资源是严重的浪费啊?对我本地的资源是不是也是严重的浪费啊?这种方式咱们是不是一直去服务器问啊?问有没有我的信息?有我就显示?这种方式咱们一般称为轮询

http协议:

​ 一次请求 一次相应 断开

​ 无状态的 - 你曾经来过 session or cookie

​ 在断开的情况下如果有数据只能等下次再访问的时候返回

那么我们先来总结一下,轮询优缺点

轮询02年之前使用的都是这种技术

​ 每分钟访问60次服务器

​ 优点:消息就基本实时

缺点:双资源浪费

长轮询2000-现在一直在使用

客户端发送一个请求- 服务器接受请求-不返回- 阻塞等待客户端-如果有消息了-返回给客户端

然后客户端立即请求服务器

​ 优点:节省了部分资源,数据实时性略差

​ 缺点:断开连接次数过多

那有没有一种方法是:我的服务器知道我的客户端在哪?有客户端的消息的时候我就把数据发给客户端

websocket是一种基于tcp的新网络协议,它实现了浏览器和服务器之间的双全工通信,允许服务端直接向客户端发送数据

websocket 是一个长连接

现在咱们的前端已经支持websocket协议了,可以直接使用websocket

简单应用

<body>
  <!-- 输入内容-->
  <input type="text" id="input">
  <!-- 提交数据-->
  <button> 提交数据</button>
  <!-- 显示内容-->
  <div>
    <div ></div>
  </div>

<script>
  var input=document.getElementById('input');
  var button=document.querySelector('button');
  var message=document.querySelector('div');
  //websocket在浏览器端如何使用
  //现在html已经提供了websocket,我们可以直接使用
  var socket= new WebSocket('ws://echo.websocket.org');
  socket.onopen=function () {
    message.innerHTML='连接成功了'
  };
  //socket.addEventListener('open',function (data) {
   //  message.innerHTML='连接成功了'
  //});
  //点击事件
  button.onclick=function () {
    request=input.value;
    socket.send(request)
  }
  //获取返回数据
  socket.onmessage=function (data) {
    message.innerHTML=data.data
  };
  socket.onclose=function (data) {
    message.innerHTML=data.data
  }

</script>

</body>

优化前端代码

button.onclick=function () {
    request=input.value;
    socket.send(request);
    input.value=''
  }
  //获取返回数据
  socket.onmessage = function (data) {
    var dv=document.createElement('div');
    dv.innerHTML=data.data;
    message.appendChild(dv)

  };

websocket 事件

事件 事件处理函数 描述
open socket.onopen 连接建立是触发
message socket.onmessage 客户端收到服务端数据是触发
error socket.error 通信发生错误时触发
close socket.close 连接关闭时触发

websocket方法

方法 描述
socket.send() 使用连接发送数据
socket.close() 关闭连接

websocke treadyState值的状态

描述
0 (CONNECTING) 正在链接中
1 (OPEN) 已经链接并且可以通讯
2 (CLOSING) 连接正在关闭
3 (CLOSED) 连接已关闭或者没有链接成功

自建websocket服务端

准备前端页面

<!-- chat/templates/chat/index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <title>Chat Rooms</title>
</head>
<body>
  What chat room would you like to enter?<br/>
  <input id="room-name-input" type="text" size="100"/><br/>
  <input id="room-name-submit" type="button" value="Enter"/>

  <script>
    document.querySelector('#room-name-input').focus();
    document.querySelector('#room-name-input').onkeyup = function(e) {
      if (e.keyCode === 13) { // enter, return
        document.querySelector('#room-name-submit').click();
      }
    };

    document.querySelector('#room-name-submit').onclick = function(e) {
      var roomName = document.querySelector('#room-name-input').value;
      window.location.pathname = '/web/' + roomName + '/';
    };
  </script>
</body>
</html>

编辑django的views,使其返回数据

# chat/views.py
from django.shortcuts import render

def index(request):
  return render(request, 'chat/index.html', {})

修改url

from django.conf.urls import url
from .views import *

urlpatterns = [
  url(r'^$', index, name='index'),
  ]

跟settings同级目录下创建routing.py 文件

# mysite/routing.py
from channels.routing import ProtocolTypeRouter

application = ProtocolTypeRouter({
  # (http->django views is added by default)
})

编辑settings文件,将channels添加到installed_apps里面

INSTALLED_APPS = [
  'channels',
  'chat',
  'django.contrib.admin',
  'django.contrib.auth',
  'django.contrib.contenttypes',
  'django.contrib.sessions',
  'django.contrib.messages',
  'django.contrib.staticfiles',
]

并添加channel的配置信息

ASGI_APPLICATION = 'mysite.routing.application'

准备聊天室的页面

<!-- chat/templates/chat/room.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8"/>
  <title>Chat Room</title>
</head>
<body>
  <textarea id="chat-log" cols="100" rows="20"></textarea><br/>
  <input id="chat-message-input" type="text" size="100"/><br/>
  <input id="chat-message-submit" type="button" value="Send"/>
</body>
<script>
  var roomName = {{ room_name_json|safe }};

  var chatSocket = new WebSocket(
    'ws://' + window.location.host +
    '/ws/chat/' + roomName + '/');

  chatSocket.onmessage = function(e) {
    var data = JSON.parse(e.data);
    var message = data['message'];
    document.querySelector('#chat-log').value += (message + '\n');
  };

  chatSocket.onclose = function(e) {
    console.error('Chat socket closed unexpectedly');
  };

  document.querySelector('#chat-message-input').focus();
  document.querySelector('#chat-message-input').onkeyup = function(e) {
    if (e.keyCode === 13) { // enter, return
      document.querySelector('#chat-message-submit').click();
    }
  };

  document.querySelector('#chat-message-submit').onclick = function(e) {
    var messageInputDom = document.querySelector('#chat-message-input');
    var message = messageInputDom.value;
    chatSocket.send(JSON.stringify({
      'message': message
    }));

    messageInputDom.value = '';
  };
</script>
</html>

准备views文件,使其返回页面

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

修改url

from django.conf.urls import url

from . import views

urlpatterns = [
  url(r'^$', views.index, name='index'),
  url(r'^(?P<room_name>[^/]+)/$', views.room, name='room'),
]

实现简单的发送返回

from channels.generic.websocket import WebsocketConsumer
import json

class ChatConsumer(WebsocketConsumer):
  def connect(self):
    self.accept()

  def disconnect(self, close_code):
    pass

  def receive(self, text_data):
    text_data_json = json.loads(text_data)
    message = text_data_json['message']

    self.send(text_data=json.dumps({
      'message': message
    }))

创建ws的路由

# chat/routing.py
from django.conf.urls import url

from . import consumers

websocket_urlpatterns = [
  url(r'^ws/chat/(?P<room_name>[^/]+)/$', consumers.ChatConsumer),
]

修改application的信息

# mysite/routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing

application = ProtocolTypeRouter({
  # (http->django views is added by default)
  'websocket': AuthMiddlewareStack(
    URLRouter(
      chat.routing.websocket_urlpatterns
    )
  ),
})

执行数据库的迁移命令

python manage.py migrate

要实现群聊功能,还需要准备redis

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

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

# mysite/settings.py
# Channels
ASGI_APPLICATION = 'mysite.routing.application'
CHANNEL_LAYERS = {
  'default': {
    'BACKEND': 'channels_redis.core.RedisChannelLayer',
    'CONFIG': {
      "hosts": [('127.0.0.1', 6379)],
    },
  },
}

修改consumer.py文件

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
import json

class ChatConsumer(WebsocketConsumer):
  def connect(self):
    self.room_name = self.scope['url_route']['kwargs']['room_name']
    self.room_group_name = 'chat_%s' % self.room_name

    # Join room group
    async_to_sync(self.channel_layer.group_add)(
      self.room_group_name,
      self.channel_name
    )

    self.accept()

  def disconnect(self, close_code):
    # Leave room group
    async_to_sync(self.channel_layer.group_discard)(
      self.room_group_name,
      self.channel_name
    )

  # Receive message from WebSocket
  def receive(self, text_data):
    text_data_json = json.loads(text_data)
    message = text_data_json['message']

    # Send message to room group
    async_to_sync(self.channel_layer.group_send)(
      self.room_group_name,
      {
        'type': 'chat_message',
        'message': message
      }
    )

  # Receive message from room group
  def chat_message(self, event):
    message = event['message']

    # Send message to WebSocket
    self.send(text_data=json.dumps({
      'message': message
    }))

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python模块restful使用方法实例
Dec 10 Python
Python的Django框架下管理站点的基本方法
Jul 17 Python
Python cookbook(数据结构与算法)通过公共键对字典列表排序算法示例
Mar 15 Python
处理python中多线程与多进程中的数据共享问题
Jul 28 Python
分享一个pycharm专业版安装的永久使用方法
Sep 24 Python
Python Django框架模板渲染功能示例
Nov 08 Python
Python如何使用函数做字典的值
Nov 30 Python
Python原始套接字编程实例解析
Jan 29 Python
python实现UDP协议下的文件传输
Mar 20 Python
Python基础之进程详解
May 21 Python
Python图片处理之图片裁剪教程
May 27 Python
Python制作一个随机抽奖小工具的实现
Jul 07 Python
CentOS7安装Python3的教程详解
Apr 10 #Python
django富文本编辑器的实现示例
Apr 10 #Python
详解Python:面向对象编程
Apr 10 #Python
5款Python程序员高频使用开发工具推荐
Apr 10 #Python
python初学者,用python实现基本的学生管理系统(python3)代码实例
Apr 10 #Python
Python将json文件写入ES数据库的方法
Apr 10 #Python
pycharm创建一个python包方法图解
Apr 10 #Python
You might like
咖啡机如何保养和日常清洁?
2021/03/03 冲泡冲煮
自动分页的不完整解决方案
2007/01/12 PHP
php更改目录及子目录下所有的文件后缀的代码
2010/09/24 PHP
PHP中3种生成XML文件方法的速度效率比较
2012/10/06 PHP
thinkphp关于简单的权限判定方法
2017/04/03 PHP
php实现姓名根据首字母排序的类与方法(实例代码)
2018/05/16 PHP
PHP时间日期增减操作示例【date strtotime实现加一天、加一月等操作】
2018/12/21 PHP
javascript操作字符串的原生方法
2014/12/22 Javascript
Jquery Ajax xmlhttp请求成功问题
2015/02/04 Javascript
JavaScript控制listbox列表框的项目上下移动的方法
2015/03/18 Javascript
jQuery实现的多屏图像图层切换效果实例
2015/05/07 Javascript
JavaScript 通过Ajax 动态加载CheckBox复选框
2017/08/31 Javascript
浅谈React Native 中组件的生命周期
2017/09/08 Javascript
vue实现鼠标移入移出事件代码实例
2019/03/27 Javascript
一文快速了解JQuery中的AJAX
2019/05/31 jQuery
微信小程序 数据缓存实现方法详解
2019/08/26 Javascript
python client使用http post 到server端的代码
2013/02/10 Python
python通过yield实现数组全排列的方法
2015/03/18 Python
独特的python循环语句
2016/11/20 Python
对python-3-print重定向输出的几种方法总结
2018/05/11 Python
python 3.7.0 安装配置方法图文教程
2018/08/27 Python
对python中数据集划分函数StratifiedShuffleSplit的使用详解
2018/12/11 Python
Python嵌套式数据结构实例浅析
2019/03/05 Python
PyPDF2读取PDF文件内容保存到本地TXT实例
2020/05/12 Python
Django自关联实现多级联动查询实例
2020/05/19 Python
Python如何对XML 解析
2020/06/28 Python
美国百年历史早餐食品供应商:Wolferman’s
2017/01/18 全球购物
美国演唱会订票网站:Ticketmaster美国
2017/10/05 全球购物
写自荐信有哪些不宜?
2013/10/17 职场文书
初一地理教学反思
2014/01/16 职场文书
《宋庆龄故居的樟树》教学反思
2014/04/07 职场文书
工程材料采购方案
2014/05/18 职场文书
小学重阳节活动总结
2015/03/24 职场文书
2016年幼儿园万圣节活动总结
2016/04/05 职场文书
只需要12页,掌握撰写一流商业计划书的技巧
2019/05/07 职场文书
如何做好员工培训计划?
2019/07/09 职场文书