DelphiMVCFrameWork 源码分析(三)

news2025/6/20 13:21:25

中间件(MiddleWare)

文档中是这样说的:

Middleware is a powerful and flexible layer within DMVCFramework. Middleware allows you to
write SOLID code and separate the processing or each request into smaller steps to be invoked during the request handling. Middleware is the perfect layer for cross cutting concerns or to control HTTP requests/response.

中间件是DMVCFramework中一个强大而灵活的层。中间件允许您编写符合"SOLID"原则的代码,并将处理或请求分成更小的步骤,以便在调用请求期间进行处理。中间件是全局关注或控制HTTP的请求/响应的理想层。

  • 中间件的使用场景:
  • 支持CORS(跨域资源共享)
  • JWT 授权
  • HTTP基础授权
  • 其它形式的授权
  • URL重定向(如果你没用Apache或IIS的Webservice的URL重定向功能)
  • 反向代理(Reverse proxy)
  • 处理 ETag HTTP header
  • 日志Log
  • 缓存
  • 其它等等....

贴一个DelphiMVCFrameWork的中间件的时序图:

框架的中间件是通过事件机制来实现的,有4种情况:

  1. OnBeforeRouting
  2. ONBeforeControllerAction
  3. OnAfterControllerAction
  4. OnAfterRouting

中间件根据需要可以实现其中的一个或多个 。

中间件是通过接口机制来实现的:

基础单元MVCFramework.pas 声明了接口:

  IMVCMiddleware = interface
    ['{3278183A-124A-4214-AB4E-94CA4C22450D}']
    procedure OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean);
    procedure OnBeforeControllerAction(AContext: TWebContext;
      const AControllerQualifiedClassName: string; const AActionName: string;
      var AHandled: Boolean);
    procedure OnAfterControllerAction(AContext: TWebContext;
      const AControllerQualifiedClassName: string; const AActionName: string;
      const AHandled: Boolean);
    procedure OnAfterRouting(AContext: TWebContext; const AHandled: Boolean);
  end;

中间件是由引擎(MVCEngine)管理的。中间件和控制器的一个主要区别是:中间件是单例模式(所有请求共用一个实例),控制器是每个请求一个实例,所以引擎在添加这二者时,中间件是添加实例,控制器是添加类型。

FMVC.AddController(TPublicController);  //添加控制器类名
FMVC.AddController(TPrivateController);  //添加控制器类名
//Other controllers...
FMVC.AddMiddleware(TMyMiddleware.Create());  //添加中间件实例

看一个基本DEMO:(..\samples\basicdemo_server, ..\samples\basicdemo_vclclient)

服务端的WebModule代码:

unit WebModuleUnit1;

interface

uses System.SysUtils,
  System.Classes,
  Web.HTTPApp,
  MVCFramework;

type
  TWebModule1 = class(TWebModule)
    procedure WebModuleCreate(Sender: TObject);
    procedure WebModuleDestroy(Sender: TObject);

  private
    MVC: TMVCEngine;

  public
    { Public declarations }
  end;

var
  WebModuleClass: TComponentClass = TWebModule1;

implementation

{$R *.dfm}


uses
  App1MainControllerU,
  MVCFramework.Commons,
  MVCFramework.Middleware.StaticFiles;

procedure TWebModule1.WebModuleCreate(Sender: TObject);
begin
  MVC := TMVCEngine.Create(Self,
    procedure(Config: TMVCConfig)
    begin
      Config[TMVCConfigKey.ViewPath] := '.\www\public_html';
    end);

  // Web files
  MVC.AddMiddleware(TMVCStaticFilesMiddleware.Create('/app', '.\www\public_html'));

  // Image files
  MVC.AddMiddleware(TMVCStaticFilesMiddleware.Create('/images', '.\www\public_images', 'database.png'));

  MVC.AddController(TApp1MainController);
end;

