【全栈开发】---- 一文掌握 Websocket 原理,并用 Django 框架实现

目录

介绍

底层原理

握手环节详解:

收发数据(加密)

[Django 中配置 channels](#Django 中配置 channels)

[1、注册 channels](#1、注册 channels)

[2、在 settings.py 中添加 asgi_application](#2、在 settings.py 中添加 asgi_application)

[3、修改 asgi.py 文件](#3、修改 asgi.py 文件)

4、routing

5、consumers

[实现 聊天室](#实现 聊天室)


介绍

WebSocket是一种先进的通信协议,旨在通过单个长时间运行的连接实现实时双向数据交换,极大地提升了Web应用程序的交互性和响应速度。不同于传统的HTTP请求-响应模型,WebSocket在客户端与服务器之间开启了一个持久化的连接,使得双方可以随时发送文本或二进制数据,无需为每次通信重新建立连接,从而减少了延迟并提高了效率。这种特性特别适用于需要实时更新的应用场景,如在线游戏、即时通讯、金融交易监控等。WebSocket协议不仅简化了开发流程,还确保了更高效的数据传输,支持更高的并发量和更低的资源消耗,成为现代Web开发中不可或缺的技术之一。此外,随着互联网技术的发展,WebSocket正逐渐成为构建高度互动和动态Web应用的标准选择,为企业提供前所未有的用户体验改进机会。

底层原理

http 协议:

。连接

。数据传输(请求和响应)

。断开连接

websocket 协议:

。连接,客户端发起

。握手(验证),客户端发送一个消息,服务端接收到消息后做一些特殊处理并返回。服务端支持 websocket 协议

。收发数据(加密)

。断开连接

握手环节详解:

创建完链接后,客户端会自己生成一串随机字符串,并且加密后以密文的形式存储到客户端,以明文发送给服务端,服务端加密后发送给客户端,客户端比较自己加密算法得到的密文与服务端发送过来的密文是否相同,若相同,则服务端支持 websocket 协议,即握手成功,可以进行下一步操作,否则则不支持。

客户端发送给服务端的是这玩意:

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

...

...

\r\n\r\n

服务端真正接收的是 Sec``-``WebSocket``-``Key

mnwFxiOlctXFN``/``DeMt1Amg``=``=

然后将服务端将 Sec-WebSocket-Key 与 magic_string进行字符串拼接

**magic_string 有个固定值:**258EAFA5-E914-47DA-95CA-C5AB0DC85B11

再将拼接的字符串进行 hmac1 加密,再进行 base64 加密

服务端将最后加密得到的结果返还给客户端,返还新式如下:

"HTTP/1.1 101 Switching Protocols\r\n" \

"Upgrade:websocket\r\n" \

"Connection: Upgrade\r\n" \

"Sec-WebSocket-Accept: 最终密文

然后客户端将服务端发送的密文和客户端自己产生的密文进行比较,相同则牵手成功。

收发数据(加密)

收发数据加密我看不懂,也不想去懂,大家可以自己搜搜视频看看。

Django 中配置 channels

django 默认不支持 websocket ,需要安装组件:

pip install channels

配置

1、注册 channels

python 复制代码
INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "app01.apps.App01Config",
    "channels",
]

2、在 settings.py 中添加 asgi_application

python 复制代码
ASGI_APPLICATTON = "lang_poll.asgi.application"

这里的 lang_poll 是自己的 Django 项目名称

3、修改 asgi.py 文件

原来的 asgi.py 文件只能处理 http 请求,现在加入 websocket后需要修改:

python 复制代码
"""
ASGI config for lang_poll project.

It exposes the ASGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
"""

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", "lang_poll.settings")
# application = get_asgi_application()

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

4、routing

asgi.py 中的 routing 是自己编写的文件,相当于 http 中的 urls.py ,创建在 asgi.py 相同目录中:

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

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

5、consumers

这里的 consumers 也得自己创建,放在 app01 目录下,相当于 views.py ,用来编写 websocket 请求的响应逻辑:

python 复制代码
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer

class ChatConsumer(WebsocketConsumer):
    def websocket_connect(self,message):
        # 客户端发送 websocket请求时,会自动触发这个函数
        # accept 函数允许客户端与服务端建立联系
        self.accept()

    def websocket_receive(self,message):
        # 客户端向服务端基于 websocket发送数据时,会自动触发该函数,并接收消息
        self.send("不要回复")
        # self.close()  是服务端主动断开连接

    def websocket_disconnect(self,message):
        # 客户端与服务端断开连接时,自动触发
        print("断开连接")
        raise StopConsumer()

实现 聊天室

前端页面可就用之前轮询的:

python 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .message{
            width: 200px;
            height: 300px;
            border: 1px solid orangered;
        }
    </style>
</head>
<body>
<div class="message" id="message"></div>
<div class="input">
    <input type="text" placeholder="请输入" id="txt">
    <input type="button" value="发送">
</div>
</body>
</html>

因为博主最近事比较多,聊天功能就不实现了,请见谅,后期会补回来的。

相关推荐
菜菜小蒙3 分钟前
【Linux】http 协议
网络·网络协议·http
小柒的博客34 分钟前
TCP-UDP-WebSocket-组播-单播
websocket·tcp/ip·udp
钟离墨笺1 小时前
【Linux】【网络】UDP打洞-->不同子网下的客户端和服务器通信(未成功版)
linux·服务器·网络
黑客K-ing1 小时前
网络安全:利用 IP 查询构建网络安全系统的方法
网络·安全·web安全
嘿·嘘2 小时前
第五章 STM32 环形缓冲区
linux·服务器·网络
Alfred young2 小时前
CS144 Lab Checkpoint 0: networking warm up
c++·网络协议
Craaaayon2 小时前
Docker基础-自定义镜像与容器网络
java·运维·网络·数据库·后端·docker·容器
程序员黄同学2 小时前
请解释 HTTP 中的状态码,常见的状态码有哪些?
网络·网络协议·http
程序员黄同学3 小时前
请谈谈 HTTP 中的请求方法(GET、POST、PUT、DELETE等),它们的区别是什么?
网络·网络协议·http