Godot 4 源码分析 - 碰撞
碰撞功能应该是一个核心功能,它能自动产生相应的数据,比如目标对象进入、离开本对象的检测区域。
基于属性设置,能碰撞的都具备这样的属性:Layer、Mask.

在Godot 4中,Collision属性中的Layer和Mask属性是用于定义碰撞过滤的重要参数。它们允许控制哪些物体可以与该节点进行碰撞检测。
-
Layer(图层):
- Layer是所有节点都具有的属性,用于将节点分组到不同的图层中。Layer是一组位掩码(bitmask),每个位代表一个特定的碰撞图层。每个物体都可以分配一个或多个碰撞图层。通过将物体分配到特定的碰撞图层,可定义其所属的逻辑组
- 每个节点可以属于一个或多个图层。你可以在节点的属性面板中的Layer部分选择一个或多个图层。
- Layer属性定义了节点所属的图层。默认情况下,节点属于基本图层(Base Layer)。
- 使用不同图层将场景中的节点分组,可以使你能够仅与特定图层上的节点进行碰撞检测。
-
Mask(掩码):
- Mask也是所有节点都具有的属性,用于指定该节点对碰撞的兴趣。Mask也是一组位掩码,用于指示物体可以与哪些碰撞图层的物体发生碰撞检测。每个物体都可以指定一个碰撞掩码。通过设置碰撞掩码,可定义物体与哪些碰撞图层的物体发生碰撞
- 每个节点都有一个掩码值。可以在节点的属性面板中的Collision属性下设置掩码。
- Mask属性定义了该节点对哪些图层的节点感兴趣,该节点将与这些图层中的其他节点进行碰撞检测。
- 每个节点的掩码值是一个32位的整数,每一位代表一个图层。0表示不兴趣该图层,1表示兴趣该图层。可以使用位操作(如按位与和按位或)来设置和检查掩码值。
通过使用Layer和Mask属性,你可以灵活地控制碰撞检测,使得只特定图层中的节点相互交互。例如,你可以设置一个节点仅与属于某个特定图层的节点进行碰撞,而忽略其他图层的节点。
需要注意的是,为了让两个节点进行碰撞检测,它们的Layer和Mask需要同时满足一定条件。具体而言,一个节点的Layer值必须包含在另一个节点的Mask值中,同时另一个节点的Layer值必须包含在该节点的Mask值中。
通过合理设置Layer和Mask属性,可在Godot 4中创建精细且灵活的碰撞过滤系统,以实现各种复杂的物理效果和游戏机制。
假设有两个碰撞图层,一个是"Player",另一个是"Enemy"。有一个玩家角色和一些敌人,想要确保玩家和敌人之间发生碰撞,但是敌人之间不发生碰撞。
-
对于玩家对象:将其分配到"Player"碰撞图层,并将其Collision Mask设置为"Enemy"的位掩码。这将使玩家只与"Enemy"图层的物体发生碰撞。
-
对于敌人对象:将它们分配到"Enemy"碰撞图层,并将其Collision Mask设置为"Player"的位掩码。这将使敌人只与"Player"图层的物体发生碰撞。
对于敌人对象之间,可将它们分配到相同的碰撞图层并设置相应的碰撞掩码,以确保它们不会互相碰撞。
通过这种方式,可在复杂的场景中更精细地控制碰撞的交互,使碰撞逻辑更加清晰和可管理。
从理解角度来说,逻辑说起来复杂,技术上实现很简单
Layer与Mask应该都是32位整数,对象A有Layer与Mask属性,对象B也有Layer与Mask属性
如果 A.Layer & B.Mask 非零,则 A要与B发生碰撞
如果 A.Mask & B.Layer非零,则B要与A发生碰撞
还是有点绕,再多想一下,以下是我自己的想法,不一定正确:
其实,Layer与Mask都是逻辑概念,虚拟的。在真实场景中,A、B都占据相应的空间位置(3D)或平面位置(2D),在运行过程中,A、B至少有一个能动,它们的相对位置可能会变化,在某个时刻会有交叠。这个时候,godot引擎知道,因为它能实时计算。那么,godot发现两个对象发生交叠了,怎么办呢?
那就检查A、B的Layer与Mask。
- 如果 A.Layer & B.Mask 非零,则B能检测到A,触发B的body_entered信号,实参为A。
- 如果 B.Layer & A.Mask 非零,则A能检测到B,触发A的body_entered信号,实参为B。
其实上面的说法不严谨,因为body_entered信号不应该连续发送,而是有个进出状态。整体连起来应该就是:
- A、B分别维护自己的body_map,知道与自己交叠的对象列表。这个工作不能交给godot引擎做,它多忙的,还是各自管好自己的事
- 从源码看出,检测进出的状态标志,还是A、B自己完成,也就是说,遍历所有的监控对象monitored_bodies,看与自己的??状态E->value,若为0,则啥事没有,不再监控该对象。如果>0,则为AREA_BODY_ADDED,表示进入。如果<0,则为AREA_BODY_REMOVED,表示离开。
void GodotArea2D::call_queries() {if (!monitor_callback.is_null() && !monitored_bodies.is_empty()) {if (monitor_callback.is_valid()) {Variant res[5];Variant *resptr[5];for (int i = 0; i < 5; i++) {resptr[i] = &res[i];}for (HashMap<BodyKey, BodyState, BodyKey>::Iterator E = monitored_bodies.begin(); E;) {if (E->value.state == 0) { // Nothing happenedHashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;++next;monitored_bodies.remove(E);E = next;continue;}res[0] = E->value.state > 0 ? PhysicsServer2D::AREA_BODY_ADDED : PhysicsServer2D::AREA_BODY_REMOVED;res[1] = E->key.rid;res[2] = E->key.instance_id;res[3] = E->key.body_shape;res[4] = E->key.area_shape;HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;++next;monitored_bodies.remove(E);E = next;Callable::CallError ce;Variant ret;monitor_callback.callp((const Variant **)resptr, 5, ret, ce);if (ce.error != Callable::CallError::CALL_OK) {ERR_PRINT_ONCE("Error calling event callback method " + Variant::get_callable_error_text(monitor_callback, (const Variant **)resptr, 5, ce));}}} else {monitored_bodies.clear();monitor_callback = Callable();}}if (!area_monitor_callback.is_null() && !monitored_areas.is_empty()) {if (area_monitor_callback.is_valid()) {Variant res[5];Variant *resptr[5];for (int i = 0; i < 5; i++) {resptr[i] = &res[i];}for (HashMap<BodyKey, BodyState, BodyKey>::Iterator E = monitored_areas.begin(); E;) {if (E->value.state == 0) { // Nothing happenedHashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;++next;monitored_areas.remove(E);E = next;continue;}res[0] = E->value.state > 0 ? PhysicsServer2D::AREA_BODY_ADDED : PhysicsServer2D::AREA_BODY_REMOVED;res[1] = E->key.rid;res[2] = E->key.instance_id;res[3] = E->key.body_shape;res[4] = E->key.area_shape;HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;++next;monitored_areas.remove(E);E = next;Callable::CallError ce;Variant ret;area_monitor_callback.callp((const Variant **)resptr, 5, ret, ce);if (ce.error != Callable::CallError::CALL_OK) {ERR_PRINT_ONCE("Error calling event callback method " + Variant::get_callable_error_text(area_monitor_callback, (const Variant **)resptr, 5, ce));}}} else {monitored_areas.clear();area_monitor_callback = Callable();}}
}
- 根据是否为AREA_BODY_ADDED确定body_in标志。若为body_in,则触发tree_entered、tree_exiting信号,如果本对象在工作场景中,则触发body_entered,参数为node。顺便还触发了body_shape_entered信号,看着用吧。
- 若body_in为false,则触发tree_entered、tree_exiting。如果本对象在工作场景中,则触发body_exited、body_shape_exited
void Area2D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_area_shape) {bool body_in = p_status == PhysicsServer2D::AREA_BODY_ADDED;ObjectID objid = p_instance;Object *obj = ObjectDB::get_instance(objid);Node *node = Object::cast_to<Node>(obj);HashMap<ObjectID, BodyState>::Iterator E = body_map.find(objid);if (!body_in && !E) {return; //does not exist because it was likely removed from the tree}lock_callback();locked = true;if (body_in) {if (!E) {E = body_map.insert(objid, BodyState());E->value.rid = p_body;E->value.rc = 0;E->value.in_tree = node && node->is_inside_tree();if (node) {node->connect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree).bind(objid));node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree).bind(objid));if (E->value.in_tree) {emit_signal(SceneStringNames::get_singleton()->body_entered, node);}}}E->value.rc++;if (node) {E->value.shapes.insert(ShapePair(p_body_shape, p_area_shape));}if (!node || E->value.in_tree) {emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_area_shape);}} else {E->value.rc--;if (node) {E->value.shapes.erase(ShapePair(p_body_shape, p_area_shape));}bool in_tree = E->value.in_tree;if (E->value.rc == 0) {body_map.remove(E);if (node) {node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area2D::_body_enter_tree));node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area2D::_body_exit_tree));if (in_tree) {emit_signal(SceneStringNames::get_singleton()->body_exited, obj);}}}if (!node || in_tree) {emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, obj, p_body_shape, p_area_shape);}}locked = false;unlock_callback();
}
到此,就该关注monitored_bodies,它在add_body_to_query、remove_body_from_query中维护
void GodotArea2D::add_body_to_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {BodyKey bk(p_body, p_body_shape, p_area_shape);monitored_bodies[bk].inc();if (!monitor_query_list.in_list()) {_queue_monitor_update();}
}void GodotArea2D::remove_body_from_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {BodyKey bk(p_body, p_body_shape, p_area_shape);monitored_bodies[bk].dec();if (!monitor_query_list.in_list()) {_queue_monitor_update();}
}
但这没看到Layer与Mask属性的作用。那就倒查。最终发现在GodotCollisionObject2D类中:
_FORCE_INLINE_ bool collides_with(GodotCollisionObject2D *p_other) const {return p_other->collision_layer & collision_mask;
}
果然,与猜测的一致。具体调用collides_with是在一些setup函数中。
bool GodotAreaPair2D::setup(real_t p_step) {bool result = false;if (area->collides_with(body) && GodotCollisionSolver2D::solve(body->get_shape(body_shape), body->get_transform() * body->get_shape_transform(body_shape), Vector2(), area->get_shape(area_shape), area->get_transform() * area->get_shape_transform(area_shape), Vector2(), nullptr, this)) {result = true;}process_collision = false;has_space_override = false;if (result != colliding) {if ((int)area->get_param(PhysicsServer2D::AREA_PARAM_GRAVITY_OVERRIDE_MODE) != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED) {has_space_override = true;} else if ((int)area->get_param(PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE) != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED) {has_space_override = true;} else if ((int)area->get_param(PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE) != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED) {has_space_override = true;}process_collision = has_space_override;if (area->has_monitor_callback()) {process_collision = true;}colliding = result;}return process_collision;
}bool GodotBodyPair2D::setup(real_t p_step) {check_ccd = false;if (!A->interacts_with(B) || A->has_exception(B->get_self()) || B->has_exception(A->get_self())) {collided = false;return false;}collide_A = (A->get_mode() > PhysicsServer2D::BODY_MODE_KINEMATIC) && A->collides_with(B);collide_B = (B->get_mode() > PhysicsServer2D::BODY_MODE_KINEMATIC) && B->collides_with(A);report_contacts_only = false;if (!collide_A && !collide_B) {if ((A->get_max_contacts_reported() > 0) || (B->get_max_contacts_reported() > 0)) {report_contacts_only = true;} else {collided = false;return false;}}//use local A coordinates to avoid numerical issues on collision detectionoffset_B = B->get_transform().get_origin() - A->get_transform().get_origin();_validate_contacts();const Vector2 &offset_A = A->get_transform().get_origin();Transform2D xform_Au = A->get_transform().untranslated();Transform2D xform_A = xform_Au * A->get_shape_transform(shape_A);Transform2D xform_Bu = B->get_transform();xform_Bu.columns[2] -= offset_A;Transform2D xform_B = xform_Bu * B->get_shape_transform(shape_B);GodotShape2D *shape_A_ptr = A->get_shape(shape_A);GodotShape2D *shape_B_ptr = B->get_shape(shape_B);Vector2 motion_A, motion_B;if (A->get_continuous_collision_detection_mode() == PhysicsServer2D::CCD_MODE_CAST_SHAPE) {motion_A = A->get_motion();}if (B->get_continuous_collision_detection_mode() == PhysicsServer2D::CCD_MODE_CAST_SHAPE) {motion_B = B->get_motion();}bool prev_collided = collided;collided = GodotCollisionSolver2D::solve(shape_A_ptr, xform_A, motion_A, shape_B_ptr, xform_B, motion_B, _add_contact, this, &sep_axis);if (!collided) {oneway_disabled = false;if (A->get_continuous_collision_detection_mode() == PhysicsServer2D::CCD_MODE_CAST_RAY && collide_A) {check_ccd = true;return true;}if (B->get_continuous_collision_detection_mode() == PhysicsServer2D::CCD_MODE_CAST_RAY && collide_B) {check_ccd = true;return true;}return false;}if (oneway_disabled) {return false;}if (!prev_collided) {if (shape_B_ptr->allows_one_way_collision() && A->is_shape_set_as_one_way_collision(shape_A)) {Vector2 direction = xform_A.columns[1].normalized();bool valid = false;for (int i = 0; i < contact_count; i++) {Contact &c = contacts[i];if (c.normal.dot(direction) > -CMP_EPSILON) { // Greater (normal inverted).continue;}valid = true;break;}if (!valid) {collided = false;oneway_disabled = true;return false;}}if (shape_A_ptr->allows_one_way_collision() && B->is_shape_set_as_one_way_collision(shape_B)) {Vector2 direction = xform_B.columns[1].normalized();bool valid = false;for (int i = 0; i < contact_count; i++) {Contact &c = contacts[i];if (c.normal.dot(direction) < CMP_EPSILON) { // Less (normal ok).continue;}valid = true;break;}if (!valid) {collided = false;oneway_disabled = true;return false;}}}return true;
}
这就没有必要再跟下去了。至于这些setup函数什么时候被调用,可以用一个实际项目调试来看看。
主要核心思想是理解碰撞的Layer与Mask配置问题。
相关文章:
Godot 4 源码分析 - 碰撞
碰撞功能应该是一个核心功能,它能自动产生相应的数据,比如目标对象进入、离开本对象的检测区域。 基于属性设置,能碰撞的都具备这样的属性:Layer、Mask. 在Godot 4中,Collision属性中的Layer和Mask属性是用于定义碰撞…...
前端面试经典算法题
前言 现在面试流行考核算法,做过面试官,也被面试。问算法对面试官来说,是一种解脱,找出了一个看似很高明且能偷懒的办法选择人,避免了不知道问啥的尴尬;被面试者,也找到了一种新的面试八股文&am…...
ospf减少LSA更新
实验及实验要求 一、思路 1.根据区域划分IP地址 2.使公网可通---写缺省 3.使R3成为MGRE中心站点,R5、R6、R7为分支站点 4.一个个去配置ospf区域和RIP区域,确保每个区域配置无误 5.区域0要更改OSPF在接口的工作类型为broadcast ,并使R3为…...
万字长文解析深度学习中的术语
引言 新手在学习深度学习或者在看深度学习论文的过程中,有不少专业词汇,软件翻译不出来,就算是翻译出来也看不懂,因为不少术语是借用其他学科的概念,这里整理了一些在深度学习中常见的术语,并对一些概念进…...
冠达管理投资前瞻:三星加码机器人领域 大信创建设提速
上星期五,沪指高开高走,盘中一度涨超1%打破3300点,但随后涨幅收窄;深成指、创业板指亦强势震动。截至收盘,沪指涨0.23%报3288.08点,深成指涨0.67%报11238.06点,创业板指涨0.95%报2263.37点&…...
24届近5年上海交通大学自动化考研院校分析
今天给大家带来的是上海交通大学控制考研分析 满满干货~还不快快点赞收藏 一、上海交通大学 学校简介 上海交通大学是我国历史最悠久、享誉海内外的高等学府之一,是教育部直属并与上海市共建的全国重点大学。经过120多年的不懈努力,上海交…...
【PDF密码】PDF文件不能打印,为什么?
正常的PDF文件是可以打印的,如果PDF文件打开之后发现文件不能打印,我们需要先查看一下自己的打印机是否能够正常运行,如果打印机是正常的,我们再查看一下,文件中的打印功能按钮是否是灰色的状态。 如果PDF中的大多数功…...
LeetCode-Java(03)
9. 回文数 class Solution {public boolean isPalindrome(int x) {if (x < 0 || (x % 10 0 && x ! 0)) {return false;}int revertedNumber 0;while (x > revertedNumber) {revertedNumber revertedNumber * 10 x % 10;x / 10;}// 当长度为奇数时通过reverte…...
【Linux命令行与Shell脚本编程】第十六章 Shell函数
Linux命令行与Shell脚本编程 第一章 文章目录 Linux命令行与Shell脚本编程六.函数6.1.脚本函数基础6.1.1.创建函数6.1.2.使用函数 6.2.函数返回值6.2.1.默认的退出状态码6.2.2.使用return命令6.2.3.使用函数输出 6.3.函数中使用变量6.3.1.向函数传递参数6.3.2.在函数中处理变量…...
SpringCloud-Hystrix服务熔断与降级工作原理源码 | 京东物流技术团队
先附上Hystrix源码图 在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplateRibbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。…...
(一)react脚手架
1. react脚手架 react提供了一个用于创建react项目的脚手架库:create-react-app 项目的整体技术架构为:react webpack es6 eslint 使用脚手架开发的项目的特点:模块化、组件化、工程化 2. 创建项目并启动 # 第一步: 全局安…...
Typescript中的元组与数组的区别
Typescript中的元组与数组的区别 元组可以应用在经纬度这样明确固定长度和类型的场景下 //元组和数组类似,但是类型注解时会不一样//元组赋值的类型、位置、个数需要和定义的类型、位置、个数完全一致,不然会报错。 // 数组 某个位置的值可以是注解中的…...
SpringBoot的index首页的访问、自定义Favicon图标
目录 1. index首页1.1 index首页访问规则的源码1.2 index首页的访问 2. 自定义Favicon图标 1. index首页 1.1 index首页访问规则的源码 package org.springframework.boot.autoconfigure.web.servlet; ......省略部分......// SpringBoot给容器中放WebMvcConfigurationSuppor…...
【C++】C++文件操作-文本文件/二进制文件
0.前言 一、文本文件 1.写文件 代码 #include <iostream> using namespace std; #include <fstream> //头文件包含//************************************** //文本文件 写文件 void test01() {//1.包含文件 fstream//2.创建流对象ofstream ofs;//3.指导打开方式…...
java通过http网络url下载文件
Testpublic void test3() throws ParseException {String fileUrl "http://*****/123.pdf";String savePath "C:\\Users\\HHH\\Desktop\\文件\\123.pdf";try {URL url new URL(fileUrl);InputStream inputStream url.openStream();Path outputPath Pa…...
网络安全【黑客】自学
1.什么是网络安全? 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域,都有…...
PCA和自动编码器:每个人都能理解的算法
一、说明 本文的主要重点是提供主成分分析 (PCA) 和自动编码器数据转换技术的直观信息。我不打算深入研究支撑这些模型的数学理论,因为已经有大量的资源可用。 二、pca降维和自编码 2.1 pca和自编码的共同点 自动编码器通过组合数据最重要的特…...
C++——STL容器【priority_queue】模拟实现
本章代码:优先级队列模拟实现、priority_queue文档 文章目录 🐈1. priority_queue介绍🦄2. priority_queue模拟实现🐧2.1 构造函数🐧2.2 建堆向下调整向上调整 🐧2.3 仿函数🐧2.4 push & po…...
SpringBoot实现文件记录日志,日志文件自动归档和压缩
😊 作者: Eric 💖 主页: https://blog.csdn.net/weixin_47316183?typeblog 🎉 主题:SpringBoot实现文件记录日志,日志文件自动归档和压缩 ⏱️ 创作时间: 2023年08月06日 文章目…...
MySQL 窗口函数
聚合函数作为窗口函数 设聚合函数为op语法结构: op(字段名A) over(partition by 字段名B order by 字段名C rows between D1 and D2) 其中: partition by:按照某一字段将数据进行分组 order by:按照某一字段将数据进行排序&…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