procedure TWebModule1.WebModuleDestroy(Sender: TObject);
begin
  MVC.free;
end;

end.

这里添加了2个处理静态文件的中间件,一个是静态网页,一个是图片。

看下这个中间件的代码:

// ***************************************************************************
//
// Delphi MVC Framework
//
// Copyright (c) 2010-2023 Daniele Teti and the DMVCFramework Team
//
// https://github.com/danieleteti/delphimvcframework
//
// Collaborators on this file:
// Jo鉶 Ant鬾io Duarte (https://github.com/joaoduarte19)
//
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// *************************************************************************** }

unit MVCFramework.Middleware.StaticFiles;

interface

uses
  MVCFramework,
  MVCFramework.Commons,
  System.Generics.Collections;

type
  TMVCStaticFilesDefaults = class sealed
  public const
    /// <summary>
    /// URL segment that represents the path to static files
    /// </summary>
    STATIC_FILES_PATH = '/static';

    /// <summary>
    /// Physical path of the root folder that contains the static files
    /// </summary>
    DOCUMENT_ROOT = '.\www';

    /// <summary>
    /// Default static file
    /// </summary>
    INDEX_DOCUMENT = 'index.html';

    /// <summary>
    /// Charset of static files
    /// </summary>
    STATIC_FILES_CONTENT_CHARSET = TMVCConstants.DEFAULT_CONTENT_CHARSET;
  end;

  TMVCStaticFileRulesProc = reference to procedure(const Context: TWebContext; var PathInfo: String; var Handled: Boolean);
  TMVCStaticFileMediaTypesCustomizer = reference to procedure(const MediaTypes: TMVCStringDictionary);
  TMVCStaticFilesMiddleware = class(TInterfacedObject, IMVCMiddleware)
  private
    fSanityCheckOK: Boolean;
    fMediaTypes: TMVCStringDictionary;
    fStaticFilesPath: string;
    fDocumentRoot: string;
    fIndexDocument: string;
    fStaticFilesCharset: string;
    fSPAWebAppSupport: Boolean;
    fRules: TMVCStaticFileRulesProc;
    procedure AddMediaTypes;
    // function IsStaticFileRequest(const APathInfo: string; out AFileName: string;
    // out AIsDirectoryTraversalAttach: Boolean): Boolean;
    function SendStaticFileIfPresent(const AContext: TWebContext; const AFileName: string): Boolean;
    procedure DoSanityCheck;
  public
    constructor Create(
      const AStaticFilesPath: string = TMVCStaticFilesDefaults.STATIC_FILES_PATH;
      const ADocumentRoot: string = TMVCStaticFilesDefaults.DOCUMENT_ROOT;
      const AIndexDocument: string = TMVCStaticFilesDefaults.INDEX_DOCUMENT;
      const ASPAWebAppSupport: Boolean = True;
      const AStaticFilesCharset: string = TMVCStaticFilesDefaults.STATIC_FILES_CONTENT_CHARSET;
      const ARules: TMVCStaticFileRulesProc = nil;
      const AMediaTypesCustomizer: TMVCStaticFileMediaTypesCustomizer = nil);
    destructor Destroy; override;

    procedure OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean);
    procedure OnBeforeControllerAction(AContext: TWebContext; const AControllerQualifiedClassName: string;
      const AActionName: string; var AHandled: Boolean);

    procedure OnAfterControllerAction(AContext: TWebContext;
      const AControllerQualifiedClassName: string; const AActionName: string;
      const AHandled: Boolean);

    procedure OnAfterRouting(AContext: TWebContext; const AHandled: Boolean);
  end;

implementation

uses
  MVCFramework.Logger,
  System.SysUtils,
  System.NetEncoding,
  System.IOUtils,
  System.Classes;

{ TMVCStaticFilesMiddleware }

