基于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的内存泄漏及gc模块的使用分析
Jul 16 Python
浅谈Python中的数据类型
May 05 Python
基于Python实现文件大小输出
Jan 11 Python
python:socket传输大文件示例
Jan 18 Python
浅谈django model postgres的json字段编码问题
Jan 05 Python
TensorFlow saver指定变量的存取
Mar 10 Python
python 重定向获取真实url的方法
May 11 Python
超简单使用Python换脸实例
Mar 27 Python
PyQt5 在label显示的图片中绘制矩形的方法
Jun 17 Python
python手写均值滤波
Feb 19 Python
Django高并发负载均衡实现原理详解
Apr 04 Python
全网首秀之Pycharm十大实用技巧(推荐)
Apr 27 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
Thinkphp实现站点静态化的方法详解
2017/03/21 PHP
Laravel学习基础之migrate的使用教程
2017/10/11 PHP
tp5框架基于Ajax实现列表无刷新排序功能示例
2020/02/10 PHP
thinkphp5.1框架模板赋值与变量输出示例
2020/05/25 PHP
js 全兼容可高亮二级缓冲折叠菜单
2010/06/04 Javascript
jquery索引在使用中的一些困惑
2013/10/24 Javascript
基于jquery插件制作左右按钮与标题文字图片切换效果
2013/11/07 Javascript
JS实现同时搜索百度和必应的方法
2015/01/27 Javascript
基于jquery实现简单的手风琴特效
2015/11/24 Javascript
浏览器兼容的JS写法总结
2016/04/27 Javascript
angular实现商品筛选功能
2017/02/01 Javascript
js实现九宫格的随机颜色跳转
2017/02/19 Javascript
bootstrap实现动态进度条效果
2017/03/08 Javascript
Angular2入门教程之模块和组件详解
2017/05/28 Javascript
Validform验证时可以为空否则按照指定格式验证
2017/10/20 Javascript
微信小程序template模板实例详解
2017/10/27 Javascript
详解webpack loader和plugin编写
2018/10/12 Javascript
Vue基础配置讲解
2019/11/29 Javascript
JS函数本身的作用域实例分析
2020/03/16 Javascript
在HTML中使用JavaScript的两种方法
2020/12/24 Javascript
[49:42]DOTA2上海特级锦标赛主赛事日 - 3 胜者组第二轮#2Secret VS EG第一局
2016/03/04 DOTA
[01:03:42]VP vs VGJ.S 2018国际邀请赛小组赛BO2 第一场 8.19
2018/08/21 DOTA
使用PDB简单调试Python程序简明指南
2015/04/25 Python
python清除字符串里非字母字符的方法
2015/07/02 Python
Django admin实现图书管理系统菜鸟级教程完整实例
2017/12/12 Python
Python 装饰器实现DRY(不重复代码)原则
2018/03/05 Python
Python文件操作函数用法实例详解
2019/12/24 Python
pytorch方法测试详解——归一化(BatchNorm2d)
2020/01/15 Python
pymysql模块使用简介与示例
2020/11/17 Python
仲裁协议书
2014/09/26 职场文书
三年级学生期末评语
2014/12/26 职场文书
如何书写民事调解协议书?
2019/06/25 职场文书
Python入门之使用pandas分析excel数据
2021/05/12 Python
React 并发功能体验(前端的并发模式)
2021/07/01 Javascript
java设计模式--三种工厂模式详解
2021/07/21 Java/Android
Redis基本数据类型Zset有序集合常用操作
2022/06/01 Redis