【项目组件】第三方库——websocketpp

目录

第三方协议:websocket

websocket简介

websocket特点 

websocket协议切换

 websocket协议格式段

websocketpp库介绍

endpoint

server

 connection 

websocketpp库搭建服务器流程 

基本框架实现 

业务处理回调函数的实现

http_callback 

open_callback

close_callback

message_callback 


第三方协议:websocket

websocket简介

为什么要有websocket? 

websocket协议是应用层协议之一,既然这种协议会出现,那么也就意味着它是在某些特殊场景下弥补了一些其他应用层协议的缺点。

所以关于为什么要有websocket协议,我们首先要聊聊最常见的协议http/https,它们没有解决的一些问题!

我们都知道,http协议是如何建立起连接的呢?

  • http协议规定,首先由客户端向服务器发送一个http请求, 服务器收到后,根据客户端的http请求,返回给客户端一个http应答!
  • 补充:来回一次报文发送,http双方通信连接就会被关闭,这也意味着http协议在通常情况下是无连接或短连接的协议!

http协议上述建立连接的方式带来了一个问题:必须由客户端主动向服务器发送请求报文,服务器无法主动给客户端发送消息!这也就意味着http协议是一个单向通信的协议

而网页的即时聊天或者像我们接下来做的项目“五子棋游戏”这样的程序都是非常依赖服务器给客户端发送消息的!

若我们想要用http协议来完成这个功能,那么只能是客户端等待一段时间之后,主动向服务器询问是否有消息。也就是基于轮询的策略。

轮询策略带来了几个弊端:

  • 消息不够实时:客户端是按一定的时间间隔向服务器发送轮询的,那么从消息准备就绪到服务器收到轮询之间肯定有一定的时间间隔。这样的话就影响了整体的效率!
  • 成本过高:轮询意味着客户端需要不断发送请求到网络中,而大部分的请求都是无意义的,即服务器还未准备就绪的。

基于上述的种种,最终产生了websocket协议,很明显,websocket协议是为了解决http协议的弊端,这两种协议具有强相关。所以后面我们说websocket协议通常是由http协议切换过来的!


websocket特点 

websocket协议与http协议相比,主要具有以下特点:

  • websocket协议支持双向通信,即服务器可以主动给客户端发送消息,客户端也可以主动给服务器发送消息
  • websocket协议是一种长连接协议,通常与TCP协议相同,websocket会为通信双方建立长时间的连接,使得通信双方通信时不再需要频繁建立连接。这降低了延迟,提高了实时性,使得数据可以更快地传输到客户端。

websocket协议切换

websocket协议是由http协议切换过来的,整体切换如下图:

整体协议切换一共分为三步:

  1. 第一步:TCP三次握手建立连接         
  2. 第二步:通过http报文由http协议切换到websocket协议
  3. 第三步:websocket协议格式通信

 TCP三次握手建立连接 

不管是websocket协议还是http协议,都是基于TCP协议的应用层协议。

所以对于websocket协议来说,它需要为通信双方建立一个长连接,最好的方式就是TCP的方式


http协议切换至websocket协议

协议的切换,是通过http报文的方式实现的。即客户端向服务器发送http请求,服务器给客户端http应答,之后进行协议切换。

但不同的是,协议切换的请求和应答的格式不再与普通http报文格式相同

协议切换请求报文的格式:

  • "GET /ws HTTP/1.1":该行表示GET方法,ws是websocket的缩写
  • "Connection:Upgrade":该行表示客户端希望升级当前的HTTP连接到一个新的协议
  • "Upgrade:WebSocket":该行表示升级的协议名称为WebSocket
  • "Sec-WebSocket-Version:xxx":该行表示升级的协议版本为xxx
  • "Sec-WebSocket-Key:xxx":是一个由客户端(通常是浏览器)随机生成的Base64编码值。这个值在WebSocket握手请求中发送给服务器,用于确保客户端和服务器之间的连接是安全的,并且不是由恶意软件或未授权的第三方建立的。