procedure TMVCStaticFilesMiddleware.AddMediaTypes;
begin
  fMediaTypes.Add('.html', TMVCMediaType.TEXT_HTML);
  fMediaTypes.Add('.htm', TMVCMediaType.TEXT_HTML);
  fMediaTypes.Add('.txt', TMVCMediaType.TEXT_PLAIN);
  fMediaTypes.Add('.text', TMVCMediaType.TEXT_PLAIN);
  fMediaTypes.Add('.csv', TMVCMediaType.TEXT_CSV);
  fMediaTypes.Add('.css', TMVCMediaType.TEXT_CSS);
  fMediaTypes.Add('.js', TMVCMediaType.TEXT_JAVASCRIPT);
  fMediaTypes.Add('.json', TMVCMediaType.APPLICATION_JSON);
  fMediaTypes.Add('.jpg', TMVCMediaType.IMAGE_JPEG);
  fMediaTypes.Add('.jpeg', TMVCMediaType.IMAGE_JPEG);
  fMediaTypes.Add('.jpe', TMVCMediaType.IMAGE_JPEG);
  fMediaTypes.Add('.png', TMVCMediaType.IMAGE_PNG);
  fMediaTypes.Add('.ico', TMVCMediaType.IMAGE_X_ICON);
  fMediaTypes.Add('.appcache', TMVCMediaType.TEXT_CACHEMANIFEST);
  fMediaTypes.Add('.svg', TMVCMediaType.IMAGE_SVG_XML);
  fMediaTypes.Add('.xml', TMVCMediaType.TEXT_XML);
  fMediaTypes.Add('.pdf', TMVCMediaType.APPLICATION_PDF);
  fMediaTypes.Add('.svgz', TMVCMediaType.IMAGE_SVG_XML);
  fMediaTypes.Add('.gif', TMVCMediaType.IMAGE_GIF);
end;

constructor TMVCStaticFilesMiddleware.Create(
      const AStaticFilesPath: string;
      const ADocumentRoot: string;
      const AIndexDocument: string;
      const ASPAWebAppSupport: Boolean;
      const AStaticFilesCharset: string;
      const ARules: TMVCStaticFileRulesProc;
      const AMediaTypesCustomizer: TMVCStaticFileMediaTypesCustomizer);
begin
  inherited Create;
  fSanityCheckOK := False;
  fStaticFilesPath := AStaticFilesPath.Trim;
  if not fStaticFilesPath.EndsWith('/') then
    fStaticFilesPath := fStaticFilesPath + '/';

  if TDirectory.Exists(ADocumentRoot) then
  begin
    fDocumentRoot := TPath.GetFullPath(ADocumentRoot);
  end
  else
  begin
    fDocumentRoot := TPath.Combine(AppPath, ADocumentRoot);
  end;
  fIndexDocument := AIndexDocument;
  fStaticFilesCharset := AStaticFilesCharset;
  fSPAWebAppSupport := ASPAWebAppSupport;
  fMediaTypes := TMVCStringDictionary.Create;
  fRules := ARules;
  AddMediaTypes;
  if Assigned(AMediaTypesCustomizer) then
  begin
    AMediaTypesCustomizer(fMediaTypes);
  end;
end;

destructor TMVCStaticFilesMiddleware.Destroy;
begin
  fMediaTypes.Free;

  inherited Destroy;
end;

procedure TMVCStaticFilesMiddleware.DoSanityCheck;
begin
  if not fStaticFilesPath.StartsWith('/') then
  begin
    raise EMVCException.Create('StaticFilePath must begin with "/" and cannot be empty');
  end;
  if not TDirectory.Exists(fDocumentRoot) then
  begin
    raise EMVCException.CreateFmt('TMVCStaticFilesMiddleware Error: DocumentRoot [%s] is not a valid directory', [fDocumentRoot]);
  end;
  fSanityCheckOK := True;
end;

