Websocket-ws通信(Channels)
Websocket-ws通信(Channels)
-原理介绍:https://www.cnblogs.com/wupeiqi/p/6558766.html
-channels文档:https://channels.readthedocs.io/en/stable/installation.html
1 websocker介绍
GET /chatsocket HTTP/1.1 Host: 127.0.0.1:8002 Connection: Upgrade Pragma: no-cache Cache-Control: no-cache Upgrade: websocket Origin: http://localhost:63342 Sec-WebSocket-Version: 13 Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg== Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits ... ...
#提取Sec-WebSocket-Key值并加密: import socket import base64 import hashlib def get_headers(data): """ 将请求头格式化成字典 :param data: :return: """ header_dict = {} data = str(data, encoding='utf-8') for i in data.split('\r\n'): print(i) header, body = data.split('\r\n\r\n', 1) header_list = header.split('\r\n') for i in range(0, len(header_list)): if i == 0: if len(header_list[i].split(' ')) == 3: header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ') else: k, v = header_list[i].split(':', 1) header_dict[k] = v.strip() return header_dict sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1', 8002)) sock.listen(5) conn, address = sock.accept() data = conn.recv(1024) headers = get_headers(data) # 提取请求头信息 # 对请求头中的sec-websocket-key进行加密 response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \ "Upgrade:websocket\r\n" \ "Connection: Upgrade\r\n" \ "Sec-WebSocket-Accept: %s\r\n" \ "WebSocket-Location: ws://%s%s\r\n\r\n" magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' value = headers['Sec-WebSocket-Key'] + magic_string ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest()) response_str = response_tpl % (ac.decode('utf-8'), headers['Host'], headers['url']) # 响应【握手】信息 conn.send(bytes(response_str, encoding='utf-8')) ... ...
2 channels(django中实现ws通信)
相关配置
即时通信原理
前端配置
3 django -channels 示例:
4 示例参考1:房间共同聊天
链接:https://mp.weixin.qq.com/s/hqaPrPS7w3D-9SeegQAB2Q
5 使用 django channels
- 1安装
pip3 install channels
- 2使用
2.1 在setting中添加配置
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'channels', ]
ASGI_APPLICATION = "django_channels_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
基于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)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)
作者:华王
博客:https://www.cnblogs.com/huahuawang/