协议切换应答报文的格式:

  • "HTTP/1.1 101 xxx":其中101是响应状态码,即告诉客户端支持WebSocket协议
  • "Connection:Upgrade":与请求报文对应字段含义相同
  • "Upgrade:WebSocket":与请求报文对应字段含义相同
  • "Sec-WebSocket-Accept":Sec-WebSocket-Accept是服务器在WebSocket握手过程中,根据客户端发送的Sec-WebSocket-Key字段,通过一定的算法计算并返回给客户端的一个响应头字段。它用于验证服务器是否理解并接受客户端发起的WebSocket连接请求,同时也作为WebSocket连接安全性的一个基本保障。

接下来的内容,就是对websocket协议格式段进行介绍!


 websocket协议格式段

websocket协议格式段主要如下图:

FIN(1bit)

  • WebSocket支持将长消息切割成若干帧发送,切分后,前边的帧的FIN字段均为0,最后一个帧的FIN为1。
  • 当消息没有分段时,FIN标志位为1。

RSV1-3(各1bit)

  • 保留位,一般情况下为全0。
  • 当客户端、服务端协商采用WebSocket扩展时,这三个标志位可以非0,且值的含义由扩展进行定义。
  • 如果出现非0值但并未采用WebSocket扩展,连接会出错。

Opcode(4bit)

主要用于指定帧类型,可以指定的帧类型有以下几种:

  • %x0:表示一个延续帧。当Opcode为0时,表示本次数据传输采用了数据分片,当前收到的数据帧为其中一个数据分片。
  • %x1:表示这是一个文本帧。
  • %x2:表示这是一个二进制帧。
  • %x3-7:保留的操作代码,用于后续定义的非控制帧。
  • %x8:表示连接断开。
  • %x9:表示这是一个ping操作。
  • %xA:表示这是一个pong操作。
  • %xB-F:保留的操作代码,用于后续定义的控制帧。

注意:尽管帧类型有很多,但我们经常用的主要是文本帧与二进制帧! 


Payload数据 

  • 实际的有效数据载荷部分。
  • 如果通信双方约定使用了WebSocket扩展,则扩展数据也存放于此,并声明扩展长度。
  • 如果没有约定使用,则扩展数据为0字节。

Payload长度 

Payload长度记录的是Payload数据的长度!单位字节

在协议格式中,有四个Payload长度,它们对应着三种不同的场景:

  • 7bitsPayload长度若<126,那么该Payload长度表示的就是有效载荷的长度(0-126字节)
  • 7bitsPayload长度若=126,那么后两个字节(16bitsPayload长度)表示的就是有效载荷的长度(0-65535字节)
  • 7bitsPayload长度若=126,那么后八个字节(16+32+16bitsPayload长度)表示的就是有效载荷的长度(0-2^64-1字节)

Mask(1bit)与Mask-Key(可选) 

Mask表示Payload数据是否被编码,若为1则必有Mask-Key,⽤于解码Payload数据。仅客户端发送给服务端的消息需要设置。

  • 若Mask标志位为1,那么Mask-Key(4bits)一定被设置
  • 若Mask标志位为0,那么Mask-Key未被设置

Mask-Key:

  • 当Mask为1时存在,长度为4字节
  • 解码规则:DECODED[i] = ENCODED[i] ^ MASK[i % 4] 

websocketpp库介绍

WebSocketpp是⼀个跨平台的开源(BSD许可证)头部专⽤C++库,它实现了RFC6455(WebSocket 协议)和RFC7692(WebSocketCompression Extensions)。它允许将WebSocket客户端和服务器功能集成到C++程序中。在最常见的配置中,全功能⽹络I/O由Asio⽹络库提供。

如下为它的基本定义,混个眼熟就好~,后续用了自然就理解了

webscoketpp库


endpoint

 endpoint中提供了一些供我们使用的方法,并且endpoint就是服务器和客户端建立连接时的一个端点。endpoint屏蔽了底层网络通信的细节,依赖于boost库中的Asio对底层网络通信的具体实现

具体来说,endpoint类提供了如下类型的接口:

  • 日志相关接口
  • 回调函数相关接口
  • 通信连接相关接口
  • 其他服务器搭建的接口

日志相关接口 

设置日志输出等级:

void set_access_channels(log::level channels);   /*设置⽇志打印等级*/

 输出等级分为如下:

注意:websocketpp日志输出较为繁杂,后续我们直接设置为none,表示不输出日志即可!

其他日志接口由于不使用,不再过多介绍!