// function TMVCStaticFilesMiddleware.IsStaticFileRequest(const APathInfo: string; out AFileName: string;
// out AIsDirectoryTraversalAttach: Boolean): Boolean;
// begin
// Result := TMVCStaticContents.IsStaticFile(fDocumentRoot, APathInfo, AFileName,
// AIsDirectoryTraversalAttach);
// end;

procedure TMVCStaticFilesMiddleware.OnAfterControllerAction(AContext: TWebContext;
      const AControllerQualifiedClassName: string; const AActionName: string;
      const AHandled: Boolean);
begin
  // do nothing
end;

procedure TMVCStaticFilesMiddleware.OnAfterRouting(AContext: TWebContext; const AHandled: Boolean);
begin
  // do nothing
end;

procedure TMVCStaticFilesMiddleware.OnBeforeControllerAction(AContext: TWebContext; const AControllerQualifiedClassName,
  AActionName: string; var AHandled: Boolean);
begin
  // do nothing
end;

procedure TMVCStaticFilesMiddleware.OnBeforeRouting(AContext: TWebContext; var AHandled: Boolean);
var
  lPathInfo: string;
  lFileName: string;
  lIsDirectoryTraversalAttach: Boolean;
  lFullPathInfo: string;
  lRealFileName: string;
  lAllow: Boolean;
begin
//  if not fSanityCheckOK then
//  begin
//    DoSanityCheck;
//  end;

  lPathInfo := AContext.Request.PathInfo;

  if not lPathInfo.StartsWith(fStaticFilesPath, True) then
  begin
    { In case of folder request without the trailing "/" }
    if not lPathInfo.EndsWith('/') then
    begin
      lPathInfo := lPathInfo + '/';
      if not lPathInfo.StartsWith(fStaticFilesPath, True) then
      begin
        AHandled := False;
        Exit;
      end;
    end
    else
    begin
      AHandled := False;
      Exit;
    end;
  end;

  if Assigned(fRules) then
  begin
    lAllow := True;
    fRules(AContext, lPathInfo, lAllow);
    if not lAllow then
    begin
      AHandled := True;
      Exit;
    end;
  end;

  // calculate the actual requested path
  if lPathInfo.StartsWith(fStaticFilesPath, True) then
  begin
    lPathInfo := lPathInfo.Remove(0, fStaticFilesPath.Length);
  end;
  lPathInfo := lPathInfo.Replace('/', PathDelim, [rfReplaceAll]);
  if lPathInfo.StartsWith(PathDelim) then
  begin
    lPathInfo := lPathInfo.Remove(0, 1);
  end;
  lFullPathInfo := TPath.Combine(fDocumentRoot, lPathInfo);

  { Now the actual requested path is in lFullPathInfo }

  if not fSanityCheckOK then
  begin
    DoSanityCheck;
  end;

  if TMVCStaticContents.IsStaticFile(fDocumentRoot, lPathInfo, lRealFileName,
    lIsDirectoryTraversalAttach) then
  begin
    // check if it's a direct file request
    // lIsFileRequest := TMVCStaticContents.IsStaticFile(fDocumentRoot, lPathInfo, lRealFileName,
    // lIsDirectoryTraversalAttach);
    if lIsDirectoryTraversalAttach then
    begin
      AContext.Response.StatusCode := HTTP_STATUS.NotFound;
      AHandled := True;
      Exit;
    end;

    AHandled := SendStaticFileIfPresent(AContext, lRealFileName);
    if AHandled then
    begin
      Exit;
    end;
  end;

  // check if a directory request
  if TDirectory.Exists(lFullPathInfo) then
  begin
    if not AContext.Request.PathInfo.EndsWith('/') then
    begin
      AContext.Response.StatusCode := HTTP_STATUS.MovedPermanently;
      AContext.Response.CustomHeaders.Values['Location'] := AContext.Request.PathInfo + '/';
      AHandled := True;
      Exit;
    end;

    if not fIndexDocument.IsEmpty then
    begin
      AHandled := SendStaticFileIfPresent(AContext, TPath.Combine(lFullPathInfo, fIndexDocument));
      Exit;
    end;
  end;

  // if SPA support is enabled, return the first index.html found in the path.
  // This allows to host multiple SPA application in subfolders
  if (not AHandled) and fSPAWebAppSupport and (not fIndexDocument.IsEmpty) then
  begin
    while (not lFullPathInfo.IsEmpty) and (not TDirectory.Exists(lFullPathInfo)) do
    begin
      lFullPathInfo := TDirectory.GetParent(lFullPathInfo);
    end;
    lFileName := TPath.GetFullPath(TPath.Combine(lFullPathInfo, fIndexDocument));
    AHandled := SendStaticFileIfPresent(AContext, lFileName);
  end;
