打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
基于django channel 实现websocket的聊天室

websocket

​ 网易聊天室?

​ web微信?

​ 直播?

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

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

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

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

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

http协议:

​ 一次请求 一次相应 断开

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

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

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

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

​ 每分钟访问60次服务器

​ 优点:消息就基本实时

缺点:双资源浪费

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

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

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

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

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

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

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

websocket 是一个长连接

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

简单应用

  1. <body>
  2. <!-- 输入内容-->
  3. <input type="text" id="input">
  4. <!-- 提交数据-->
  5. <button> 提交数据</button>
  6. <!-- 显示内容-->
  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. //现在html已经提供了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. //点击事件
  24. button.onclick=function () {
  25. request=input.value;
  26. socket.send(request)
  27. }
  28. //获取返回数据
  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>

优化前端代码

  1. button.onclick=function () {
  2. request=input.value;
  3. socket.send(request);
  4. input.value=''
  5. }
  6. //获取返回数据
  7. socket.onmessage = function (data) {
  8. var dv=document.createElement('div');
  9. dv.innerHTML=data.data;
  10. message.appendChild(dv)
  11. };

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服务端

准备前端页面

  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,使其返回数据

  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同级目录下创建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'

准备聊天室的页面

  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>

准备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. ]

实现简单的发送返回

  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. }))

创建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. })

执行数据库的迁移命令

python manage.py migrate

要实现群聊功能,还需要准备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. }))

 

欢迎关注公众号获取源码:南城故梦 

一个程序媛的后花园

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Django实战: channels+celery+websocket打造聊天机器人(附源码)
hasCode.com ? Blog Archive ? Creating a Chat Application using Java EE 7, Websockets and GlassFis
1024
WebSocket如何向前端发送Json数据
python测试开发django
Python web实战 | 细说 Django 的 WebSocket 支持
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服