回调函数相关接口 

websocketpp的回调思想:针对特定的事件可以进行设置它的处理函数指针。

websocketpp搭建了服务器之后,给不同的事件设置了不同的处理函数指针,这些指针可以指向指定的函数,当服务器收到了指定的数据,触发了指定的事件后就会通过函数指针去调用这些函数,这时候,我们程序员就可以编写一些业务处理函数,将其设置为对应事件的业务处理函数!

例如:五子棋游戏中,当一名用户想进入到某个游戏房间时,该用户会向服务器发送websocket连接请求,websocket握手连接建立成功,该用户进入了房间的消息应该转发给房间内的所有成员!对于这种情况,我们修改握手成功的回调即可!

websocketpp提供了以下事件的回调:

  • set_open_handler:设置websocket协议握手成功的回调函数
  • set_close_handler:设置websocket连接断开的回调函数
  • set_message_handler:设置websocket消息处理函数
  • set_http_handler:设置http请求的处理函数

通信连接相关接口 

send:给客户端发送消息

close:关闭连接

get_con_from_hdl:通过connection_hdl获取对应的connection_ptr

  • connection_hdl:是一个用于引用和操作WebSocket连接的句柄,而不是连接实例本身。它是WebSocket++库提供的一种机制,允许用户在不直接访问连接实例的情况下与连接进行交互。
  • connection_ptr:具体的连接对象,是一个智能指针类型,当连接被关闭时会自动释放该连接。除此之外,也能通过该类型,直接访问连接的执行方法

其他服务器搭建接口 

init_asio:初始化asio框架,websocketpp网络通信底层依赖的就是这个框架

set_reuse_addr:设置是否启动地址重用

listen:设置绑定监听套接字

run:启动服务器

set_timer:设置定时任务


server

server继承自endpoint,而它自己的接口仅有一个start_accept

start_accept:初始化并启动服务端监听连接的accept事件处理


 connection 

connection是连接管理类,它是对asio中的底层连接进行再封装


以上就是websocketpp库中的基本介绍! 


websocketpp库搭建服务器流程 

使用websocketpp库搭建一个服务器最主要的逻辑如下:

  1. 实例化server对象
  2. 设置日志输出等级
  3. 初始化asio框架中的调度器
  4. 设置业务处理回调函数(具体业务处理的函数由我们自己实现)
  5. 设置服务器监听端口
  6. 开始获取新建连接
  7. 启动服务器

基本框架实现 


搭建服务器的前置准备

首先是包含websocketpp服务器对应的头文件和asio框架的头文件:

#include <websocketpp/config/asio_no_tls.hpp> //asio框架头文件
#include <websocketpp/server.hpp> //

注意:asio框架头文件我们采用asio_no_tls.hpp,不采用asio.hpp。两者的区别是asio_no_tls.hpp不支持TLS功能,TLS是一种用于在两个通信应用程序之间提供保密性和数据完整性的协议。


使用websocketpp中的server实例化对象时,需要传入一个底层网络通信模板参数。我们采用的时asio作为模板参数传入,同时为了使得代码简短,可以对它进行typedef

typedef websocketpp::server<websocketpp::config::asio> wsserver_t;

实例化server对象 

//1、实例化server对象
wsserver_t svr;

设置日志输出等级

由于websocketpp自带的日志输出内容非常多,不便于观察,我们不使用它的日志,把日志输出等级设置为none即可

//2、设置日志输出等级
svr.set_access_channels(websocketpp::log::alevel::none);

初始化asio框架中的调度器

//3、初始化asio框架
svr.init_asio();

设置业务处理回调函数

业务处理回调函数一共有4个 

  • set_open_handler:设置websocket协议握手成功的回调函数
  • set_close_handler:设置websocket连接断开的回调函数
  • set_message_handler:设置websocket消息处理函数
  • set_http_handler:设置http请求的处理函数

这四个函数的函数原型如下:

typedef lib::function<void(connection_hdl)> open_handler;
typedef lib::function<void(connection_hdl)> close_handler;
typedef lib::function<void(connection_hdl)> http_handler;
typedef lib::function<void(connection_hdl, message_ptr)> message_handler;void set_open_handler(open_handler h);       /*websocket握⼿成功回调处理函数*/
void set_close_handler(close_handler h);     /*websocket连接关闭回调处理函数*/
void set_message_handler(message_handler h); /*websocket消息回调处理函数*/
void set_http_handler(http_handler h);       /*http请求回调处理函数*/

