《WebKit 技术内幕》之四(3): 资源加载和网络栈
3. 网络栈
3.1 WebKit的网络设施
WebKit的资源加载其实是交由各个移植来实现的,所以WebCore其实并没有什么特别的基础设施,每个移植的网络实现是非常不一样的。
从WebKit的代码结构中可以看出,网络部分代码的确比较少的,它们都在目录“WebKit/Source/WebCore/platform/network”中。主要是一些HTTP消息头、MIME消息、状态码等信息的描述和处理,没有实质的网络连接和各种针对网络的优化。
3.2 Chromium网络栈
(1)“net“所包含的主要子目录,是chromium网络栈的主要模块。要实现一些基础的部分,如 HTTP 协议、DNS 解析等模块,还包括一些为了减少网络时间而引入的新技术(如 SPDY、QUIC 等)。
(2)网络栈结构
在“Net”目录下的子目录,包含了主要的子模块,如下图描述了从URLRequest类到Socket类之间的调用过程。以HTTP协议为例,图中列出了建立TCP的Socket连接过程中涉及的类。
- URLRequest类被上层调用并启动请求的时候,它会根据URL的“scheme”来决定需要创建什么类型的请求,“scheme”也就是URL的协议类型,如:“http://"、“file://”·,也可以是自定义的scheme,例如Andriod系统的”file://andriod_asset/“。URLRequest对象创建的是一个URLRequestJob子类的一个对象,例如图形的中的URLRequestHttpJob类。为了支持自定义的scheme处理方式,Chromium使用工厂模式。URLRequestJob类和它的工厂类URLRequestJobFactory的管理工作都由URLRequestJobManager类负责。基本思路是,用户可以在该类中注册多个工厂,当有URLRequest请求时,先由工厂检查它是否需要处理该类“scheme”,如果没有,工厂管理类继续交给下一个工厂类来处理。最后,如果没有任何工厂能够处理,Chromium则交给内置的工厂来检查和处理是否为“http://”、“f'tp://”或者“file://”等。
- 当URLRequestHttpJob对象被创建后,该对象首先从Cookie管理器中获取与该URL相关的信息。之后,它同样借助于HttpTransactionFactory对象创建一个HttpTransaction对象来表示开启一个HTTP连接的事务(当然这里的概念不同于数据库中的事务概念)。通常情况下,HttpTransactionFactory对象对应的是一个它的子类HttpCache对象。HttpCache类使用本地磁盘缓存机制,如果该请求对应的回复已经在磁盘缓存中,那么Chromium无需再建立HttpTransaction来发起连接,而是直接从磁盘中获取即可。如果磁盘中没有该URL的缓存,同时如果目前该URL请求对应的HttpTransaction已经建立,那么只要等待它的回复即可。当这些条件都不满足的时候,Chromium实际上才会真正创建HttpTransaction对象、
- HttpNetworkTransaction类使用HttpNetworkSession类来管理连接会话。HttpNetworkSession类通过它的成员HttpStreamFactory对象来建立TCPSocket连接,之后Chromium创建HttpStream对象。HttpStreamFactory对象将和网络之间的数据读写交给自己新创建的一个HttpStream子类的对象来处理。
- 套接字的建立,Chromium中与服务器建立连接的套接字是StreamSocket类,它是一个抽象类,在POSIX系统和Windows系统上有着分别不同的实现。同时为了支持SSL机制,StreamSocket类还有一个子类----SSLSocket。
(3)代理
用户代理由以下几个类来处理:
- Proxy Service:对于一个URL,HttpStreamFactory类使用ProxyService类来获取代理信息。ProxyService类首先会检查当前代理设置不是最新的。如果不是,它依赖ProxyConfigService来重新获取代理信息。该类不处理实际任务,而是使用ProxyReslover类来做实际代理工作。
- Proxy Config Service:获取代理信息的类,可获得平台上的代理设置,在Linux】Windows上有不同的实现。
- ProxyScriptFetcher:Chromium支持代理的JavaScript脚本,该类负责从代理的URL中获取该脚本。
- Proxy Resolver:实际负责代理的解释和执行,通常启用新的线程来处理,因为当前可能会被域名的解析所阻碍。
- ProxyResolverV8:ProxyResolver的子类,使用V8引擎来解析和执行脚本。
(4)域名解析(DNS)
通常情况下,用户都是使用域名来访问网络资源的,所以在建立TCP连接前需要解析域名。Chromium中使用HostResolverImpl类来解析域名,具体调用的函数时“getaddrinfo()”,该函数是一个阻塞式的函数,所以Chromium理所当然使用单独的线程来处理它,这是Chromium的原则之一。
为了保证效率,使用HostCache类来保存解析后的域名,最多时会有多达1000个域名和地址映射关系会被存储起来。
3,3 磁盘本地缓存
浏览器的缓存机制能够提高网页的加载速度。
(1)特性
- 虽然需要缓存的资源可能很多,但磁盘空间不是无限大的,所以必须要有相应的机制来移除合适的缓存资源,以便加入新的资源。
- 能够保证在浏览器崩溃时不破坏磁盘文件,至少能够保护原先在磁盘中的数据。
- 能够高效和快速地访问磁盘中现有的数据结构,支持同步和异步两种访问方式。
- 能够避免同时存储两个相同的资源。
- 能够很方便地从磁盘中删除一个项,同时可以在操作一个项的时候不受其他请求的影响。
- 磁盘不支持多线程访问,所以需要把所有磁盘缓存的操作放入单独的一个线程。
- 升级版本时,如果磁盘缓存的内部存储结构发生改变,Chromium仍然能够支持老版本的结构。
(2)结构
实现上主要有两个类,Backend(整个磁盘缓存) 和 Entry(表中的表项)。至少需要一个索引文件和四个数据文件。索引文件用来索引,数据文件又称块文件。缓存通常是一个表,对于整个表的操作作用在 Backend 类上,包括创建表中的一个个项,每个项由关键字来唯一个确定,这个关键字就是资源的 URL。对于项目内的操作包括读写都是由 Entry 类来处理。
- Backend 类表示整个磁盘缓存,是所有磁盘缓存操作的主入口,表示一个缓存表。
- Entry 类指的是表中的表项,表项的结构分为两个部分。
表和表项如何组织和存储在磁盘上:至少有一个索引文件和四个数据文件(块文件),每个块文件的大小是固定的,当资源文件超过某个块的大小就会为其分配多个块来解决,但最多不能超过四个块,超过四个块能存储的时候会建立单独的文件来保存。如果一个表项需要分配四个块则这些块在文件中的索引位置是对齐的(起始块的位置是4 的倍数)
- 索引文件:用来检索存放在数据文件中的众多索引项,用来索引表项,包括一个索引头部和索引地址表。直接将文件映射到内存地址。头部用来表示该索引文件的信息,如索引文件版本号、索引项数量、文件大小等。索引地址表:保存各个表项对应的索引地址。该索引文件直接将文件映射到内存地址,这样可以快速地找到表项的索引地址。
struct NET_EXPORT_PRIVATEIndex Header{
uint32 magic;
uint32 version;
int32 num_entries; //nuimuber entries currently stored.
int32 num_bytes; //Total size of the stored data.
int32 last_file; //Last external file created.
int32 this_id; //Id for all entries being changed (dirty flag).
CacheAddr status; //Storage for usage data.
int32 table_len; //Actual size of the table(0==kIndexTablesize),
int32 crash; //Signals a previous crash.
int32 experiment; //Id of an ongoing test.
uint64 create_time; //Creation time for this set of files.
int32 pad[52];
LruData lru; //Eviction control data.
};
strunct Index { // The structure of the whole index file.
IndexHeader header;
CacheAddr table[kIndexTablesize] ; //Default size Actual size controlled by header.table_len.
};
- 数据文件:块文件,有很多不同大小的块,用于快速检索,这些数据块的内容是表项,包括 HTTP 文件头、请求数据和资源数据。资源文件大小超过一定值的时候,Chromium会捡来单独的文件来保存它们,而不是将它们放入四个数据文件中。这些单独存储的文件中并没有元数据信息,只是资源文件的内容,其文件名形如“f_xxxx,xxxx是0-9A-F是十六进制数来表示编号。
表项的结构也分为两个部分:
- 第一部分用于标记自己,包括各种元数据信息和自身的内容,较少变动
- 第二部分主要为表项的回收算法服务,经常发生变动,里面保存了回收算法所需要的信息
struct EntryStore {
uint32 hash; //Full hash of the key.
CacheAddr next; //Next entry with the same hash or bucket.
CacheAddr rankings_node; //Rankings node for this entry.
int32 reuse_count; //How often is this entry used.
int32 refetch_count; //How often is this fetched from the net.
int32 state; //Current state
uint64 creation_time;
int32 key_len;
CachedAddr long_key; //Optional adddress of a long key.
int32 data_size[4]; //We can store up to 4 data streams for each
CacheAddr data_add[4]; //entry
uint32 flags; //Any Combination of EntryFlags.
int32 pad[4];
uint32 self_hash; // The hash of EntryStore up to this point.
char key[256-24*4]; //null terminated
};
磁盘缓存的存储结构如下:
Chomium使用LRU算法来回收表项,因为磁盘存储的空间是有限的,不能无限的增长下去,所以对于很少使用到的表项,可以回收这一部分磁盘空间。
3.4 Cookie机制
Cookie就是一系列的“关键字+值”对。
test1=webkit;test2=chromium;Expires=Sun,30 Oct 2016 21:35:00 GMT;Domain.myweb.com;
test1,test2 自定义的关键字
Expires、Domain:为预定义的关键字,表示的是该Cookie的失效时间和该Cookie对应的域。
一个网页的 Cookie 只能被该网页(或者说是该域的网页)访问。根据 Cookie 的时效性可以将 Cookie 分成两种类型,第一种是会话型 Cookie(Session Cookie),这些 Cookie 只是保存在内存中,当浏览器退出的时候即清除这些 Cookie。如果 Cookie 没有设置失效时间,就是会话型 Cookie。第二中是持续型 Cookie(Persistent Cookie),也就是当浏览器退出的时候,仍然保留 Cookie 的内容。该类型的 Cookie 有一个有效期,在有效期内,每次访问该 Cookie 所属域的时候,都需要将该 Cookie 发送个服务器,这样服务器就能够追踪用户的行为。
Chromium中支持Cookie的机制也较为简单和清晰,CookieMonster是Cookie机制中最重要的类,实际上相当于Cookie的管理器,它包括几个作用:
- 实现CookieStore的接口,它是对外的接口,调用者可以设置和获得Cookie。
- 报告各种Cookie的事件,如更新信息等,主要使用Delegate类。
- Cookie对象的集合,也就是CanonicalCookie的集合,每个CanonicalCookie对象表示一个域的Cookie集合。
- 持续型Cookie的存储
以上的是在内存中保存的,当需要存储到磁盘的时候使用PersistentCookieStore,具体由SQLLitePersistenCookieStore类负责实际的存储动作。
3.5 安全机制
HTTP是一种使用明文来传输数据的应用层协议。构建建在SSL之上的HTTPS提供了安全的网络传输机制。支持一种新的标准 HSTS 协议(HTTP Strict Transport Security):能够让网络服务器声明它只支持 HTTPS 协议。浏览器能够理解服务器的声明,发送基于 HTTPS 的连接和请求。通常情况下,浏览器的用户不会输入“scheme(http://),l浏览器的补齐功能通常会加入该“scheme”,但是,服务器可能需要”https://",在这样的情况下,该协议就显得非常有用。一般情况次啊,服务在返回的消息头中加入以下信息表明它支持该标准。
Strict-Transport-Security:max-age=16070400;includesSubDomains
3.6 高性能网络栈
Chromium的网络模块有两个重要目标,其一是安全,其二是速度。
(1)DNS 预取和 TCP 预连接:
- DNS 预取就是利用现有的 DNS 机制,提前解析网页中可能的网络连接。当用户正在浏览当前网页的时候,Chromium 提取网页中的超链接,将域名抽取出来,利用比较少的 CPU 和网络带宽来解析这些域名和 IP 地址,这样一来,用户根本感觉不到这一过程。当用户单击这些链接的时候,可以节省不少时间,特别在域名解析比较慢的时候,效果特别明显。
- DNS预取技术直接利用系统的域名解析机制,不会阻碍当前网络栈的工作,DNS预取技术针对多个域名采取并行处理的方式,每个域名的解析须由新开启一个线程来处理,结束后此线程即退出。
- 网页的开发者可以显示指定预取那些域名来让Chromium解析,这非常直接了当,特别对于那些需要重定向的域名,具体做法如下:<link rel="dns-prefetch" href="http://this-is-a-dns-prefetch-example.com">。当然,DNS预取技术不仅应用于网页中的超链接,在用户敲下回车键获取网页之前,Chromium 就已经开始用 DNS 预解析技术解析该域名。
- 可以通过在地址栏中输入“chrome://dns/”查看Chromium的DNS预取的域名。
- Chromium使用追踪技术来获取用户从什么网页跳转到另一个网页,可以利用这些数据、一些启发式规则和其他一些暗示来预测用户下面会单击什么超链接,当有足够的把握时,它便先DNS预取,更进一步,还可以预先建立TCP连接。
- 同DNS预取技术一样,追踪技术不仅应用于网页中的超链接,当用户在地址栏输入地址,如候选项同输入的地址很匹配,则在用户敲下回车键获取该网页之前,Chromium就已经开始尝试建立TCP连接了。
(2)支持 HTTP 管线化技术(Pipelining):
HTTP 1.1 中新增的管线化技术:将多个 HTTP 请求一次性提交给服务器,可能将多个 HTTP 请求填充在 TCP 数据包内(相当于 HTTP 权威指南中串行事务处理中的管道化连接),HTTP管线化需要在网络上传较少的TCP数据包,因此减少了网络的负载。
请求结果的管线化使得HTML网页加载时间动态提升,特别在具体有高延迟的连接环境下,管线化机制需要通过永久连接(Persistent Connection)完成,并且只有 GET 和 HEAD 等幂等请求可以进行管线化。使用场景有很大的限制。
(3) SPDY
解决 HTTP 管线技术的使用限制。SPDY 协议是一种新的会话层协议,定义在 HTTP 协议和 TCP 协议之间。核心思想是多路复用,仅使用一个连接来传输一个网页中的众多资源,没有改变 HTTP 协议,将 HTTP 协议头通过 SPDY 来封装和传输,服务器只需要从 SPDY 的消息头中获取各个资源的 HTTP 头即可。SPDY 的工作方式有以下四个特征:
- 利用一个 TCP 连接来传输不限个数的资源请求的读写数据流。
- 根据资源请求的特性和优先级, SPDY 可以调整这些资源请求的优先级。
- 只对 https 请求使用压缩技术,可以大大减少需要传输的字节数。
- 当用户需要浏览某个网页,支持 SPDY 的服务器在发送网页内容时可以发送一些信息给浏览器告诉后面可能需要哪些资源,浏览器可以提前知道并决定是否需要下载。更极端的情况是,服务器可以主动发送资源。
下图是是基于SPDY协议的网络栈结构
与前面的调用栈结构有三点不同:
- 虚线框表示可能有多个SpdyStream和SpdyHttpStream对象,也就是多个流使用一个SpdySession会话,同时使用一个socket连接。多多个Stream对象的管理、删除、创建、数量限制等都是由SpdySession来处理。
- 对于之前的一些类,Spdy有专门的实现,因为需要支持新协议的关系。
- SpdyHttpStream类继承自之前的HttpStream类会对应一个SpdyStream并将Spdy协议部分等实际工作交给SpdyStream类来做
(4)QUIC
新的网络传输协议,改进 UDP 数据协议的能力。同SPDY建立传输层之上不同,QUIC所要解决的问题就是传输层的传输效率,并提供了数据加密。所以,SPDY可以在QUIC上工作。
3.7 实践:chromium网络工具和信息
Chromium提供了网路信息工具Chrome://net-internals.可以看到Capture、Export、Import、Proxy、Events、Timeline、DNS、Sockets、SPDY、QUIC、Pipelining、擦车、SPIs、Tests、HSTS、Bandwidth、Prerender等类别。
首先是Events类别,该类别记录了所有网络栈完成的工作和传送消息。记录ID、对象类。其中有些以_JOB结尾的类,表示一个个的任务,这些任务可能是连接、域名解析等。它们不负责具体的工作,只起到一层桥接和封装的作用,任务完成后就直接结束了,当用户单击表中一项的时候,当前页面会给当前对象从过去到现在发生的各个操作,或者叫事件。
其次是类别“Timeline”.它的含义就是一个按照时间绘制的图,图中记录在各个时间点Chromium 使用网络资源。
相关文章:

《WebKit 技术内幕》之四(3): 资源加载和网络栈
3. 网络栈 3.1 WebKit的网络设施 WebKit的资源加载其实是交由各个移植来实现的,所以WebCore其实并没有什么特别的基础设施,每个移植的网络实现是非常不一样的。 从WebKit的代码结构中可以看出,网络部分代码的确比较少的,它们都在…...

vue3-模板引用
//1.调用ref函数 -> ref对象 const h1Ref ref(null) const comRef ref(null) //组件挂载完毕之后才能获取 onMounted(()>{console.log(h1Ref.value);console.log(comRef.value); })<div class"father"><!-- 通过ref标识绑定ref对象 --><h2 re…...

聚类模型评估指标
聚类模型评估指标-轮廓系数 计算样本i到同簇其它样本到平均距离ai,ai越小,说明样本i越应该被聚类到该簇(将ai称为样本i到簇内不相似度);计算样本i到其它某簇Cj的所有样本的平均距离bij,称为样本i与簇Cj的…...

测试 ASP.NET Core 中间件
正常情况下,中间件会在主程序入口统一进行实例化,这样如果想单独测试某一个中间件就很不方便,为了能测试单个中间件,可以使用 TestServer 单独测试。 这样便可以: 实例化只包含需要测试的组件的应用管道。发送自定义请…...
智能小程序小部件(Widget)媒体组件属性说明和示例代码汇总
camera 基础库 2.2.0 开始支持, 低版本需做兼容处理。 系统相机。相关 API:ty.createCameraContext。这是基于异层渲染的原生组件, 请注意原生组件使用限制。 属性说明 属性名类型默认值必填说明modestringnormal否应用模式,只在初始化时有效ÿ…...
enum的比较
enum的比较 使用enum定义的枚举类是一种引用类型。 前面我们讲到,引用类型比较,要使用equals()方法,如果使用比较,它比较的是两个引用类型的变量是否是同一个对象。 因此,引用类型比较,要始终使用equals…...

网工每日一练(1月15日)
1.某计算机系统由下图所示的部件构成,假定每个部件的千小时可靠度为R,则该系统的千小时的可靠度为 ( D ) 。 2.以下IP地址中,属于网络 201.110.12.224/28 的主机IP是( B )。 A.201.110.12.224 B.201.110.12.238 C.20…...
henauOJ 1113: 计算x的n次方
题目描述 给定x和n,计算出x^n。 0<x<10,0<n<9; 要求定义一个函数:int pow1(int x,int n);//返回x^n。 本题只允许提交函数及其内容,提交多余内容会编译错误 C/C函数原型 int pow1(int x,int n); Java函数原型 public static int pow1(int…...

64.Spring事件监听的核心机制是什么?
Spring事件监听的核心机制是什么? spring的事件监听有三个部分组成 事件(ApplicationEvent) 负责对应相应监听器 事件源发生某事件是特定事件监听器被触发的原因监听器(ApplicationListener) 对应于观察者模式中的观察者。监听器监听特定事件,并在内部定义了事件发生后的响应…...

《C++大学教程》3.12Account类
题目: (Account类)创建一个名为Account 的类,银行可以使用它表示客户的银行账户。这个类应该包括一个类型为int 的数据成员,表示账户余额。【注意:在后续章节中,将使用称为浮点值的包含小数点的数(例如2.75)表示美元数。】 这个…...
【工作记录】基于springboot3+springsecurity实现多种方式登录及鉴权(二)
前言 上篇文章介绍了基于springboot3+springsecurity实现的基于模拟数据的用户多种方式登录及鉴权的流程和代码实现,本文我们继续完善。 主要完善的点 主要通过如下几个点来完成优化和完善: 用户信息获取通过查询mysql数据库实现token生成方式使用jwt用户信息存储及读取使用…...
CSS笔记III
选择器 结构伪类选择器 作用:根据元素的结构关系查找元素 选择器说明E:first-child查找第一个E元素E:last-child查找最后一个E元素E:nth-child(N)查找第N个E元素(第一个元素N值为1) nth-child(公式) 作用是可以根据元素的结构关系查找多个元素 偶数标签&#x…...
openssl3.2 - 官方demo学习 - mac - siphash.c
文章目录 openssl3.2 - 官方demo学习 - mac - siphash.c概述笔记END openssl3.2 - 官方demo学习 - mac - siphash.c 概述 MAC算法为 SIPHASH, 设置参数(C-rounds, D-rounds, 也可以不设置, 有默认值) 用key初始化MAC算法, 算明文的MAC值 笔记 /*! \file siphash.c \note op…...

(六)深入理解Bluez协议栈之“GATT Client Profile”
前言: 本章节我们继续介绍GATT Client Profile的实现,参考的程序是tools\btgatt-client.c,需要注意的一点,在./configure时,需要添加 --enable-test --enable-testing才会编译该c文件,编译完成后,生成的可执行程序为btgatt-client。本文主要以btgatt-client运行时可能会…...

SVO编译
文章目录 软件版本错误编译运行轨迹路径保存运行TUM数据集 附录针对svo slam的/svo/pose_imu转为tum格式代码 软件版本 ubuntu 20 rosnoeticSVO SLAM虚拟机 windows 11 错误 常见的git clone问题可以使用DevSidecar解决,在 加速服务-基本设置-绑定IP 设置为0.0.0…...

探索未知:最新发布的顶级浏览器,为你带来前所未有的浏览体验
目录 学习目标: 学习内容: 学习时间: 学习产出: 基本功能和操作方法: 如何打开和关闭浏览器 如何新建和关闭标签页 前进、后退、刷新页面等导航操作 使用地址栏和搜索栏进行访问和搜索 管理浏览器的历史记录 高级功能&…...

EasyX图形化学习(三)
1.帧率: 即每秒钟界面刷新次数,下面以60帧为例: 1.数据类型 clock_t: 用来保存时间的数据类型。 2.clock( ) 函数: 用于返回程序运行的时间,无需参数。 3.例子: 先定义所需帧率: const …...

git-生成证书、公钥、私钥、error setting certificate verify locations解决方法
解决方法 方法1-配置证书、公钥、私钥打开Git Bash设置名称和邮箱执行,~/.ssh执行,ssh-keygen -t rsa -C "这是你的邮箱",如图:进入文件夹可以看到用记事本之类的软件打开id_rsa.pub文件,并且复制全部内容。…...

论文笔记(四十)Goal-Auxiliary Actor-Critic for 6D Robotic Grasping with Point Clouds
Goal-Auxiliary Actor-Critic for 6D Robotic Grasping with Point Clouds 文章概括摘要1. 介绍2. 相关工作3. 学习 6D 抓握政策3.1 背景3.2 从点云抓取 6D 策略3.3 联合运动和抓握规划器的演示3.4 行为克隆和 DAGGER3.5 目标--辅助 DDPG3.6 对未知物体进行微调的后视目标 4. 实…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...