由于http协议无状态,一次请求,一次响应。无法保持持续的连接,而websocket协议可以创建连接持久连续不断的连接,基于这个连接,可以持续收发数据。
常用场景:
web聊天室
实时监控平台,一些图表等。
原理:
建立在http协议之上
--连接,客户端发起
--握手,客户端发送消息,后端接收到消息后再做一些特殊的处理并返回。验证服务端支持websocket协议
在请求头添加几个特殊的请求头
Sec-Websocket-Version
Sec-Websocket-Key
Sec-Websocket-Extensions
服务端接收到后将v1 = Sec-Websocket-Key += magic String进行拼接
拼接完成后,再对拼接后的值进行hmac1加密 v2 = hmac1(Sec-Websocket-Key += magic String)
最后进行base64加密 v3 = base64(v2)
最后再将v3的值返回,放入如下的请求头中
Sec-Websocket-Accept:v3
--收发数据(加密)
b'1213;ddac;32dqf;432r;14q4aff'
--断开连接
----django使用websocket
django默认不支持websocket,需要安装组件channels --pip install channels==3.0.0 注意如果使用2.0的版本,该配置可能报错
---settings配置
---注册channels 注册到INSTALLED_APPS
--再setting中添加asgi_application
ASGI_APPLICATION = 'websocket_3.asgi.application'
--修改asgi.py文件
--在settings同级目录下创建文件routing.py
--在app03上创建consumer.py文件,并写一个ChatConsumer类,该类必须有以下三个方法
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
class ChatConsumer(WebsocketConsumer):
def websocket_connect(self,message):
# 有客户端来向服务端发送websocket连接请求,自动触发
self.accept()
#服务端允许和客户端创建连接
def websocket_receive(self,message):
#客户端基于websocket向后端发送数据,自动触发,用于接收客户端传过来的消息
self.send()
#用户向客户端发送消息
self.close()
#服务端主动断开连接
pass
def websocket_disconnect(self,message):
#客户端与服务端断开连接时,自动触发。
pass
def websocket_connect(self,message):
# 有客户端来向服务端发送websocket连接请求,自动触发
self.accept()
#服务端允许和客户端创建连接
-----------------------注意事项-------------------------------------
def websocket_receive(self,message):
#客户端基于websocket向后端发送数据,自动触发,用于接收客户端传过来的消息
self.send()
# #用户向客户端发送消息
# self.close()
# raise StopConsumer()
#如果服务端只单纯的用self.close(),关闭连接。由于是全双工模式,该方式会让两端全部断开,即也会自动执行websocket_disconnect函数
#如果self.close()后面接raise StopConsumer(),只是服务端主动与客户端的连接断开,不会执行websocket_disconnect函数
def websocket_disconnect(self,message):
#客户端与服务端断开连接时,自动触发。
raise StopConsumer()
----聊天室案例
前端
<html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.js"></script> <style> .msg{ height: 500px; border: 1px solid #ddd; width: 100%; } </style> </head> <body> <div class="msg"></div> <div> <input type="text" placeholder="请输入" id="text"> <input type="button" value="发送" onclick="sendMsg();"> </div> </body> <script> var socket = new WebSocket("ws://127.0.0.1:8000/room/{{ num }}/"); //当与服务端建立连接后,自动触发 socket.onopen = function (event) { var tag = $("<div>"); tag.text('连接成功'); //创建一个标签<div></div> $(".msg").append(tag); //将标签添加到页面 } function sendMsg(){ var text = $("#text").val(); socket.send(text) //利用soket给服务端发送消息 } //回调函数,当服务端给客户端发送消息时,自动触发 socket.onmessage = function (event) { console.log(event.data) //获取服务端返回的数据 var tag = $("<div>"); tag.text(event.data); //创建一个标签<div></div> $(".msg").append(tag); //将标签添加到页面 console.log(event.data) } </script> </html>
后端代码
from channels.generic.websocket import WebsocketConsumer from channels.exceptions import StopConsumer from asgiref.sync import async_to_sync class ChatConsumer(WebsocketConsumer): def websocket_connect(self,message): # 有客户端来向服务端发送websocket连接请求,自动触发 self.accept() #服务端允许和客户端创建连接 group_num = self.scope['url_route']['kwargs'].get('group') #获取路由的组名 print('有人连接到--',group_num) async_to_sync(self.channel_layer.group_add)(group_num,self.channel_name) # 将与这个客户端连接放入到channel_layer的组里,组名为group_num # channel_layer该方法只支持异步,所以用async_to_sync转为同步写法 def websocket_receive(self,message): print(message) #返回一个字典 text = message['text'] #获取发送过来的数据 group_num = self.scope['url_route']['kwargs'].get('group') #获取路由的组名 # 通知组内所有的客户端,执行send_msg方法。我们在此方法中可以定义任何功能 async_to_sync(self.channel_layer.group_send)(group_num,{'type':'send.msg','message':text}) def websocket_disconnect(self,message): #客户端与服务端断开连接时,自动触发。 group_num = self.scope['url_route']['kwargs'].get('group') #获取路由的组名 print('有人断开连接了---') async_to_sync(self.channel_layer.group_discard)(group_num,self.channel_name) # 将与该客户端的连接从channel_layer的组中移除 raise StopConsumer() def send_msg(self,msg): print(msg) text = msg['message'] self.send(text) #将消息发送给所有客户端
标签:group,self,websocket,message,服务端,客户端 From: https://www.cnblogs.com/powfu/p/16901864.html