end;

function TMVCStaticFilesMiddleware.SendStaticFileIfPresent(const AContext: TWebContext;
  const AFileName: string): Boolean;
var
  lContentType: string;
begin
  Result := False;
  if TFile.Exists(AFileName) then
  begin
    if fMediaTypes.TryGetValue(LowerCase(ExtractFileExt(AFileName)), lContentType) then
    begin
      lContentType := BuildContentType(lContentType, fStaticFilesCharset);
    end
    else
    begin
      lContentType := BuildContentType(TMVCMediaType.APPLICATION_OCTETSTREAM, '');
    end;
    TMVCStaticContents.SendFile(AFileName, lContentType, AContext);
    Result := True;
    Log(TLogLevel.levDebug, AContext.Request.HTTPMethodAsString + ':' +
      AContext.Request.PathInfo + ' [' + AContext.Request.ClientIp + '] -> ' +
      ClassName + ' - ' + IntToStr(AContext.Response.StatusCode) + ' ' +
      AContext.Response.ReasonString);
  end;
end;

end.

可以看到静态文件处理中间件只要处理OnBeforeRouting()事件,其它事件为空。

静态文件有许多种类型,这里把常用的以后缀标志文件类型进行了归类。对文件路径进行检查,如果有自定义的规则,则进行规则处理等。

中间件的架构比较简单,但是实现中间件的功能就不那么容易了。要清楚中间件是要完成什么功能,理清中间件的流程,中间件的使用场景。可以看框架自带的中间件的代码,是如何实现的。

框架自带了常用的中间件:

  •  MVCFramework.Middleware.Compression.pas   // 压缩
  • •MVCFramework.Middleware.ActiveRecord.pas   //ORM
  •  MVCFramework.Middleware.CORS.pas          //CORS
  •  MVCFramework.Middleware.JWT.pas             //JWT Token授权
  •  MVCFramework.Middleware.Analytics.pas     //Use Log save Data 
  • •MVCFramework.Middleware.Authentication.pas   //授权
  •  MVCFramework.Middleware.Authentication.RoleBasedAuthHandler.pas  //基于角色的授权
  • MVCFramework.Middleware.Redirect   //重定向
  • MVCFramework.Middleware.ETag   //ETag Http header
  • •MVCFramework.Middleware.SecurityHeaders.pas   //加密头
  •  MVCFramework.Middleware.StaticFiles.pas   //静态文件
  •  MVCFramework.Middleware.Swagger.pas  //文档生成
  •  MVCFramework.Middleware.Trace.pas   //Debug Utils Tools

关于中间件,就简单介绍这些,这里不是分析。

框架里的JWT Token 登录授权处理中间件比较简单,不支持验证码处理,签名方法也只支持一种HS(HS256,HS384,HS512),不支持RS(RS256, RS384, RS512),ES(ES256,ES384,ES512),PS(PS256,PS384,PS512)等,实际运用中还得扩充。

以后写一个支持验证码的注册登录中间件后再做一个详细分析。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二本4年测试经验,5面阿里(定薪38K),分享我的心得

