基于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实现的解析crontab配置文件代码
Jun 30 Python
Mac中升级Python2.7到Python3.5步骤详解
Apr 27 Python
python @property的用法及含义全面解析
Feb 01 Python
Python学习之Django的管理界面代码示例
Feb 10 Python
python opencv检测目标颜色的实例讲解
Apr 02 Python
python实现定时提取实时日志程序
Jun 22 Python
django框架自定义用户表操作示例
Aug 07 Python
python 切换root 执行命令的方法
Jan 19 Python
使用Python 统计高频字数的方法
Jan 31 Python
PyCharm 2019.3发布增加了新功能一览
Dec 08 Python
利用PyQt5+Matplotlib 绘制静态/动态图的实现代码
Jul 13 Python
手把手教你怎么用Python实现zip文件密码的破解
May 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
php时间不正确的解决方法
2008/04/09 PHP
php下通过伪造http头破解防盗链的代码
2010/07/03 PHP
php实现两表合并成新表并且有序排列的方法
2014/12/05 PHP
PHP substr()函数参数解释及用法讲解
2017/11/23 PHP
JavaScript 捕获窗口关闭事件
2009/07/26 Javascript
js 绑定带参数的事件以及手动触发事件
2010/04/27 Javascript
JQuery操作Select的Options的Bug(IE8兼容性视图模式)
2013/04/21 Javascript
兼容FF和IE的动态table示例自写
2013/10/21 Javascript
javascript写的异步加载js文件函数(支持数组传参)
2014/06/07 Javascript
js delete 用法(删除对象属性及变量)
2014/08/24 Javascript
原生js实现图片层叠轮播切换效果
2016/02/02 Javascript
jQuery实现拖拽页面元素并将其保存到cookie的方法
2016/06/12 Javascript
js实现旋转木马效果
2017/03/17 Javascript
jQuery UI 实例讲解 - 日期选择器(Datepicker)
2017/09/18 jQuery
基于webpack.config.js 参数详解
2018/03/20 Javascript
jQuery实现判断上传图片类型和大小的方法示例
2018/04/11 jQuery
vue-cli脚手架build目录下utils.js工具配置文件详解
2018/09/14 Javascript
解决vue热替换失效的根本原因
2018/09/19 Javascript
使用xampp将angular项目运行在web服务器的教程
2019/09/16 Javascript
Vue(定时器)解决mounted不能获取到data中的数据问题
2020/07/30 Javascript
Python函数中定义参数的四种方式
2014/11/30 Python
对Python进行数据分析_关于Package的安装问题
2017/05/22 Python
Windows下python3.6.4安装教程
2018/07/31 Python
在Python 中实现图片加框和加字的方法
2019/01/26 Python
使用tensorboard可视化loss和acc的实例
2020/01/21 Python
Pandas实现一列数据分隔为两列
2020/05/18 Python
Html5原生拖拽相关事件简介以及基础实现
2020/11/19 HTML / CSS
工程师自我评价怎么写
2013/09/19 职场文书
采购文员岗位职责
2013/11/20 职场文书
上课打牌的检讨书
2014/02/15 职场文书
学习十八大报告感言
2014/02/28 职场文书
学校查摆问题整改措施
2014/09/28 职场文书
教师自我剖析材料(群众路线)
2014/09/29 职场文书
村干部四风问题整改措施
2014/09/30 职场文书
汽车质检员岗位职责
2015/04/08 职场文书
Spring this调用当前类方法无法拦截的示例代码
2022/03/20 Java/Android