为了后续操作方便,我们回调函数的参数中传入一个server对象,使用bind把这个server对象绑定回调函数,生成一个新的可调用对象传入给这四个函数的参数即可!

void http_callback(wsserver_t* svr,websocketpp::connection_hdl)
{}
void open_callback(wsserver_t* svr,websocketpp::connection_hdl)
{}
void close_callback(wsserver_t* svr,websocketpp::connection_hdl)
{}
void message_callback(wsserver_t* svr,websocketpp::connection_hdl hdl,wsserver_t::message_ptr msg)
{}//4、设置业务处理回调函数
svr.set_close_handler(std::bind(close_callback,&svr,std::placeholders::_1));
svr.set_open_handler(std::bind(open_callback,&svr,std::placeholders::_1));
svr.set_http_handler(std::bind(http_callback,&svr,std::placeholders::_1));
svr.set_message_handler(std::bind(message_callback,&svr,std::placeholders::_1,std::placeholders::_2));

 设置服务器监听端口

//5、设置服务器监听端口
svr.listen(8888);

 开始获取新建连接与启动服务器

//6、开始获取新建连接
svr.start_accept();
svr.run();

业务处理回调函数的实现

通过上述的几个步骤,我们的服务器的框架已经被搭建好了,接下来处理4个回调方法中的实现即可


http_callback 

 对于http_callback,我们要实现的是给客户端返回一个Hello World的页面

大体上,一共分为两步:

  1. 处理来自客户端的http请求
  2. 构建并发送http应答给客户端

1、处理来自客户端的http请求 

http请求中,最为关键的几个要素:请求方法、请求正文、uri

接下来处理http请求就是我们把这几个关键要素获取下来,并打印!

我们首先获取http请求中的body,在websocketpp库中,connection类提供了获取body的这个方法!

接下来的问题是如何获取connection类对象呢?

实际上,websocketpp库中endpoint类提供了一个方法get_con_from_hdl,即通过一个connection_hdl对象获取一个connection_ptr对象,connection_ptr指向的内容就是connection对象

在websocketpp库中,http命名空间下的parser命名空间下的request类提供了获取请求方法与uri的方法

接下来的问题是如何获取request类对象?

实际上,connection对象中提供了get_request方法用于获取一个request对象


2、构建并发送http应答给客户端

构建http应答一共经历如下几个步骤:

  1. 构建应答正文(Hello World 页面)
  2. 设置应答正文
  3. 添加Content-Type为"text/html"
  4. 设置状态码为ok

其中,我们可以使用string类型构建应答正文 。剩余的方法connection类中都提供了


代码

void http_callback(wsserver_t* svr,websocketpp::connection_hdl hdl)
{//1、处理http请求wsserver_t::connection_ptr conn = svr->get_con_from_hdl(hdl);std::cout << "body:" << conn->get_request_body() << std::endl;websocketpp::http::parser::request req = conn->get_request();std::cout << "method:" << req.get_method() << std::endl;std::cout << "uri:" << req.get_uri() << std::endl;//2、构建并发送http应答给客户端std::string body = "<html><body><h1>Hello World</h1></body></html>";conn->set_body(body);conn->append_header("Content-Type","text/html");conn->set_status(websocketpp::http::status_code::ok);
}

open_callback

对于该回调,无其他特殊需求,直接打印一行用于观察即可!

void open_callback(wsserver_t* svr,websocketpp::connection_hdl)
{std::cout << "websocket握手成功!" << std::endl;
}

close_callback

与open_callback同理

void close_callback(wsserver_t* svr,websocketpp::connection_hdl)
{std::cout << "连接关闭!" << std::endl;
}

message_callback 

message_callback被回调时,一定是服务器收到了来自客户端的websocket格式的消息。也就是message_callback的msg参数

为方便测试,我们实现的是服务器把客户端发来的消息原封不动返回

主要完成两个工作:

  • 构建回复消息
  • 发送消息

构建回复消息我们可以采用message类中的get_payload接口

发送消息我们使用connection对象中的send接口