年前准备跳槽&#xff0c;先后面试了各大小公司&#xff0c;拿了一些offer&#xff0c;有阿里&#xff0c;滴滴&#xff0c;快手&#xff0c;达达&#xff0c;得物等公司。面试的公司大部分都能过&#xff0c;所以这里给大家分享下自己的经验&#xff0c;也给自己做个归档&…

新书推荐:《AIGC未来已来——迈向通用人工智能时代》

新书推荐&#xff1a;《AIGC未来已来——迈向通用人工智能时代》 导言 AIGC为何引发关注&#xff1f; ChatGPT会成为人工智能的拐点吗&#xff1f; GPT-4未来已来&#xff0c;奇点时刻该如何面对&#xff1f; 人类的创新能力会被AIGC替代吗&#xff1f; 当下有哪些典型的AIGC变…

单片机GD32F303RCT6 (Macos环境)开发 (二十)—— 光感芯片veml7700的使用

光感芯片veml7700的使用 1、veml有7个寄存器&#xff0c;每个十六位&#xff0c;见图。 00是config寄存器&#xff0c; 01 02 是中断设置的阈值 03是节能模式的设置 04 是得到的光的亮度值 05是得到的data of whole WHITE 06是中断设置值。 2、我们只测试得到光的亮度值&…

86盒IP对讲一键报警器

86盒IP对讲一键报警器 86盒IP对讲一键报警器&#xff1a;革命性保障生命安全的利器&#xff01; 随着科技的飞速发展&#xff0c;我们的生活变得越来越方便和智能化。而86盒IP对讲一键报警器更是在这种背景下应运而生。这款产品不仅无缝对接各种手机APP&#xff0c;也可以在智…

Linux系统crash后定位方法-PCIE举例

crash解释 在Linux操作系统中&#xff0c;"crash"通常是指一种用于分析系统崩溃&#xff08;crash&#xff09;的工具或方法。当系统发生崩溃时&#xff0c;可能会产生一些关键信息&#xff0c;如错误日志、内存转储文件等。使用crash工具可以分析这些信息&#xff…

零基础自学【Web安全/网络渗透】,保姆级快速入门指南(非常详细)

前言 基础真的很简单&#xff0c;是个人稍微认点真都能懂&#xff0c;这就是好多人说的网络安全简单、易懂、好学&#xff0c;然后就是一顿浮夸的言论&#xff0c;误导那些小白&#xff0c;再然后那些小白也就跟着浮夸。这里我就给那些轻浮的人泼一桶冷水&#xff0c;懂跟学会…

香港人才引进只看本科排名,“以本科定终身”是否公平?

2022年10月20日&#xff0c;香港特首在《2022 施政报告》时公布&#xff0c;新推出高端人才通行证计划&#xff0c;简称“高才通”&#xff0c;豪言3年引入10万人才&#xff0c;接受全球百强名校本科毕业的高材生或年收入超过250万港币的高收入群体到香港工作。 香港高才通是目…

【数据结构】链表(C语言实现)

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c语言系列专栏&#xff1a;c语言之路重点知识整合 &#x…

丁鹿学堂:vue的脚手架项目基础入门和单文件代码分析

在实际开发中&#xff0c;我们都是通过脚手架创建vue项目的。 默认安装了node&#xff0c;yarn&#xff0c; 1创建项目&#xff1a; yarn create vue为了学习&#xff0c;我们选择最简单的培训&#xff0c;一路下来都选no 2 创建项目以后&#xff0c;只是搭起了架子&#x…

Uniapp一言“一句话接口调用

界面 代码 <template><view ><view class"uni-padding-wrap uni-common-mt"><view class"text-box" scroll-y"true"><text>{{data}}</text></view><view class"uni-btn-v"><butto…

(C语言版)力扣(LeetCode)栈和队列面试题

