学习视频:
14-11 群聊(一)_哔哩哔哩_bilibili
目次
Websocket 连接不了?
收发数据
断开连接
完备代码
聊天室的实现
聊天室一
聊天室二
settings 配置
consumer 配置
多聊天室
Websocket 连接不了?
基于这篇博客:
【全栈开发】---- 一文把握 Websocket 原理,并用 Django 框架实现_django websocket-CSDN博客
之前这篇博客固然大致原理都先容了,但终极的代码并没有实现,这是因为博主当时遇见了一个题目,尽管我按照教程来的,但是 websocket 服务就是连不上,背面也参考了许多博客,也去官网看了,还去 github 上抄项目来对比,都办理不了,厥后急得我转 SpringBoot 去了。但偶尔间发现了这篇博客:
https://blog.csdn.net/qq_25218219/article/details/131752459Django的websocket
终极题目才得以办理,再次感谢这位博主!!!
办理办法很简单,基于上面学习视频的配置后,需要在注册组件的 “channels” 前面添加一个组件 "daphne"
- INSTALLED_APPS = [
- "daphne",
- "channels",
- "django.contrib.admin",
- "django.contrib.auth",
- "django.contrib.contenttypes",
- "django.contrib.sessions",
- "django.contrib.messages",
- "django.contrib.staticfiles",
- "app01.apps.App01Config"
- ]
复制代码 然后运行就能连上 asgi 了:
大致缘故原由是 pip install channels 按照命令默认按照的是最新版的 channels ,可能与 Django 版本并不匹配。
收发数据
在 websocket 模式中,服务端和客户端都能主动收发数据:
在客户端发数据:
- function sendMessage(){
- var txt = document.getElementById("txt")
- console.log(txt.value)
- socket.send(txt.value)
- }
复制代码 在服务端收数据:
- def websocket_receive(self, message):
- # 收数据message
- print("接收消息-->",message["text"])
复制代码 在服务端发数据:
利用 send() 方法即可
- def websocket_connect(self,message): print("发送连接哀求") self.accept() # 发数据 self.send("来了呀客官") def websocket_receive(self, message):
- # 收数据message
- print("接收消息-->",message["text"]) self.send(message["text"])
复制代码 在客户端收数据:
这里的服务端发数据在发送 websocket 连接函数和吸收消息函数中都可,相对于,在客户端收数据也对应两种方法,一个是 socket.onopen ,创建好连接后自动触发(握手环节,服务端执行self.accept());另有一个就是 socket.onmessage ,用于正常吸收数据。
- socket.onopen = function(event){
- console.log(event.value)
- let lag = document.createElement("div")
- lag.innerText = "[websocket连接成功]"
- document.getElementById("message").appendChild(lag)
- }
- {#收数据#}
- socket.onmessage = function (event){
- var data = event.data
- console.log("客户端接收到消息-->",data)
- let lag = document.createElement("div")
- lag.innerText = data
- document.getElementById("message").appendChild(lag)
- }
复制代码 断开连接
在服务端断开连接一样平常是颠末下面这个函数:
- def websocket_disconnect(self, message):
- # 浏览器关闭也会自动发送断开链接请求
- print("断开连接")
- # 服务端同意断开连接
- raise StopConsumer()
复制代码 这个函数不仅仅关闭欣赏器的哀求链接,还会关闭服务端链接,实现完全断连。在类中其他函数中可利用 self.close() 来调用此关闭链接函数,实现完全断连;而假如用 raiseStopConsumer() ,则表示仅仅断开服务器连接,也不会执行 websocket_disconnect 函数。
服务器断开连接时,客户端也会触发一个函数:
- socket.onclose = function (event){
- }
复制代码 而且客户端也可以设置按钮,主动断开连接:
- function closeOnn(){
- socket.close()
- }
复制代码
完备代码
index.html:
- <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title> <style> .message{ height: 300px; width: 100%; border: 1px solid #dddddd; } </style></head><body><div class="message" id="message"></div><div> <input type="text" placeholder="请输入" id="txt"> <input type="button" value="发送" onclick="sendMessage()"> <input type="button" value="断开连接" onclick="closeOnn()"></div><script> socket = new WebSocket("ws://127.0.0.1:8080/room/123/") {#创建好连接后自动触发(握手环节,服务端执行self.accept())#} socket.onopen = function(event){ console.log(event.value) let lag = document.createElement("div") lag.innerText = "[websocket连接乐成]" document.getElementById("message").appendChild(lag) } {#发数据#} function sendMessage(){
- var txt = document.getElementById("txt")
- console.log(txt.value)
- socket.send(txt.value)
- } {#收数据#} socket.onmessage = function (event){ var data = event.data console.log("客户端吸收到消息-->",data) let lag = document.createElement("div") lag.innerText = data document.getElementById("message").appendChild(lag) } {#服务器主动断开连接,触发#} socket.onclose = function (event){
- } function closeOnn(){
- socket.close()
- }</script></body></html>
复制代码 consumers.py:
- from channels.generic.websocket import WebsocketConsumerfrom channels.exceptions import StopConsumer# socket = new WebSocket("ws://127.0.0.1:8000/room/123/")class ChatConsumer(WebsocketConsumer): print("进入消费者") def websocket_connect(self,message): print("发送连接哀求") self.accept() # 发数据 self.send("来了呀客官") def websocket_receive(self, message):
- # 收数据message
- print("接收消息-->",message["text"]) # 服务器主动断开连接 if message["text"] == "关闭": self.close() # 假如在这儿加上下面代码,执行StopConsumer异常,那么就不会执行websocket_disconnect raise StopConsumer() # return self.send(message["text"]) # 调用self.close()方法默认都会调用下面这个函数 def websocket_disconnect(self, message):
- # 浏览器关闭也会自动发送断开链接请求
- print("断开连接")
- # 服务端同意断开连接
- raise StopConsumer()
复制代码 聊天室的实现
固然,上面只是先容 websocket 的一样平常利用,还并没有现实应用,下面将以聊天室场景举行应用。
聊天室一
前面的操作都是基于 self 来的。服务端仅仅关心本身与对应欣赏器的连接通道,而不会接洽到其它欣赏器。可利用列表存储各个用户,某用户想断开连接或者主动退出欣赏器时,再到列表中删除用户:
需要注意的是,用户添加到列表中后,后续的一系列操作需要在列表中循环操作每一个对象,以实现群聊
- from channels.generic.websocket import WebsocketConsumer
- from channels.exceptions import StopConsumer
- CONN_LIST = []
- class ChatConsumer(WebsocketConsumer):
- print("进入消费者")
- def websocket_connect(self,message):
- print("发送连接请求")
- self.accept()
- CONN_LIST.append(self)
- def websocket_receive(self, message):
- res = message["text"]
- # 收数据message
- print("接收消息-->",res)
- for conn in CONN_LIST:
- conn.send(res)
- # 调用self.close()方法默认都会调用下面这个函数
- def websocket_disconnect(self, message):
- # 浏览器关闭也会自动发送断开链接请求
- print("断开连接")
- CONN_LIST.remove(self)
- # 服务端同意断开连接
- raise StopConsumer()
复制代码 效果:
聊天室二
聊天室一固然能实现简单的群聊功能,但是利用列表来储存各个用户,其实效率会很低,而且功能也不强盛,Django 的 channels 组件中有一个更加锋利的东西叫 channel layers,可以帮助我们更加方便地去实现这种群聊。
参考文章:django channels - 武沛齐 - 博客园
settings 配置
layers 需要在 setting 中举行配置:
- CHANNEL_LAYERS = {
- "default": {
- "BACKEND": "channels.layers.InMemoryChannelLayer",
- }
- }
复制代码 consumer 配置
再修改 Consumer 代码:
- from channels.generic.websocket import WebsocketConsumer
- from channels.exceptions import StopConsumer
- from asgiref.sync import async_to_sync
- class ChatConsumer(WebsocketConsumer):
- print("进入消费者")
- def websocket_connect(self,message):
- print("发送连接请求")
- self.accept()
- # 将这个客户端的连接对象加入到某个地方(内存或redis),channel_name 随机给个名字
- async_to_sync(self.channel_layer.group_add)("111",self.channel_name)
- def websocket_receive(self, message):
- res = message["text"]
- # 收数据message
- print("接收消息-->",res)
- async_to_sync(self.channel_layer.group_send)("111",{"type":"send_to","message":message})
- def send_to(self,event):
- # 群中每一个连接对象都发送
- text = event["message"]["text"]
- self.send(text)
- # 调用self.close()方法默认都会调用下面这个函数
- def websocket_disconnect(self, message):
- # 浏览器关闭也会自动发送断开链接请求
- print("断开连接")
- async_to_sync(self.channel_layer.group_discard)("111",self.channel_name)
- # 服务端同意断开连接
- raise StopConsumer()
复制代码 部门解释:
需要注意的是,这里的 channel_layer 操作都是异步举行的,需要本身导入 async_to_sync 举行异步转同步操作。
- async_to_sync((self.channel_layer.group_add)("111",self.channel_name)
复制代码 这里的作用是将本连接对象存入 channel_layer 中,而且 group 名为 "111" ,self.channel_name 的作用是连接对象存储时,随机给一个名字。
- async_to_sync(self.channel_layer.group_send)("111",{"type":"send_to","message":message})
复制代码- def send_to(self,event):
- # 群中每一个连接对象都发送
- text = event["message"]["text"]
- self.send(text)
复制代码 这里的作用是为 "111" 群聊中每个连接对象执行 type 对应的方法,并传入 message 给每个连接对象;下面的 send_to 方法就是为每一个连接对象发送 text 消息。
- async_to_sync(self.channel_layer.group_discard)("111",self.channel_name)
复制代码 这里的作用是为群聊中的每一个连接对象关闭连接。
上诉代码已能实现聊天室功能,但还不敷高级,因为群聊 id 是固定的。下面先容在欣赏器中打开多个聊天室,各个聊天室之间有不同的 id ,各个聊天室之前互不干扰。
多聊天室
实现思路是通过 http 的 get 传参将群号传给视图函数,视图函数给 index.html 页面,在 index 页面构造 websocket url 并参加群号,在 consumer 中获取群号,并更换群号为原先的固定群号。
实现:
视图函数传参:
- def index(request):
- QQ_number = request.GET.get('qq')
- return render(request, 'index.html', {'QQ_number': QQ_number})
复制代码 index 页面 websocket 传参:
- socket = new WebSocket("ws://127.0.0.1:8080/room/{{ QQ_number }}/")
复制代码 routings 中正则吸收参数:
- websocket_urlpatterns = [
- re_path(r'^room/(?P<group>\w+)/$', consumers.ChatConsumer.as_asgi()),
- ]
复制代码 consumer 中吸收 group 并修改群号为 group:
- from channels.generic.websocket import WebsocketConsumer
- from channels.exceptions import StopConsumer
- from asgiref.sync import async_to_sync
- class ChatConsumer(WebsocketConsumer):
- print("进入消费者")
- def websocket_connect(self,message):
- print("发送连接请求")
- self.accept()
- group = self.scope['url_route']['kwargs'].get('group')
- # 将这个客户端的连接对象加入到某个地方(内存或redis),channel_name 随机给个名字
- async_to_sync(self.channel_layer.group_add)(group,self.channel_name)
- def websocket_receive(self, message):
- group = self.scope['url_route']['kwargs'].get('group')
- res = message["text"]
- # 收数据message
- print("接收消息-->",res)
- async_to_sync(self.channel_layer.group_send)(group,{"type":"send_to","message":message})
- def send_to(self,event):
- # 群中每一个连接对象都发送
- text = event["message"]["text"]
- self.send(text)
- # 调用self.close()方法默认都会调用下面这个函数
- def websocket_disconnect(self, message):
- group = self.scope['url_route']['kwargs'].get('group')
- # 浏览器关闭也会自动发送断开链接请求
- print("断开连接")
- async_to_sync(self.channel_layer.group_discard)(group,self.channel_name)
- # 服务端同意断开连接
- raise StopConsumer()
复制代码 效果:

这样即能实现多聊天室,各个聊天室互不打搅。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |