1.原理
webSocket协议,是建立在http协议之上的
- 客户端发起连接,生成一个随机字符串后发给服务器
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
...
...
- 服务器接收,与magic_string拼接,后处理
magic_string = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
v1 = "mnwFxiOlctXFN/DeMt1Amg==" + magic_string
v2 = hmac1(v1)
v3 = base64(v2)
- 服务器返回数据
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Pragma: no-cache
Sec-WebSocket-Accept: 上面的密文
-
收发数据,会进行加密
-
断开连接
2.Django Clannels实现
在Django中,wsgi:默认支持;asgi:支持http+websocket
同步请求:客户端向服务器发送请求-->等待服务器响应-->处理完毕返回,客户端浏览器没有做别的事情。
异步请求:通过事件触发请求-->服务器处理请求-->处理完毕返回,但是客户端浏览器可以接着做别的事情
- 安装模块
pip install channels
- 配置
#setting.py
INSTALLED_APPS = [
...
'channels',
]
# 指定ASGI的路由地址
ASGI_APPLICATION = 'XiangDataDisplaySys.asgi.application'
- setting同级目录下创建/修改asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from . import routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'XiangDataDisplaySys.settings')#参数2:工程名.settings
application = ProtocalTypeRouter({
"http": get_asgi_application(), #自动找urls.py
"websocket": URLRouter(routing.websocket_urlpatterns), #创建routings,创建comsumers
})
- 在setting同级目录下创建routings.py
import os
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.conf.urls import url
from django.core.asgi import get_asgi_application
from XiangDataDisplaySys import routings
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "XiangDataDisplaySys.settings")
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
routings.websocket_urlpatterns
)
),
})
- 在app目录下创建routings.py
from django.conf.urls import re_path
from appDataDisplay import consumers
websocket_urlpatterns = [
re_path(r'^chat-channel/',consumers.ChatConsumer.as_asgi())
]
- 在app目录下创建consumers.py
#异步处理代码
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.exceptions import StopConsumer
CONN_LIST = []
class ChatConsumer(AsyncWebsocketConsumer):
#groups = ["broadcast"]
async def connect(self):
# Called on connection.
# To accept the connection call:
await self.accept()
# Or accept the connection and specify a chosen subprotocol.
# A list of subprotocols specified by the connecting client
# will be available in self.scope['subprotocols']
#await self.accept("subprotocol")
# To reject the connection, call:
#await self.close()
print("来了来了.....")
CONN_LIST.append(self)
#await self.send(text_data="要回复要回复!!!")
async def receive(self, text_data=None, bytes_data=None):
# Called with either text_data or bytes_data for each frame
# You can call:
print(text_data)
await self.send(text_data="不要回复不要回复!!!")
# # Or, to send a binary frame:
# await self.send(bytes_data="Hello world!")
# # Want to force-close the connection? Call:
# await self.close()
# # Or add a custom WebSocket error code!
# await self.close(code=4123)
async def disconnect(self, close_code):
# Called when the socket closes
print("失去了连接.....")
raise StopConsumer()
CONN_LIST.remove(self)
- 前端javascript代码
//链接服务器
let websocket = new WebSocket('ws://127.0.0.1:8000/msg/');
//指定事件回调
websocket.onmessage = function(e){
//var data = JSON.parse(e.data);
//console.log(data);
console.log(e);
}
websocket.onopen = function(e){
console.log(e);
}
websocket.onerror = function(e){
console.log(e);
}
websocket.onclose = function(e){
console.log(e);
}
var interval = setInterval(function() {
websocket.send("你好好!!!");
}, 1000); //5秒循环调用执行remind()函数