send接口需要传入一个字符串与帧格式

帧格式在websocketpp::frame::opcode中,我们采用的是为text(文本)帧,缺省参数也为文本帧!

void message_callback(wsserver_t* svr,websocketpp::connection_hdl hdl,wsserver_t::message_ptr msg)
{wsserver_t::connection_ptr conn = svr->get_con_from_hdl(hdl);std::cout << "client say: " << msg->get_payload() << std::endl;std::string rep = "server say: " + msg->get_payload();conn->send(rep);}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/471489.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux—进程学习-02

目录 Linux—进程学习—21.通过系统调用创建进程—fork1.1fork创建子进程1.2fork函数的返回值1.3利用fork实现多进程 2.有关cpu的常识了解3.进程状态3.1从操作系统层面了解进程状态3.1.1就绪和新建状态的理解3.1.2运行和阻塞状态的理解3.1.3挂起状态的理解挂起和阻塞的区别 3.1…

基于yolov8、yolov5的鱼类检测识别系统(含UI界面、训练好的模型、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8、yolov8 SE注意力机制 或 yolov5、yolov5 SE注意力机制 &#xff0c; 直接提供最少两个训练好的模型。模型十分重要&#xff0c;因为有些同学的电脑没有 GPU&#xff0…

【数据价值化】国有企业数据资产入表及估值实践指南:挖掘数字资产新价值

在当今数字化转型的浪潮中&#xff0c;国有企业作为国民经济的支柱&#xff0c;正积极探索如何更好地管理和利用自身的数据资产。数据&#xff0c;这一新时代的“石油”&#xff0c;正逐渐成为驱动国有企业创新发展的关键要素。然而&#xff0c;如何准确地将数据资产纳入企业财…

AndroidStudio-常用布局

一、线性布局LinearLayout 线性布局内部的各视图有两种排列方式: 1.orientation属性值为horizontal时&#xff0c;内部视图在水平方向从左往右排列。 2.orientation属性值为vertical时&#xff0c;内部视图在垂直方向从上往下排列。 如果不指定orientation属性&#xff0c;…

力扣-Mysql-3308- 寻找表现最佳的司机(中等)

一、题目来源 3308. 寻找表现最佳的司机 - 力扣&#xff08;LeetCode&#xff09; 二、数据表结构 表&#xff1a;Drivers ----------------------- | Column Name | Type | ----------------------- | driver_id | int | | name | varchar | | age …

【汇编语言】包含多个段的程序(二)—— 将数据、代码、栈放入不同的段

文章目录 前言1. 存在的两个问题2. 解决办法3. 示例代码3.1 程序说明3.1.1 定义多个段的方法3.1.2 对段地址的引用3.1.3 各种段完全是我们的安排 4. 总结结语 前言 &#x1f4cc; 汇编语言是很多相关课程&#xff08;如数据结构、操作系统、微机原理&#xff09;的重要基础。但…

Iotop使用

文章目录 Iotop依赖及编译1:内核配置2: 环境配置3.依赖库ncurses3.1 Ncurses的编译配置 4. Iotop的编译及修改5.测试效果如下&#xff1a; Iotop依赖及编译 源码路径&#xff1a;https://github.com/Tomas-M/iotop#how-to-build-from-source (GitHub - Tomas-M/iotop: A top u…

LaTeX之四:如何兼容中文(上手中文简历和中文论文)、在win/mac上安装新字体。

改成中文版 如果你已经修改了.cls文件和主文档&#xff0c;但编译后的PDF仍然显示英文版本&#xff0c;可能有以下几个原因&#xff1a; 编译器问题&#xff1a;确保你使用的是XeLaTeX或LuaLaTeX进行编译&#xff0c;因为它们对Unicode和中文支持更好。你可以在你的LaTeX编辑器…

java的JJWT 0.91在jdk21中报错的解决方法

参考了很多其他人的办法&#xff0c;只有这种方式可以解决问题 JSON Web Token&#xff08;缩写 JWT&#xff09; 目前最流行、最常见的跨域认证解决方案&#xff0c;前端后端都需要会使用的东西 如果根据黑马的视频&#xff0c;导入了阿里云OSS的相关依赖&#xff0c;自然不会…

卷积、频域乘积和矩阵向量乘积三种形式之间的等价关系与转换

线性移不变系统 线性移不变系统&#xff08;Linear Time-Invariant System, LTI系统&#xff09;同时满足线性和时不变性两个条件。 线性&#xff1a;如果输入信号的加权和通过系统后&#xff0c;输出是这些输入信号单独通过系统后的输出的相同加权和&#xff0c;那么该系统就…

一文窥见神经网络

一文窥见神经网络 1.初识神经元1.1 生物神经元1.2 人工神经元1.3 权重的作用1.4 偏置的作用1.5 激活函数的作用1.5.1 线性激活函数1.5.2 非线性激活函数 2. 神经元模型2.1 多输入单神经元模型2.2 一层神经元模型2.3 神经网络&#xff08;多层神经元&#xff09;模型 3. 神经网络…

DBeaver 连接 OceanBase Oracle 租户

DBeaver 是一款通用的数据库工具软件&#xff0c;支持任何具有JDBC驱动程序的数据库。DBeaver 需要 Java 运行环境的支持。截稿时 DBeaver 24.0.0 版本默认提供的 OceanBase 驱动是连接 MySQL 的&#xff0c;想连接 Oracle 租户需要新建一个驱动器使用。 下载数据库驱动包 1、…

web实操5——http数据详解,request对象功能

http请求数据 现在我们浏览器f12的那些是浏览器给http格式数据整理之后便于我们阅读的。 原始的http格式信息&#xff1a; 就是按照一定格式和符号的字符串&#xff1a; 请求行&#xff1a;格式如下图 请求头&#xff1a;一个个key&#xff0c;value数据&#xff0c;用,分割…

u盘加密软件有哪些?2025年必备的u盘加密神器分享(共6款!提前布局!)

2024年《数据泄露成本报告》最新出炉&#xff01;再破纪录&#xff01; 报告显示&#xff0c;全球数据泄露事件的平均成本达488万美元&#xff0c;同比增加10%。 其中&#xff0c;u盘最为数据存储和传输的常用媒介&#xff0c;对其进行加密早已箭在弦上&#xff01; 在2025年…

实验5:网络设备发现、管理和维护

实验5&#xff1a;网络设备发现、管理和维护 实验目的及要求&#xff1a; 通过实验&#xff0c;掌握Cisco 路由器和交换机的IOS配置管理。自动从NTP服务器获取时间信息。能够利用TFTP服务器实现路由器和交换机配置文件的备份和恢复。同时验证CDP协议和LLDP协议的网络参数。完…

基于java的航空机票预定管理系统

一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据…

排序算法 -快速排序

文章目录 1. 快速排序&#xff08;Quick Sort&#xff09;1.1、 简介1.2、 快速排序的步骤 2. Hoare 版本2.1、 基本思路1. 分区&#xff08;Partition&#xff09;2. 基准选择&#xff08;Pivot Selection&#xff09;3. 递归排序&#xff08;Recursive Sorting&#xff09; 2…

UAC2.0 speaker——同时支持 16bit,24bit 和 32bit

文章目录 同时支持 16bit,24bit 和 32bit配置描述符集合描述符结构位数切换16bit 选择24bit 选择32bit 选择枚举效果同时支持 16bit,24bit 和 32bit 在一个 USB speaker 设备中同时支持 16bit, 24bit 和 32bit。 配置描述符集合 09 02 E9 00 02 01 00 80 32 08 0B 00 02

conda创建 、查看、 激活、删除 python 虚拟环境

1、创建 python 虚拟环境 ,假设该环境命名为 “name”。 conda create -n name python3.11 2、查看 python 虚拟环境。 conda info -e 3、激活使用 python 虚拟环境。 conda activate name 4、删除 python 虚拟环境 conda remove -n name --all ​​ 助力快速掌握数据集…

三周精通FastAPI:37 包含 WSGI - Flask,Django,Pyramid 以及其它

官方文档&#xff1a;https://fastapi.tiangolo.com/zh/advanced/wsgi/ 包含 WSGI - Flask&#xff0c;Django&#xff0c;其它 您可以挂载多个 WSGI 应用&#xff0c;正如您在 Sub Applications - Mounts, Behind a Proxy 中所看到的那样。 为此, 您可以使用 WSGIMiddlewar…