Git:https://github.com/HAHA-LIU/websocket_demo.git
一、环境配置
# python3 环境
pip install Django==2.2
pip install channels==2.4.0
pip install channels-redis==2.4.2
- 安装redis:https://zhuanlan.zhihu.com/p/34527270
- 远程连接改redis.conf:protected-mode no
- 测试redis与channels-redis通信
python manage.py shell
>>> import channels.layers
>>> channel_layer = channels.layers.get_channel_layer()
>>> from asgiref.sync import async_to_sync
>>> async_to_sync(channel_layer.send)('test_channel', {'type': 'hello'})
>>> async_to_sync(channel_layer.receive)('test_channel')
{'type': 'hello'}
>>>
二、代码结构
三、代码解析
1. websocket_demo
settings.py
# 添加对应 app
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'corsheaders',
'channels',
'web',
]
# 增加项(websocket 需要 asgi 协议)
ASGI_APPLICATION = 'websocket_demo.routing.application'
# 增加项
# WebSocket
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [('192.168.103.75', 6379)],
},
},
}
urls.py
from django.contrib import admin
from django.urls import path
from django.urls import include
urlpatterns = [
path('admin/', admin.site.urls),
path('web/', include('web.urls')),
]
asgi.py
import os
import django
from channels.routing import get_default_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "RestaurantOrder.settings")
django.setup()
application = get_default_application()
routing.py
# -*- coding: utf-8 -*-
from channels.routing import ProtocolTypeRouter
from channels.routing import URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from web.urls import websocket_url
# self.scope['type']获取协议类型
# self.scope['url_route']['kwargs']['username']获取url中关键字参数
# channels routing是scope级别的,一个连接只能由一个consumer接收和处理
application = ProtocolTypeRouter({
'websocket': AllowedHostsOriginValidator(
URLRouter(
websocket_url
)
)
})
2. web
views.py
# -*- coding:utf-8 -*-
import json
from django.shortcuts import render
from django.utils.safestring import mark_safe
def index(request):
return render(request, 'index.html', {})
def room(request, room_name):
return render(request, 'room.html', {
'room_name_json': mark_safe(json.dumps(room_name))
})
urls.py
# -*- coding:utf-8 -*-
from django.urls import re_path
from web.consumers import WebConsumers
from web import views
urlpatterns = [
re_path(r'^$', views.index, name='index'),
re_path(r'^(?P<room_name>[^/]+)/$', views.room, name='room'),
]
websocket_url = [
re_path(r'^ws/chat/(?P<room_name>[^/]+)/$', WebConsumers),
]
consumers.py
# -*- coding: utf-8 -*-
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class WebConsumers(AsyncWebsocketConsumer):
"""处理通知应用的websocket请求"""
async 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
await self.channel_layer.group_add(self.room_group_name,
self.channel_name)
await self.accept()
async def disconnect(self, code):
"""断开连接"""
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
async def receive(self, text_data=None, bytes_data=None):
"""接收到的消息,返回给前端"""
text_data_json = json.loads(text_data)
message = text_data_json['message']
# Send message to room group
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
async def chat_message(self, event):
message = event['message']
# Send message to WebSocket
await self.send(text_data=json.dumps({'message': message}))
templates/index.html
<!-- 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>
templates/room.html
<!-- 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 }};
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>
最后启动项目:python manage.py runserver 127.0.0.1:8000