Django+websocket实现一个简单聊天

WebSocket是一种在单个TCP连接上进行全双工通信的协议。它由IETF在2011年定为标准RFC 6455,并由RFC7936补充规范,同时WebSocket API也被W3C定为标准。

1、定义与原理

WebSocket是独立的、创建在TCP上的协议,它使用HTTP/1.1协议的101状态码进行握手。为了创建WebSocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为"握手"(handshaking)。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。

2、特点

  1. 双向通信:WebSocket支持客户端和服务器之间的实时双向通信,而传统的HTTP协议是单向请求-响应模式。
  2. 实时性强:由于协议是全双工的,服务器可以随时主动给客户端下发数据,相对于HTTP请求需要等待客户端发起请求服务端才能响应,WebSocket的延迟明显更少。
  3. 保持连接状态:WebSocket需要先创建连接,因此是一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。
  4. 控制开销小:在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。相对于HTTP请求每次都要携带完整的头部,WebSocket的开销显著减少了。
  5. 支持二进制:WebSocket定义了二进制帧,可以更轻松地处理二进制内容。
  6. 支持扩展:WebSocket定义了扩展,用户可以扩展协议、实现部分自定义的子协议,如部分浏览器支持压缩等。
  7. 更好的压缩效果:相对于HTTP压缩,WebSocket在适当的扩展支持下,可以沿用之前内容的上下文,在传递类似的数据时,可以显著地提高压缩率。

3.案列

(1)先创建一个django项目(cmd创建或者直接pychram点击创建)

这里我们需要添加channels插件

pip install channels==3.0

在创建一个app用来访问页面,顺便创建需要用到的文件

(2)settings.py (配置一下环境)

在apps里面注册一下

在添加以下配置

python 复制代码
ASGI_APPLICATION = "websocket.asgi.application"//"websocket"是我的项目名


#channels自带的存储
CHANNEL_LAYERS = {
    "default":{
        "BACKEND": "channels.layers.InMemoryChannelLayer",
    }
}

运行检查一下,配置成功的话会有ASGI/Channels的标识(不对的话就切换成3.0的channels版本)

(3)项目目录下 asgi.py

这里主要是用来区分是http协议还是websocket协议

python 复制代码
import os

from channels.routing import ProtocolTypeRouter,URLRouter
from django.core.asgi import get_asgi_application
from websocket import routings

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'study_websocket.settings')

#application = get_asgi_application()
application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket":URLRouter(routings.websocket_urlpatterns),
})

(4) 项目目录routings.py

python 复制代码
from django.urls import re_path
from app import consumers

websocket_urlpatterns = [
    re_path(r'room/(?P<group>\w+)/$',consumers.ChatConsumer.as_asgi()),
]

(5)创建一个路由来访问聊天页面

url.py

python 复制代码
from django.contrib import admin
from django.urls import path
from app import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',views.index),
]

views.py

python 复制代码
from django.shortcuts import render

# Create your views here.


def index(request):
    num = request.GET.get('num')
    return render(request,"home.html",{"num":num})

tempaltes文件创建一个home.html

python 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .message {
            height: 500px;
            border: 1px solid #dddd;
            width: 100%;
        }
    </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="closeCount()">
</div>
</body>
<script>
    //创建连接
    socket = new WebSocket("ws://127.0.0.1:8000/room/{{ num }}/");

    //创建好连接之后自动触发(服务端执行self.accept())
    socket.onopen = function (event){
        let tag= document.createElement("div")
        tag.innerText = "[连接成功]";
        document.getElementById("message").appendChild(tag);
    }

    //当websocket接收到服务端发来的消息时,自动触发这个函数
    socket.onmessage = function (event){
        console.log(event.data)
        let tag= document.createElement("div")
        tag.innerText = event.data;
        document.getElementById("message").appendChild(tag);
    }

    function sendMessage(){
        let tag=document.getElementById("txt");
        socket.send(tag.value);
    }

    function closeCount(){
        socket.close()//关闭
    }
</script>
</html>

(6)consumers.py

python 复制代码
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连接请求时,自动触发
        #服务端允许客户端创建连接
        print("有人连接")
        self.accept()

        #获取群号
        group = self.scope['url_route']['kwargs'].get("group")
        #将这个客户端连接对象加入到内存
        #channel_layer.group_add这个方法默认是异步的所有需要async_to_sync转成同步
        async_to_sync(self.channel_layer.group_add)(group,self.channel_name)


     #浏览器基于websocket向后端发送数据,自动触发接收消息
    def websocket_receive(self, message):
        #获取群号
        group = self.scope['url_route']['kwargs'].get("group")
        #通知组内所有客户端,执行xx_oo方法,在此方法中可以自己定义任意功能
        async_to_sync(self.channel_layer.group_send)(group,{"type":"xx_oo","message":message})
    def xx_oo(self, event):
        text = event["message"]["text"]
        self.send(text)

    def websocket_disconnect(self, message):
        print("客户端断开连接")
        group = self.scope['url_route']['kwargs'].get("group")
        #客户端与服务器断开连接时自动触发
        async_to_sync(self.channel_layer.group_discard)(group,self.channel_name)
        raise StopConsumer()

效果:

同一个num名下才能收到消息

这里只实现了核心功能,具体的样式和效果可以各自发挥,适应各自的场景中。

相关推荐
星恒讯工业路由器8 小时前
5G FWA技术演进与趋势
网络·5g·信息与通信·4g·5g fwa·3gpp规范·fwa趋势
凌云C8 小时前
5G 协议体系与交互流程
网络协议
星夜夏空9911 小时前
网络编程(1)
服务器·网络
上海云盾-小余11 小时前
中小站点防护避坑:低价高防服务存在的各类安全短板剖析
网络·爬虫·安全·ddos
X7x513 小时前
跨厂商路由引入(重发布)实战指南:华为、华三、思科的逻辑差异与配置避坑
网络协议·信息与通信·信号处理·实施路由引入
数据知道13 小时前
邮件安全实战:SPF/DKIM/DMARC 配置与钓鱼绕过
网络·安全·web安全·网络安全·邮件安全
玩转4G物联网13 小时前
FS800DTU 新品上线|免费使用 DMP 设备管理平台,支持远程批量配置 & OTA 升级
物联网·网络协议·tcp/ip·云平台·核心板·fs800dtu·iot管理平台
王莎莎-MinerU14 小时前
MCP 解决的是工具接入,科研 Agent 还缺的是科学证据接口标准化
开发语言·网络·人工智能·深度学习·pdf·c#·php
Chloeis Syntax14 小时前
JAVAEE初阶 --- 构造HTTP请求
网络·网络协议·http·postman
砚凝霜14 小时前
软考网络工程师|第 1 章 计算机网络基础 完整备考笔记
网络·笔记·计算机网络