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名下才能收到消息

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

相关推荐
网络安全-杰克10 分钟前
网络安全概论
网络·web安全·php
怀澈12214 分钟前
高性能服务器模型之Reactor(单线程版本)
linux·服务器·网络·c++
耗同学一米八44 分钟前
2024 年河北省职业院校技能大赛网络建设与运维赛项样题二
运维·网络·mariadb
skywalk81631 小时前
树莓派2 安装raspberry os 并修改成固定ip
linux·服务器·网络·debian·树莓派·raspberry
C++忠实粉丝1 小时前
计算机网络socket编程(3)_UDP网络编程实现简单聊天室
linux·网络·c++·网络协议·计算机网络·udp
黑客Ela1 小时前
网络安全中常用浏览器插件、拓展
网络·安全·web安全·网络安全·php
qdprobot2 小时前
ESP32桌面天气摆件加文心一言AI大模型对话Mixly图形化编程STEAM创客教育
网络·人工智能·百度·文心一言·arduino
hakesashou3 小时前
Python中常用的函数介绍
java·网络·python
C++忠实粉丝3 小时前
计算机网络socket编程(4)_TCP socket API 详解
网络·数据结构·c++·网络协议·tcp/ip·计算机网络·算法
九州ip动态3 小时前
做网络推广及游戏注册为什么要换IP
网络·tcp/ip·游戏