Django channels
- http是一个网络协议(五状态短连接)
- https是一个网络协议(五状态短连接)
- websocket是一个网络协议(让浏览器和服务端创建链接支持,默认不再断开,两端就可以完成相互之间的收发数据)
- websocket协议的诞生,可以让我们真正实现服务端向客户端推送消息
websocket实现原理:
——握手环节,验证服务端是否支持websocket协议,浏览器生成一个随机字符串,将随机字符串发送给服务端,服务端接收到随机字符串之后,让他跟magic string拼接,然后再进行sha1/base64加密,将密文返回到用户浏览器,用户浏览器会自动进行校验
——收发数据,密文 数据解密时需要读取数据第2个字节的后7位,和127,126,125比较后看情况~~
django channels 是django支持websocket的一个模块。在channels的内部已经帮我们写了握手/加密/解密等所有环节。建议在python3.6的环境中去运行。
1. 安装
pip3 install channels==2.3
2. 快速上手
2.1 在settings中添加配置
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'channels', ]
ASGI_APPLICATION = "channel_demo.routing.application"
2.2 创建websocket应用和路由
#!/usr/bin/env python # -*- coding:utf-8 -*- from channels.routing import ProtocolTypeRouter, URLRouter from django.conf.urls import url from chat import consumers application = ProtocolTypeRouter({ 'websocket': URLRouter([ url(r'^chat/$', consumers.ChatConsumer), ]) })
2.3 编写处理websocket逻辑业务
#!/usr/bin/env python # -*- coding:utf-8 -*- from channels.generic.websocket import WebsocketConsumer from channels.exceptions import StopConsumer class ChatConsumer(WebsocketConsumer): def websocket_connect(self, message): self.accept() def websocket_receive(self, message): print('接收到消息', message) self.send(text_data='收到了') def websocket_disconnect(self, message): print('客户端断开连接了') raise StopConsumer()
#!/usr/bin/env python # -*- coding:utf-8 -*- from channels.generic.websocket import WebsocketConsumer from channels.exceptions import StopConsumer class SimpleChatConsumer(WebsocketConsumer): def connect(self): self.accept() def receive(self, text_data=None, bytes_data=None): self.send(text_data) # 主动断开连接 # self.close() def disconnect(self, code): print('客户端要断开了')
#!/usr/bin/env python # -*- coding:utf-8 -*- from channels.generic.websocket import WebsocketConsumer from channels.exceptions import StopConsumer CLIENTS = [] class ChatConsumer(WebsocketConsumer): def connect(self): self.accept() CLIENTS.append(self) def receive(self, text_data=None, bytes_data=None): for item in CLIENTS: item.send(text_data) # 主动断开连接 # self.close() def disconnect(self, code): CLIENTS.remove(self)
3. channel layer
基于内存的channel layer
CHANNEL_LAYERS = { "default": { "BACKEND": "channels.layers.InMemoryChannelLayer", } }
from channels.generic.websocket import WebsocketConsumer from asgiref.sync import async_to_sync class ChatConsumer(WebsocketConsumer): def connect(self): async_to_sync(self.channel_layer.group_add)('x1', self.channel_name) self.accept() def receive(self, text_data=None, bytes_data=None): async_to_sync(self.channel_layer.group_send)('x1', { 'type': 'xxx.ooo', 'message': text_data }) def xxx_ooo(self, event): message = event['message'] self.send(message) def disconnect(self, code): async_to_sync(self.channel_layer.group_discard)('x1', self.channel_name)
基于 redis的channel layer
pip3 install channels-redis
CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [('10.211.55.25', 6379)] }, }, } CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': {"hosts": ["redis://10.211.55.25:6379/1"],}, }, } CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': {"hosts": [('10.211.55.25', 6379)],},}, } CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": ["redis://:password@10.211.55.25:6379/0"], "symmetric_encryption_keys": [SECRET_KEY], }, }, }
from channels.generic.websocket import WebsocketConsumer from asgiref.sync import async_to_sync class ChatConsumer(WebsocketConsumer): def connect(self): async_to_sync(self.channel_layer.group_add)('x1', self.channel_name) self.accept() def receive(self, text_data=None, bytes_data=None): async_to_sync(self.channel_layer.group_send)('x1', { 'type': 'xxx.ooo', 'message': text_data }) def xxx_ooo(self, event): message = event['message'] self.send(message) def disconnect(self, code): async_to_sync(self.channel_layer.group_discard)('x1', self.channel_name)