栈和队列面试题 20. 有效的括号题目解法一&#xff1a;建立栈解决解法二&#xff1a;数组模拟栈解决 225. 用队列实现栈题目解法&#xff1a;两个队列实现栈 232. 用栈实现队列题目解法&#xff1a;两个栈实现队列 622. 设计循环队列题目解法一&#xff1a;数组解法二&#xff…

AcWing算法提高课-1.3.3宠物小精灵之收服

宣传一下算法提高课整理 <— CSDN个人主页&#xff1a;更好的阅读体验 <— 本题链接&#xff08;AcWing&#xff09; 点这里 题目描述 宠物小精灵是一部讲述小智和他的搭档皮卡丘一起冒险的故事。 一天&#xff0c;小智和皮卡丘来到了小精灵狩猎场&#xff0c;里面有…

Stable Diffusion 生成高清图片

Stable Diffusion 生成 8K 高清图片 0. 简介1. 安装 ultimate-upscale-for-automatic1111 插件2. 安装 4x-UltraSharp 模型3. 生成 1 张普通图片4. 生成 1 张高清图片 0. 简介 记录一下使用 Stable Diffusion 生成高清图片使用的插件和过程。 1. 安装 ultimate-upscale-for-a…

Qt推流程序(视频文件/视频流/摄像头/桌面转成流媒体rtmp+hls+webrtc)可在网页和播放器远程观看

一、前言说明 推流直播就是把采集阶段封包好的内容传输到服务器的过程。其实就是将现场的视频信号从手机端&#xff0c;电脑端&#xff0c;摄影机端打包传到服务器的过程。“推流”对网络要求比较高&#xff0c;如果网络不稳定&#xff0c;直播效果就会很差&#xff0c;观众观…

Linux基础篇-将虚拟机的网络配置成静态的

一般都是主机连接服务器&#xff0c;服务器连接主机也是为了上网&#xff0c;通过NAT地址转换就可以。为了防止服务器的IP地址变化&#xff0c;将虚拟机的IP地址设置成静态的。 NAT模式下&#xff0c;为了让主机和虚拟机之间能互相ping通&#xff0c;主机虚拟了一张网卡出来&am…

PicGo+阿里云OSS+Typora图床设置实践及解决方案

你是否遇到想跨平台看markdown文件结果图片加载不出&#xff0c;csdn上传的时候还要手动操作的命苦感&#xff0c;那就接着往下看吧&#xff01; 本文为作者在安装过程中的总结&#xff0c;争取以最简洁的方式帮助大家安装 markdown语法在程序员的生活中运用广泛&#xff0c;…

测试用例、测试流程模型、测试方法详解 超详细分解

1. 测试用例 1.1 测试用例前提 什么是测试用例&#xff1f; 一组由前提条件、测试输入、执行条件以及预期结果等组成&#xff0c;以完成对某个特定需求或者目标测试的数据&#xff0c;体现测试方案、方法、技术和策略的文档。 为什么编写测试用例 &#xff08;1&#xff0…

股票量价关系基础知识1

什么叫成交量 成交量的概念中&#xff0c;包含“成交”、“成交量”和“成交值”三个子概念。 所谓“成交”&#xff0c;就是买卖双方同意所达成的交易行为。“成交”是商业用语&#xff0c;因为股市本事就是一项商业活动&#xff0c;股票市场就是一个大商场&#xff0c;各地证…

每日学术速递5.9

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Tracking through Containers and Occluders in the Wild(CVPR 2023) 标题&#xff1a;在野外通过容器和遮挡物进行追踪 作者&#xff1a;Basile Van Hoorick, Pavel Tokmakov, Si…

C++数据封装以及定义结构的详细讲解鸭~

名字&#xff1a;阿玥的小东东 博客主页&#xff1a;阿玥的小东东的博客_CSDN博客-python&&c高级知识,过年必备,C/C知识讲解领域博主 目录 定义结构 访问结构成员 结构作为函数参数 指向结构的指针 typedef 关键字 C 数据封装 数据封装的实例 设计策略 C 类 &…