目录
1.采用弱密码或者无密码进行登录(弱口令)
2.密码可爆破
3.验证码可爆破
4.短信轰炸
5.手机验证码凭证可查看
6.万能验证码
7.前端验证登录结果
8.任意用户密码找回/重置
9.未授权访问他人账号
10.用户批量注册
11.注册导致存储型xss
12.URL跳转(重定向)漏洞
13.CSRF漏洞
14.登录成功凭证可复用
1.采用弱密码或者无密码进行登录(弱口令) 比如:
管理员账号:admin
密码:admin/123456/010203
测试账号:111
密码:111
万能账号:admin' or '1'='1(登录的sql注入)
修复建议:
前端提醒用户提高密码复杂度
后端检查数据库中测试环境余留下的账号
前端对密码做加密(加密流程代码最好隐藏)
2.密码可爆破 可以通过密码字典,不断请求,爆破出密码
修复建议:
后端限制一个账号请求次数,次数过多锁定账号
前端输入验证码(类似谷歌的复杂验证码最好,否则可用pkav进行识别爆破,且验证码不可复用,前端验证,容易导致复用)
3.验证码可爆破 可以通过从0000遍历到9999来破解验证码
修复建议:
加长验证码长度(加长攻击者爆破时间)
限制尝试次数(采用后端验证)
限制验证码的有效时间(1分钟内有效)
4.短信轰炸 未对发送验证码进行时间限制,导致可进行反复抓包重发验证码请求
修复建议:
前端,后端定时限制
5.手机验证码凭证可查看 当对一个手机号发送验证码之后,后端会给一个包含验证码的返回包
前端hide属性标签的隐藏有验证码,可通过F12查看
修复建议:
后端不返回验证码
前端控制台不显示验证码
6.万能验证码 1)验证码可复用
攻击使用以前使用过的验证码来通过验证
2)测试方便遗留下的万能验证码0000/1111/6666
修复建议:
后端判断验证码是否被使用并且销毁
去掉测试时遗留下的验证码
7.前端验证登录结果 点击登录之后,由后端返回登录结果
如:{“result”:false} 如果是前端验证,直接改为:{“result”:true}即可成功登录
修复建议:
使用后端验证
8.任意用户密码找回/重置 1)找回或重置时,发送验证码的手机号,未做绑定,导致可以抓包,修改发送验证码的手机号(比如自己的手机号)并且成功获取验证码
2)可以通过修改 密码找回或重置的步骤参数,直接到最后一步,直接进行修改或者重置
如url/?step=1 抓包修改为 step=3直接跳到最后一步
【Spring源码系列- IOC】
1
【Spring源码】0.安装Gradle环境
2
【Spring源码】1.下载与编译_pom relocation to an other version number is not f
3
【Spring源码】2.试个水先~Debug找到传说中的三级缓存(图解向,堆图预警)
4
【Spring源码】3. xml文件如何转换成BeanDefinition(主要涉及prepareRefresh()+ obtainFreshBeanFactory()两个函数,图解向,堆图预警)_spring xml转bean
5
【Spring源码】4. 自己搞个标签?~自定义标签保姆级全过程(图解向,堆图预警)
6
【Spring源码】5.spring的bean工厂准备工作(prepareBeanFactory(beanFactory)
7
【Spring源码】6. Spring扩展自定义属性编辑器保姆级教程
8
【Spring源码】7. 如何添加自定义的BeanFactoryPostProcessor
9
【Spring源码】8. 捋下invokeBeanFactoryPostProcessors()主要处理流程
10
【Spring源码】9. 超级重要的ConfigurationClassPostProcessor
11
【Spring源码】10. 递归调用的processConfigurationClass()方法
12
【Spring源码】11. 我是注解类不?checkConfigurationClassCandidate()注解类判断方法详解
13
【Spring源码】12. 注册bean处理器registerBeanPostProcessors()
14
【Spring源码】13. 国际化处理initMessageSource()源码解析
【补充内容】【保姆级】SpringBoot项目中的i18n国际化
15
【Spring源码】14. 消息多播器(观察者模式)
【补充内容】【保姆级示例向】观察者模式
16
【Spring源码】15. Bean的创建过程(1.概述篇)
17
【Spring源码】16. Bean的创建过程(2)
18
【Spring源码】17.创建Bean这篇认真的@(・●・)@
【补充内容】
【保姆级·创建对象】如何通过Supplier创建对象
【保姆级·创建对象】如何通过factory-method创建对象
【保姆级·创建对象】如何利用resolveBeforeInstantiation()在预处理阶段返回一个Bean的实例对象
19
【Spring源码系列- IOC】
1
【Spring源码】0.安装Gradle环境
2
【Spring源码】1.下载与编译_pom relocation to an other version number is not f
3
【Spring源码】2.试个水先~Debug找到传说中的三级缓存(图解向,堆图预警)
4
【Spring源码】3. xml文件如何转换成BeanDefinition(主要涉及prepareRefresh()+ obtainFreshBeanFactory()两个函数,图解向,堆图预警)_spring xml转bean
5
【Spring源码】4. 自己搞个标签?~自定义标签保姆级全过程(图解向,堆图预警)
6
【Spring源码】5.spring的bean工厂准备工作(prepareBeanFactory(beanFactory)
7
【Spring源码】6. Spring扩展自定义属性编辑器保姆级教程
8
【Spring源码】7. 如何添加自定义的BeanFactoryPostProcessor
9
【Spring源码】8. 捋下invokeBeanFactoryPostProcessors()主要处理流程
10
【Spring源码】9. 超级重要的ConfigurationClassPostProcessor
11
【Spring源码】10. 递归调用的processConfigurationClass()方法
12
【Spring源码】11. 我是注解类不?checkConfigurationClassCandidate()注解类判断方法详解
13
【Spring源码】12. 注册bean处理器registerBeanPostProcessors()
14
【Spring源码】13. 国际化处理initMessageSource()源码解析
【补充内容】【保姆级】SpringBoot项目中的i18n国际化
15
【Spring源码】14. 消息多播器(观察者模式)
【补充内容】【保姆级示例向】观察者模式
16
【Spring源码】15. Bean的创建过程(1.概述篇)
17
【Spring源码】16. Bean的创建过程(2)
18
【Spring源码】17.创建Bean这篇认真的@(・●・)@
【补充内容】
【保姆级·创建对象】如何通过Supplier创建对象
【保姆级·创建对象】如何通过factory-method创建对象
【保姆级·创建对象】如何利用resolveBeforeInstantiation()在预处理阶段返回一个Bean的实例对象
19
配置:
AR1:
# interface GigabitEthernet0/0/0 ip address 10.1.2.1 255.255.255.0 # interface GigabitEthernet0/0/1 # interface GigabitEthernet0/0/2 # interface NULL0 # interface LoopBack0 ip address 10.1.1.1 255.255.255.255 # ip route-static 10.1.3.0 255.255.255.0 10.1.2.2 ip route-static 10.1.4.0 255.255.255.0 10.1.2.2 AR2:
# interface GigabitEthernet0/0/0 ip address 10.1.2.1 255.255.255.0 # interface LoopBack0 ip address 10.1.1.1 255.255.255.0 # rip 1 version 2 network 10.0.0.0 AR3: # ip vpn-instance a1 ipv4-family route-distinguisher 2:2 vpn-target 2:2 export-extcommunity vpn-target 2:2 import-extcommunity # ip vpn-instance b1 ipv4-family route-distinguisher 1:1 vpn-target 1:1 export-extcommunity vpn-target 1:1 import-extcommunity # mpls lsr-id 3.
在学习多态之前我们先来了解一下什么是多态,在文章的最后我会给出四种多态实现的示例代码,来方便大家的参考和学习。
学习和理解多态的小技巧:
1.目的是让同一段代码或函数可以实现不同的功能
2.类的指针可以调用到自己类的方法函数
多态也是面向对象设计中对人类思维方式的一种直接模拟。
什么是多态 什么是多态?表面意思就是多种状态。在一本c++的书上看到一个感觉定义比较好的说法,多态性是指一段程序能够具备处理多种类型对象的能力,这句话的意思就是同一段代码或者同一个函数可以实现不同类型数据的相同或相似功能的实现(比如不同类型数据的打印输出),即过程相同细节不同。
多态性可以通过四种形式来实现,分别是强制多态、重载多态、类型参数化多态、包含多态。
其中强制多态和重载多态属于特殊多态性,只是表面的多态性;包含多态和类型参数化多态属于一般多态性,是真正的多态。
重载多态就是利用c++的重载给同名函数赋予不同的函数体来实现多态,重载多态比较简单就不过多介绍了,最后也会给出对应的多态实现代码。
强制多态 强制多态就是将一种数据类型显示或隐式转换成另一种数据类型,即通过强制类型转换来实现的
本人之前对类型转换最多的用法就是某个数据类型与要求数据类型不匹配时,为了解决那个警告或错误就使用类型转换强转一下,让代码可以通过编译,当然要保证代码在自己的可控范围下再进行操作。
这里就用类和强制类型转换来擦出不一样的火花
我们都知道类中的成员函数,虽然是属于类的,但是成员函数并不占用类的大小。为什么会这样呢?大家细琢磨琢磨这个事如果为每个类的变量都去编译出一个函数地址,并且这些变量的相同函数实现的东西都是一模一样的,这是不是就很浪费资源,所以为了解决这个浪费就把类的方法函数拿了出来只编译出一个函数的地址,然后类定义的所有变量想访问类的方法函数时都去访问那一个地址,这样既可以节省资源也可以保证正确的函数调用,这就是类的成员函数不占用类内存的原因。
我们就可以利用类对成员方法的这种特点,来实现我们的强制多态
怎么实现呢?首先要明白一个道理如果一个变量的类型是某种自定义类型,那就可以访问这个类型下它所具备的东西。那就是让编译器认为我们这个变量就是类定义的一个变量,这样就可以去访问这个类的成员函数。那问题又来了如何去让编译器认为这个变量是通过类来声明的呢?那就是把变量转换为类的变量或指针(类型转换)。
下面用一段简单的代码来实际实现一下:
class Animal { public: void cry() { printf("a = %d\n", a); std::cout << "叫~" << std::endl; } Animal(int _a) { a = _a; } int a; }; class duck { public: void cry() { printf("a = %d\n", a); std::cout << "ga ga ga" << std::endl; } duck(int _a = 2) { a = _a; } int a; }; int main(int argc, char* argv[]) { Animal _tes(5); Animal* _tes2 = new Animal(4); _tes.
目录 0 配置文件内容0.1 ###Units单位###0.2. ###INCLUDES包含###0.3 ######### NETWORK #####(网络配置*重点)0.4 ###GENERAL通用###0.5 ###SECURITY安全###0.6 ###### CLIENTS######### 1 数据类型1.1 Key1.2 String(字符串)数据结构常用命令 1.3List(列表)数据结构常用命令 1.4 Set(集合)数据结构常用命令 1.5 Zset(sorted set) 有序集合数据结构常用命令 1.6 Hash(哈希)数据结构常用命令 1.7 Bitmaps数据结构常用命令 1.8 HyperLogLog常用命令 1.9 Geospatial作用常用命令 2 Redis事务与锁机制2.1 Redis事务的三特性2.1 Redis事务的开启与执行2.2 Redis的锁机制Redis锁的开启与关闭解决乐观锁可能导致的库存遗留问题 3 Redis持久化3.1 RDB(Redis Data Base)生成文件 3.2 AOF(Append Of File)生成文件AOF启动/修复/恢复同步频率设置 3.3 两种方法优劣对比 4 主从复制4.1 如何开启主从复制4.2 常用主从复制策略一主二仆薪火相传反客为主哨兵模式(sentinel)-反客为主自动版本 4.3 复制原理 5 集群5.1 集群启动方式5.2 集群数据分配与查询逻辑常用命令故障恢复 5.3 集群的不足 6 Redis常见应用问题6.1 缓存穿透6.2 缓存击穿6.3 缓存雪崩 TCP端口6379,默认16个库,从0开始
所有库密码相同
Redis是单线程+多路IO复用技术
启动命令
[root@xgms_VM-8-13-centos ~]# redis-server /etc/redis.
前言 为什么要用VueUse VueUse是一款优秀的函数工具集,得到了Vue官方的认可
组合式 API 提供的逻辑复用能力孵化了一些非常棒的社区项目,比如 VueUse,一个不断成长的工具型组合式函数集合。
——Vue.js文档之为什么要有组合式 API?
另外,Element Plus也使用了VueUse,可见它是可靠的。
可惜的是,这玩应没有中文文档,而且它的文档还专门解释了短期内不会有翻译计划(🐮🍺)。
为了方便我自己和更多小伙伴使用这个工具,我将在这里持续更新VueUse的个人翻译。
翻译范围 本文将翻译VueUse的核心方法的简介一览,这可能比翻译每个方法的使用还更有价值,因为很多时候我们的时间是浪费在寻找我们想要的Function。
同时我会尽可能描述这些方法的使用场景、与原生方法的区别。
本文基于10.0.2版本(2023年4月14日发布),由于VueUse还在不断发展,如果有小伙伴发现我的内容已经过时,欢迎指出,我会尽快更新。
类别 VueUse的核心方法(Core Function)分成12类,分别是:
State - 状态,共13个Element - HTML元素,共15个Browser - 浏览器,共41个Sensors - 传感器(鼠标、键盘、电池、屏幕等),共37个Network - 网络,共3个Animation - 动画,共9个Component - 组件,共16个Watch - 监听,共13个Reactivity - 响应性,共20个Array - 数组,共13个Time - 时间,共2个Utilities - 工具类,共25 可以看到这些方法覆盖了日常开发的大部分场景,相信熟悉它们能大幅提升我们的开发效率。
State 状态 由于笔者使用Nuxt开发,对State需求较小,这部分将留到后面翻译(最近加班实在太多了)。
Elements 元素 HTML元素相关的方法。
useActiveElement - 响应性的document.activeElement
也就是在原生方法的基础上实现了响应性。
useDocumentVisibility - 响应性的document.visibilityState
可以用来监听浏览器窗口可见性的变化。
useDraggable - 使元素可拖拽
useDropZone - 创建一个区域,文件可以拖拽到这个区域中
类似Element UI的Upload组件的效果。
useElementBounding - 一个HTML元素的响应性的边界信息
华为交换机的三种接口类型 Access(接入)接口 介绍 这种接口只能属于一个VLAN,只能接收和发送一个VLAN的数据,通常用于连接终端设备,比如PC或服务器。
帧处理 收帧 本篇所言的收帧,指的是交换的端口从对端设备的收帧,而不是接收同一个交换机的另一个接口的帧,在交换机内部传输的帧都是带vlan标签的
Access接口在从直连设备收到入站数据帧后,会判断这个数据帧是否携带VLAN标签,若不携带,则为数据帧插入本接口的PVID并进行下一步处理;若携带则判断数据帧的VLAN ID是否与本接口的PVID相同,若相同则进行下一步处理,若不同则丢弃。
发帧 Access接口在发送出站数据帧之前,会判断这个要被转发的数据帧中携带的VLAN ID与出站接口的PVID是否相同,若相同则去掉VLAN标签进行转发;若不同则丢弃
Trunk(干道)接口 介绍 这种接口能够接收和发送多个VLAN的数据,通常用于交换机之间的连接。
帧处理 收帧 发帧 本篇所言的发帧,指的是交换的端口向对端设备的发帧,而不是发给同一个交换机的另一个接口的发帧。
Trunk 接口在发送出站数据帧之前,会判断这个要被转发的数据帧中携带的VLAN ID是否与出站接口的PVID相同,若相同则去掉VLAN标签进行转发;若不同则判断本接口是否允许传输这个数据帧的 VLAN ID,若允许则转发,否则丢弃。
也就是说,当数据所属VLAN为该Trunk接口的缺省VLAN时,Trunk接口才会去掉VLAN标签进行转发,其余的数据帧都是携带标签进行转发
Hybrid(混合)接口 介绍 华为设备的接口的默认模式,这种接口能够接收和发送多个VLAN的数据,可以用于连接交换机之间的链路,也可以用于连接交换机与终端设备。
帧处理 收帧 Trunk接口和Hybrid接口在接收入站数据时,处理方法是相同的
发帧 hybrid接口与Trunk接口的不同:
Trunk接口只摘除自己支持的vlan数据帧的vlan tag ;在转发其他VLAN的数据帧时,不会摘除数据帧的vlan tag。
Hybrid接口能够以不携带vlan tag的方式发送多个VLAN的数据。
不同接口配合下的应用实例 access接口+trunk接口应用 同一个vlan中的设备跨交换机进行二层通信,不同vlan不能二层通信 本次实验是在ensp模拟器上完成的,下面是拓扑图
这里将SW1和SW2之间相连的接口E0/0/2配置为Trunk接口,并且允许Trunk链路传输VLAN 5的数据;
将SW1、SW2与PC相连的接口E0/0/1配置为Access接口,并且将这两个接口的PVID都配置为VLAN 5。
SW1的接口配置:
[SW1]vlan 5 ///系统视图配置命令 vlan 5,创建了 VLAN 5 [SW1-vlan5]quit [SW1]interface e0/0/2 ///进入接口G0/0/2的配置视图 [SW1-Ethernet0/0/2]port link-type trunk ///把交换机之间的互联接口配置为Trunk接口 [SW1-Ethernet0/0/2]port trunk allow-pass vlan 5 ///交换机所有接口在初始状态下都可以转发VLAN 1的流量,管理员还需要在 Trunk 接口上放行 VLAN 5 的流量 ///这里使用的命令是接口配置命令 port trunk allow-pass vlan {{vlan-id1[to vlan-id2]} | all}, ///管理员通过这一条命令可以同时放行多个VLAN的流量,也可以使用关键字all来放行所有VLAN的流量 [SW1-Ethernet0/0/2]quit [SW1]interface e0/0/1 ///进入了连接PC的接口 [SW1-Ethernet0/0/1]port link-type access ///将该接口配置为Access接口 [SW1-Ethernet0/0/1]port default vlan 5 ///使用命令port default vlan<vlan-id>将该接口的PVID变更为VLAN 5 ///还有一种方法能够将接口加入 VLAN,那就是在 VLAN 配置视图下,使用命令port interface-type interface-number向VLAN中添加接口 [SW1-Ethernet0/0/1]quit 检查接口配置的情况:
文章目录 僵尸进程与孤儿进程区别如果僵尸进程的父进程,在短时间内退出了,僵尸进程会自行消除吗??init进程没有回收僵尸进城,是什么原因呢?init 进程清除僵尸进城失败后,使用 kill 命令可以清除僵尸进程吗?除了向僵尸进程的父进程发送SIGCHLD信号以外,还有其他处理僵尸进程的方法吗? 僵尸进程与孤儿进程区别 僵尸进程和孤儿进程都是进程状态的一种,但它们的含义和产生原因不同。
僵尸进程 当一个进程结束时,它的状态信息需要被回收,否则它就会成为一个僵尸进程。僵尸进程不会占用CPU资源,但会占用系统的进程表项,如果大量的僵尸进程积累,就会导致系统进程表满,从而影响系统的正常运行。
产生僵尸进程的原因是父进程没有及时回收子进程的状态信息。在Linux系统中,可以使用kill命令向僵尸进程发送SIGCHLD信号,让它的父进程回收它的状态信息,从而消除僵尸进程。
孤儿进程 孤儿进程是指父进程先于子进程结束,而子进程还在运行。这种情况下,子进程的父进程ID会被设置为1,即init进程。init进程会定期检查是否有孤儿进程,如果发现孤儿进程,就会成为它的父进程,并回收它的状态信息。
产生孤儿进程的原因是父进程先于子进程结束。在编写程序时,可以使用wait()或waitpid()函数来回收子进程的状态信息,避免产生孤儿进程。
总的来说,僵尸进程和孤儿进程都是进程状态的一种,但它们的产生原因和解决方法不同。需要注意的是,及时回收子进程的状态信息是避免产生僵尸进程和孤儿进程的关键。
如果僵尸进程的父进程,在短时间内退出了,僵尸进程会自行消除吗?? 如果僵尸进程的父进程在短时间内退出了,那么僵尸进程的父进程ID会被设置为1,即init进程。init进程会定期检查是否有僵尸进程,如果发现僵尸进程,就会回收它的状态信息,从而消除僵尸进程。
因此,如果僵尸进程的父进程在短时间内退出了,那么僵尸进程可能会被init进程回收,从而消失。但是,如果init进程也没有回收僵尸进程,那么僵尸进程就会一直存在,直到系统重启或者手动杀死进程。
需要注意的是,产生僵尸进程并不是一个好的编程习惯,因为它会占用系统资源并可能导致系统崩溃。在编写程序时,应该避免产生僵尸进程,及时回收子进程的状态信息。
init进程没有回收僵尸进城,是什么原因呢? 如果init进程没有回收僵尸进程,可能是因为以下原因:
系统资源不足:如果系统资源不足,如内存不足、进程表满等,就可能导致init进程无法回收僵尸进程。这种情况下,可以尝试释放一些系统资源,或者增加系统资源。
init进程被占用:如果init进程被占用,就可能导致它无法回收僵尸进程。这种情况下,可以尝试杀死占用init进程的进程,或者重启系统。
程序设计问题:如果程序设计存在问题,如没有及时回收子进程的状态信息,就可能导致产生大量的僵尸进程,从而导致init进程无法回收所有的僵尸进程。这种情况下,需要修改程序设计,及时回收子进程的状态信息,避免产生僵尸进程。
需要注意的是,僵尸进程会占用系统资源,并可能导致系统崩溃。在编写程序时,应该避免产生僵尸进程,及时回收子进程的状态信息。如果发现系统中存在大量的僵尸进程,就需要及时排查原因,并采取相应的措施。
init 进程清除僵尸进城失败后,使用 kill 命令可以清除僵尸进程吗? 发送kill信号并不能直接清除僵尸进程,因为僵尸进程已经结束,它的代码和数据已经被系统回收,只剩下一些状态信息需要被回收。发送kill信号只会杀死进程,但不会回收它的状态信息。
要清除僵尸进程,需要向僵尸进程的父进程发送SIGCHLD信号。SIGCHLD信号是一个特殊的信号,用于通知父进程子进程的状态发生了变化。当一个子进程结束时,它会向父进程发送SIGCHLD信号,父进程可以通过wait()或waitpid()函数来回收子进程的状态信息,从而消除僵尸进程。
在Linux系统中,可以使用kill命令向进程发送信号。要向进程的父进程发送SIGCHLD信号,可以使用以下命令:
$ kill -s SIGCHLD <pid> 其中,是进程的PID。这个命令会向进程的父进程发送SIGCHLD信号,从而消除僵尸进程。
需要注意的是,如果父进程已经退出,那么僵尸进程的父进程ID会被设置为1,即init进程。此时,可以向init进程发送SIGCHLD信号,让它回收僵尸进程的状态信息。可以使用以下命令:
$ kill -s SIGCHLD 1 这个命令会向init进程发送SIGCHLD信号,从而消除所有的僵尸进程。
除了向僵尸进程的父进程发送SIGCHLD信号以外,还有其他处理僵尸进程的方法吗? 除了向僵尸进程的父进程发送SIGCHLD信号以外,还有其他处理僵尸进程的方法,包括:
使用wait()或waitpid()函数回收子进程的状态信息。这些函数会阻塞父进程,直到有子进程结束并回收它的状态信息。如果父进程没有及时回收子进程的状态信息,那么子进程就会成为僵尸进程。
在父进程中使用signal()函数注册SIGCHLD信号的处理函数。当子进程结束时,会向父进程发送SIGCHLD信号,父进程可以在信号处理函数中调用wait()或waitpid()函数来回收子进程的状态信息。
在父进程中使用sigaction()函数注册SIGCHLD信号的处理函数。这个函数比signal()函数更加灵活,可以设置更多的选项,如设置信号处理函数的行为、阻塞其他信号等。
需要注意的是,及时回收子进程的状态信息是避免产生僵尸进程的关键。在编写程序时,应该避免产生僵尸进程,及时回收子进程的状态信息。如果发现系统中存在大量的僵尸进程,就需要及时排查原因,并采取相应的措施。
我一直不理解发生在我身边的一个现象。
从我第一次发现到现在已经几年了,在一个又一个人身上,不断地出现。
在编程界,重复 可能是软件中一切邪恶的根源,我为此吃过苦头。让我记忆尤深的是,当重复代码需要修改时,你要记得改变所有重复处。这不是你是否能记住的问题,而是你何时忘记的问题。
当新人向我请教问题,我得以看到他们的代码。当我看到代码中有重复时,总是想让他们避免我曾吃过的苦头。于是我暂停当前的问题,告诉他们这里存在代码重复,为了引起他们的注意,我会告诉他们重复的危害,以及改正方法。
让我不解的事情就此发生。当我过几天查看他们的代码时,在不同的时候,不同的人身上,从来没有一个人修改重复代码,哪怕只是简单到把重复部分提取为一个函数。
重复是一件无伤大雅的小事吗?
我想不是的。
无论是《程序员修炼之道》、还是《代码整洁之道》,它们都强调要遵循 DRY 原则 :系统中的每一项知识都必须具有单一、无歧义、权威的表述。重复代码是违反 DRY 原则最为明显的例子。
为何称其为 DRY ?DRY - Don’t Repeat Yourself(不要重复你自己)。
所以对编程而言,消除重复是一件天大的事情。甚至有人说“自函数发明以来,软件开发领域的所有创新都是在不断尝试从代码中消灭重复”。
这也是我困扰的原因,我已将重复的危害告诉了他们,为何他们还是置之不理?
我的经验对别人来说是一文不值吗?
直到不久前,我知道了原因。
因为他们不需要。
与人类需求层次理论类似,当一个人还为挨饿受冻担心时,跟他谈论个人理想抱负是不现实的。同样,这些新人还在为实现功能而担心时,跟他谈代码质量,他们不需要,也不关心。
从他们向我请教的问题就能看出来:“朱工,我这个 ADC 采集数据是错误的,你能帮我看下吗?”、“朱工,我 Ping 不通板子,你能帮我看下网络部分吗?”
这都是功能实现问题。
在进度的压力下,实现功能,无疑更具有吸引力。
再回到人类需求层次理论,人解决衣食住行后,会自然的追求友情、爱情,追求自尊和尊重。当新人程序员实现功能后,他们会自发的追求代码质量吗?
就我接触到的人来说,完全不会。
当一个项目实现功能后,一些项目会安排我进行代码审查。
对于刚工作3年内的新人,我进行代码审查的标准很简单:
不能有重复不能有魔法数函数体不大于 80 行函数嵌套不大于 3 层圈复杂度不大于 15遵循单一职责原则:一行代码只做一件事、一个变量只做一件事、一个函数只做一件事命名要准确清晰。 在审查的过程中,如果我发现了BUG,他们会很积极的修改,但如果是提升代码质量的重构,比如要求用宏来替换魔法数、用函数封装重复代码、将复杂函数拆分成简单小函数等,他们就会开始找理由、找困难,他们开始抗拒。
在我看来,提升代码质量非常重要,高质量代码更清晰,所以可以减少BUG的产生,高质量代码更好修改,所以更适应未来的需求变化。为什么新人对提升代码质量没有明显兴趣?
因为我所提出的一切关于提升代码质量的观点,新人都没有共鸣。
的确,刚刚接触编程的小伙子对代码质量标准是不会有什么感觉的。
要对这件事有感觉,需要经历几个不愉快的项目。
他们没有吃过代码重复的苦头,没有维护过整个项目只有一个超过10000行代码的 main.c 文件、没有经历过一个简单需求改出一堆连绵不绝 BUG 的崩溃事件。
没有类似的经历,就不会有共鸣。
你说得再正确,也改变不了他人。除非他们自己想要了解,不然你就一点办法也没有。就像我进行的代码审查,因为他们不能理解其中的重要性,所以不愿做,最后变成了我指出一个地方,新人修改这个地方,就这么软抗拒。
虽然我很想告诉他们目前不知道的编程法则,但如果他们自身没有这种需要,我就在白费力气。
换句话说,要是没有求助,不要主动帮忙。
读后有收获,资助博主养娃 - 千金难买知识,但可以买好多奶粉 (〃‘▽’〃)
文章目录 编程框架FLT_REGISTRATION操作回调函数集预操作回调函数回调数据包(FLT_CALLBACK_DATA)参数(FLT_IO_PARAMETER_BLOCK)状态和信息(IO_STATUS_BLOCK) 关联对象 编程框架 FltRegisterFilter 注册Minifilter驱动;使用结束后用FltUnregisterFilter卸载。
注册时,第二参数传入构建的FLT_REGISTRATION结构,主要是操作回调函数集和驱动卸载函数。FltBuildDefaultSecurityDescriptor 生成默认安全描述符。FltCreateCommunicationPort 创建通信服务器端口;使用结束后用FltCloseCommunicationPort关闭。FltStartFiltering开始过滤。当有I/O操作发生时,执行流会进入操作回调函数集。 FLT_REGISTRATION typedef struct _FLT_REGISTRATION { USHORT Size; // 结构体大小 USHORT Version; // 版本 FLT_REGISTRATION_FLAGS Flags; // 标志位 const FLT_CONTEXT_REGISTRATION *ContextRegistration; // 上下文注册 const FLT_OPERATION_REGISTRATION *OperationRegistration; // 操作回调函数集注册!!! PFLT_FILTER_UNLOAD_CALLBACK FilterUnloadCallback; // 驱动卸载回调!!! PFLT_INSTANCE_SETUP_CALLBACK InstanceSetupCallback; // 实例安装回调 PFLT_INSTANCE_QUERY_TEARDOWN_CALLBACK InstanceQueryTeardownCallback; // 控制实例销毁函数 PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownStartCallback; // 实例解绑定函数 PFLT_INSTANCE_TEARDOWN_CALLBACK InstanceTeardownCompleteCallback; // 实际解绑定完成函数 PFLT_GENERATE_FILE_NAME GenerateFileNameCallback; // 生成文件名回调 PFLT_NORMALIZE_NAME_COMPONENT NormalizeNameComponentCallback; // 格式化名字组件回调 PFLT_NORMALIZE_CONTEXT_CLEANUP NormalizeContextCleanupCallback; // 格式化上下文清理回调 PFLT_TRANSACTION_NOTIFICATION_CALLBACK TransactionNotificationCallback; PFLT_NORMALIZE_NAME_COMPONENT_EX NormalizeNameComponentExCallback; PFLT_SECTION_CONFLICT_NOTIFICATION_CALLBACK SectionNotificationCallback; } FLT_REGISTRATION, *PFLT_REGISTRATION; 操作回调函数集 typedef struct _FLT_OPERATION_REGISTRATION { UCHAR MajorFunction; // IRP FLT_OPERATION_REGISTRATION_FLAGS Flags; // 标志位:0(仅对读/写回调有用);FLTFL_OPERATION_REGISTRATION_SKIP_CACHED_IO(不过来缓冲读/写请求);FLTFL_OPERATION_REGISTRATION_SKIP_PAGING_IP(不过滤分页读/写请求) PFLT_PRE_OPERATION_CALLBACK PreOperation; // 预操作回调函数 PFLT_POST_OPERATION_CALLBACK PostOperation; // 后操作回调函数 PVOID Reserved1; } FLT_OPERATION_REGISTRATION, *PFLT_OPERATION_REGISTRATION; // 自定义 static CONST FLT_OPERATION_REGISTRATION OperationRegistration[] = { { IRP_MJ_CREATE, // 文件创建 0, PrevCreate, PostCreate, }, { IRP_MJ_OPERATION_END }, }; 预操作回调函数 FLT_PREOP_CALLBACK_STATUS PfltPreOperationCallback( [in, out] PFLT_CALLBACK_DATA Data, // 回调数据包,内包含这个请求相关的全部信息 [in] PCFLT_RELATED_OBJECTS FltObjects, // 包含与当前 I/O 请求相关的对象的不透明指针 [out] PVOID *CompletionContext // 如果该回调函数返回FLT_PREOP_SUCCESS_WITH_CALLBACK或FLT_PREOP_SYNCHRONIZE,则此参数是传递给相应操作后回调例程的可选上下文指针,否则必须为 NULL 回调数据包(FLT_CALLBACK_DATA) typedef struct _FLT_CALLBACK_DATA { FLT_CALLBACK_DATA_FLAGS Flags; // 标志位 PETHREAD Thread; // 线程指针 PFLT_IO_PARAMETER_BLOCK Iopb; // 参数 IO_STATUS_BLOCK IoStatus; // 状态和信息 struct _FLT_TAG_DATA_BUFFER *TagData; // 重新分析点数据 union { struct { LIST_ENTRY QueueLinks; PVOID QueueContext[2]; }; PVOID FilterContext[4]; }; KPROCESSOR_MODE RequestorMode; // 启动I/O操作的进程的执行模式 } FLT_CALLBACK_DATA, *PFLT_CALLBACK_DATA; 参数(FLT_IO_PARAMETER_BLOCK) typedef struct _FLT_IO_PARAMETER_BLOCK { ULONG IrpFlags; UCHAR MajorFunction; UCHAR MinorFunction; UCHAR OperationFlags; UCHAR Reserved; PFILE_OBJECT TargetFileObject; // 目标文件/目录的文件对象指针 PFLT_INSTANCE TargetInstance; // 目标Minifilter FLT_PARAMETERS Parameters; } FLT_IO_PARAMETER_BLOCK, *PFLT_IO_PARAMETER_BLOCK; typedef union _FLT_PARAMETERS { struct { PIO_SECURITY_CONTEXT SecurityContext; ULONG Options; USHORT POINTER_ALIGNMENT FileAttributes; USHORT ShareAccess; ULONG POINTER_ALIGNMENT EaLength; PVOID EaBuffer; LARGE_INTEGER AllocationSize; } Create; struct { PIO_SECURITY_CONTEXT SecurityContext; ULONG Options; USHORT POINTER_ALIGNMENT Reserved; USHORT ShareAccess; PVOID Parameters; } CreatePipe; struct { PIO_SECURITY_CONTEXT SecurityContext; ULONG Options; USHORT POINTER_ALIGNMENT Reserved; USHORT ShareAccess; PVOID Parameters; } CreateMailslot; struct { ULONG Length; ULONG POINTER_ALIGNMENT Key; LARGE_INTEGER ByteOffset; PVOID ReadBuffer; PMDL MdlAddress; } Read; struct { ULONG Length; ULONG POINTER_ALIGNMENT Key; LARGE_INTEGER ByteOffset; PVOID WriteBuffer; PMDL MdlAddress; } Write; struct { ULONG Length; FILE_INFORMATION_CLASS POINTER_ALIGNMENT FileInformationClass; PVOID InfoBuffer; } QueryFileInformation; struct { ULONG Length; FILE_INFORMATION_CLASS POINTER_ALIGNMENT FileInformationClass; PFILE_OBJECT ParentOfTarget; union { struct { BOOLEAN ReplaceIfExists; BOOLEAN AdvanceOnly; }; ULONG ClusterCount; HANDLE DeleteHandle; }; PVOID InfoBuffer; } SetFileInformation; struct { ULONG Length; PVOID EaList; ULONG EaListLength; ULONG POINTER_ALIGNMENT EaIndex; PVOID EaBuffer; PMDL MdlAddress; } QueryEa; struct { ULONG Length; PVOID EaBuffer; PMDL MdlAddress; } SetEa; struct { ULONG Length; FS_INFORMATION_CLASS POINTER_ALIGNMENT FsInformationClass; PVOID VolumeBuffer; } QueryVolumeInformation; struct { ULONG Length; FS_INFORMATION_CLASS POINTER_ALIGNMENT FsInformationClass; PVOID VolumeBuffer; } SetVolumeInformation; union { struct { ULONG Length; PUNICODE_STRING FileName; FILE_INFORMATION_CLASS FileInformationClass; ULONG POINTER_ALIGNMENT FileIndex; PVOID DirectoryBuffer; PMDL MdlAddress; } QueryDirectory; struct { ULONG Length; ULONG POINTER_ALIGNMENT CompletionFilter; ULONG POINTER_ALIGNMENT Spare1; ULONG POINTER_ALIGNMENT Spare2; PVOID DirectoryBuffer; PMDL MdlAddress; } NotifyDirectory; struct { ULONG Length; ULONG POINTER_ALIGNMENT CompletionFilter; DIRECTORY_NOTIFY_INFORMATION_CLASS POINTER_ALIGNMENT DirectoryNotifyInformationClass; ULONG POINTER_ALIGNMENT Spare2; PVOID DirectoryBuffer; PMDL MdlAddress; } NotifyDirectoryEx; } DirectoryControl; union { struct { PVPB Vpb; PDEVICE_OBJECT DeviceObject; } VerifyVolume; struct { ULONG OutputBufferLength; ULONG POINTER_ALIGNMENT InputBufferLength; ULONG POINTER_ALIGNMENT FsControlCode; } Common; struct { ULONG OutputBufferLength; ULONG POINTER_ALIGNMENT InputBufferLength; ULONG POINTER_ALIGNMENT FsControlCode; PVOID InputBuffer; PVOID OutputBuffer; PMDL OutputMdlAddress; } Neither; struct { ULONG OutputBufferLength; ULONG POINTER_ALIGNMENT InputBufferLength; ULONG POINTER_ALIGNMENT FsControlCode; PVOID SystemBuffer; } Buffered; struct { ULONG OutputBufferLength; ULONG POINTER_ALIGNMENT InputBufferLength; ULONG POINTER_ALIGNMENT FsControlCode; PVOID InputSystemBuffer; PVOID OutputBuffer; PMDL OutputMdlAddress; } Direct; } FileSystemControl; union { struct { ULONG OutputBufferLength; ULONG POINTER_ALIGNMENT InputBufferLength; ULONG POINTER_ALIGNMENT IoControlCode; } Common; struct { ULONG OutputBufferLength; ULONG POINTER_ALIGNMENT InputBufferLength; ULONG POINTER_ALIGNMENT IoControlCode; PVOID InputBuffer; PVOID OutputBuffer; PMDL OutputMdlAddress; } Neither; struct { ULONG OutputBufferLength; ULONG POINTER_ALIGNMENT InputBufferLength; ULONG POINTER_ALIGNMENT IoControlCode; PVOID SystemBuffer; } Buffered; struct { ULONG OutputBufferLength; ULONG POINTER_ALIGNMENT InputBufferLength; ULONG POINTER_ALIGNMENT IoControlCode; PVOID InputSystemBuffer; PVOID OutputBuffer; PMDL OutputMdlAddress; } Direct; struct { ULONG OutputBufferLength; ULONG POINTER_ALIGNMENT InputBufferLength; ULONG POINTER_ALIGNMENT IoControlCode; PVOID InputBuffer; PVOID OutputBuffer; } FastIo; } DeviceIoControl; struct { PLARGE_INTEGER Length; ULONG POINTER_ALIGNMENT Key; LARGE_INTEGER ByteOffset; PEPROCESS ProcessId; BOOLEAN FailImmediately; BOOLEAN ExclusiveLock; } LockControl; struct { SECURITY_INFORMATION SecurityInformation; ULONG POINTER_ALIGNMENT Length; PVOID SecurityBuffer; PMDL MdlAddress; } QuerySecurity; struct { SECURITY_INFORMATION SecurityInformation; PSECURITY_DESCRIPTOR SecurityDescriptor; } SetSecurity; struct { ULONG_PTR ProviderId; PVOID DataPath; ULONG BufferSize; PVOID Buffer; } WMI; struct { ULONG Length; PSID StartSid; PFILE_GET_QUOTA_INFORMATION SidList; ULONG SidListLength; PVOID QuotaBuffer; PMDL MdlAddress; } QueryQuota; struct { ULONG Length; PVOID QuotaBuffer; PMDL MdlAddress; } SetQuota; union { struct { PCM_RESOURCE_LIST AllocatedResources; PCM_RESOURCE_LIST AllocatedResourcesTranslated; } StartDevice; struct { DEVICE_RELATION_TYPE Type; } QueryDeviceRelations; struct { const GUID *InterfaceType; USHORT Size; USHORT Version; PINTERFACE Interface; PVOID InterfaceSpecificData; } QueryInterface; struct { PDEVICE_CAPABILITIES Capabilities; } DeviceCapabilities; struct { PIO_RESOURCE_REQUIREMENTS_LIST IoResourceRequirementList; } FilterResourceRequirements; struct { ULONG WhichSpace; PVOID Buffer; ULONG Offset; ULONG POINTER_ALIGNMENT Length; } ReadWriteConfig; struct { BOOLEAN Lock; } SetLock; struct { BUS_QUERY_ID_TYPE IdType; } QueryId; struct { DEVICE_TEXT_TYPE DeviceTextType; LCID POINTER_ALIGNMENT LocaleId; } QueryDeviceText; struct { BOOLEAN InPath; BOOLEAN Reserved[3]; DEVICE_USAGE_NOTIFICATION_TYPE POINTER_ALIGNMENT Type; } UsageNotification; } Pnp; struct { FS_FILTER_SECTION_SYNC_TYPE SyncType; ULONG PageProtection; PFS_FILTER_SECTION_SYNC_OUTPUT OutputInformation; ULONG Flags; ULONG AllocationAttributes; } AcquireForSectionSynchronization; struct { PLARGE_INTEGER EndingOffset; PERESOURCE *ResourceToRelease; } AcquireForModifiedPageWriter; struct { PERESOURCE ResourceToRelease; } ReleaseForModifiedPageWriter; struct { PIRP Irp; PVOID FileInformation; PULONG Length; FILE_INFORMATION_CLASS FileInformationClass; } QueryOpen; struct { LARGE_INTEGER FileOffset; ULONG Length; ULONG POINTER_ALIGNMENT LockKey; BOOLEAN POINTER_ALIGNMENT CheckForReadOperation; } FastIoCheckIfPossible; struct { PIRP Irp; PFILE_NETWORK_OPEN_INFORMATION NetworkInformation; } NetworkQueryOpen; struct { LARGE_INTEGER FileOffset; ULONG POINTER_ALIGNMENT Length; ULONG POINTER_ALIGNMENT Key; PMDL *MdlChain; } MdlRead; struct { PMDL MdlChain; } MdlReadComplete; struct { LARGE_INTEGER FileOffset; ULONG POINTER_ALIGNMENT Length; ULONG POINTER_ALIGNMENT Key; PMDL *MdlChain; } PrepareMdlWrite; struct { LARGE_INTEGER FileOffset; PMDL MdlChain; } MdlWriteComplete; struct { ULONG DeviceType; } MountVolume; struct { PVOID Argument1; PVOID Argument2; PVOID Argument3; PVOID Argument4; PVOID Argument5; LARGE_INTEGER Argument6; } Others; } FLT_PARAMETERS, *PFLT_PARAMETERS; 状态和信息(IO_STATUS_BLOCK) typedef struct _IO_STATUS_BLOCK { union { NTSTATUS Status; // 完成状态 PVOID Pointer; }; ULONG_PTR Information; // 依赖于请求的值,如果传输请求完成,则为传输的字节数;如果使用另一个STATUS_XXX完成传输请求,则为0 } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; 关联对象 typedef struct _FLT_RELATED_OBJECTS { USHORT Size; USHORT TransactionContext; PFLT_FILTER Filter; PFLT_VOLUME Volume; PFLT_INSTANCE Instance; PFILE_OBJECT FileObject; // 操作的文件对象 PKTRANSACTION Transaction; } FLT_RELATED_OBJECTS, *PFLT_RELATED_OBJECTS;
使用c++/winrt API获取RGB相机视频流
1、前提条件
该示例使用c++/winrt进行开发,需要编译器支持c++17,本人使用Visual Studio2017,系统版本为Windows10 21H2,由于UWP API是从Windows10系统进行支持的,故而Windows7及Windows8等系统可能不能正常使用。
注:遇到未定义行为请先检查该API是从哪个Windows版本支持的,可能你当前的系统版本还不支持该API。
2、使用MediaCapture获取RGB相机视频的流程
使用FindAllAsync接口获取所有的VideoCapture设备,选择你想要的设备;根据选好的设备ID及自定义配置初始化MediaCapture对象;使用刚刚初始化的MediaCapture获取所有的帧源,我们这里选择RGB视频流这个帧源;为选择好的MeidaFrameSource设置指定的format(width,height);获取读取视频流帧对象MediaFrameReader;使用MediaFrameReader读取相机的视频帧; 3、代码演示
注:演示代码中还需要依赖opencv进行nv12->bgr的转换工作,以及使用opencv进行实时显示取到的视频帧工作。
头文件
//MediaFrameCapture.h #pragma once #ifdef _WIN32 // winrt #include <mfapi.h> #include <mfidl.h> #include <winrt/base.h> #include <winrt/Windows.Media.Core.h> #include <winrt/Windows.Media.Devices.h> #include <winrt/Windows.Media.Capture.h> #include <winrt/Windows.Media.Capture.Frames.h> #include <winrt/Windows.Media.Mediaproperties.h> #include <winrt/Windows.Foundation.h> #include <winrt/Windows.Foundation.Collections.h> #include <winrt/Windows.Storage.Streams.h> #include <winrt/Windows.Devices.Enumeration.h> #include <winrt/Windows.Graphics.h> #include <winrt/Windows.Graphics.Imaging.h> using namespace winrt; #include "opencv2/opencv.hpp" // std #include <vector> class MediaFrameCapture { public: MediaFrameCapture(); ~MediaFrameCapture(); /** * @brief 获取已连接的设备数 * * @return 返回已连接的mipi RGB的设备数量 * @note */ int listDevices(); /** * @brief 更新已连接mipi RGB相机列表 * * @return void * @note */ void updateListDevices(); /** * @brief 连接指定deviceId的设备 * * 通过deviceId获取指定设备并初始MediaCapture对象 * * @param[in] deviceId RGB设备的PID,通过该deviceId查询设备是否存在 * @return 返回设置是否被正确的打开和初始化 * @note */ bool setupDevice(const std::string& deviceId); /** * @brief 连接指定deviceId的设备并指定帧的分辨率 * * 通过deviceId获取指定设备并初始MediaCapture对象;指定帧的分辨率,若指定的大小格式不支持则失败 * * @param[in] deviceId RGB设备的PID,通过该deviceId查询设备是否存在 * @param[in] width 分辨率宽 * @param[in] height 分辨率高 * @return 返回设置是否被正确的打开和初始化 * @note */ bool setupDevice(const std::string& deviceId, int width, int height); bool startCapture(); bool stopCapture(); /** * @brief 提供给外部调用返回读取到的新帧 * * * @param[out] oneFrame 返回读取到的新帧 * @return false没有获取到新帧,true获取到新帧 * @note */ bool read(cv::Mat&); /** * @brief 用于通过设备PID号选择指定的设备,初始化m_selectedDevice字段 * * * @param[in] deviceId 设备的PID号 * @return false指定的设备不存在,获取失败;true指定的设备存在 * @note */ bool selectDeviceByDeviceId(const std::string& deviceId); /** * @brief 判断已选设备是否被占用 * * @param[in] selectedDevice 传入选中的设备信息 * @return false设备未被占用,可以使用;true设备已被占用或互斥打开 * @note */ bool isDeviceInUse(Windows::Devices::Enumeration::DeviceInformation selectedDevice); /** * @brief 获取设备列表 * * @return 返回设备列表结果集 * @note */ Windows::Devices::Enumeration::DeviceInformationCollection getDeviceList(); private: /** * @brief 通过选好的设备初始化MediaCapture对象,该对象用于媒体设备流采集 * * * @param[in] selectedDevice 选择的设备信息 * @return false初始化失败,true初始化成功 * @note */ bool initMediaCapture(const Windows::Devices::Enumeration::DeviceInformation& selectedDevice); /** * @brief 选择Color帧源,由于一个设备中可能有color、depth、ir等源,选择color帧 * * @return false不存在color源,true存在并初始化成功 * @note */ bool chooseMediaFrameSource(); /** * @brief 获取最合适的分辨率 * * 若width和height为0时,选择最高分辨率,不为0时,选择与width、height对应的分辨率 * * @param[in] width 需要适配的分辨率宽 * @param[in] height 需要适配的分辨率高 * @return 为nullptr表示没有找到适配的format * @note */ Windows::Media::Capture::Frames::MediaFrameFormat getSupportFormat(int width = 0, int height = 0); private: int m_width; // 分辨率-宽 int m_height; // 分辨率-高 int m_deviceNums; // 设备数 std::string m_deviceId; // 设备的PID param::hstring m_subType; std::map<uint32_t, std::pair<int, int>> m_supportFormatMap;// 支持的NV12格式 static std::map <std::string, bool> m_deviceOpenedMap; Windows::Devices::Enumeration::DeviceInformation m_selectedDevice; // 被选择的设备 Windows::Devices::Enumeration::DeviceInformationCollection m_deviceList; // 设备列表 Windows::Media::Capture::MediaCapture m_mediaCapture; // 媒体流采集 Windows::Media::Capture::Frames::MediaFrameSource m_mediaFrameSource; // 媒体流源,从mediacapture获取 Windows::Media::Capture::Frames::MediaFrameFormat m_defaultFormat; // 媒体格式,用于自定义视频流的分辨率 Windows::Media::Capture::Frames::MediaFrameReader m_mediaFrameReader; // 读取视频流 }; #endif 实现
Godot 4.0 stable
## 根据图片创建不规则碰撞形状 ##[br] ##[br][code]image[/code] 图片 ##[br][code]canvas_node[/code] 画布节点。[Sprite2D, AnimatedSprite2D] 中的类型,用于根据节点的属性进行偏移点的坐标 ##[br][code]group_idx[/code] 创建出的点位组的列表索引。因为可能一张图片有多个图块,会生成超过一组的点列表 static func create_collision_polygon_from_image(image: Image, canvas_node: Node2D = null, group_idx: int = 0) -> CollisionPolygon2D: # 获取图片点 var bit_map = BitMap.new() bit_map.create_from_image_alpha(image) var points_list = bit_map.opaque_to_polygons(Rect2(Vector2(), image.get_size())) # 偏移位置 var points = points_list[group_idx] if canvas_node: assert(canvas_node is Sprite2D or canvas_node is AnimatedSprite2D, "画布节点需要是 [Sprite2D, AnimatedSprite2D] 类型的节点") var offset_pos : Vector2 = Vector2(0, 0) if canvas_node is Sprite2D or canvas_node is AnimatedSprite2D: if canvas_node.
1、如图所示,选中右编号敲击公式后,公式与右编号上下不居中
2、在开始--样式 中找到如下图所示的样式,右击--修改
3、 进入段落,修改如下图示
然后点击确定,在应用修改后的样式 就好了
1、公式居中,编号具有的办法应该采用mathtype有编号功能来编写公式。
2、然后弹出的页面中填入需要的公式就会出现如下场景;
3、然后选中有编号
4、修改为自己需要的编号 5、有时候会出现右编号不能完全居右的问题,
6、这时 需要 点击--布局--分栏--更多栏
7、然后记住文章在一栏情况下的宽度,如图示我的宽度是38.5字符,在记住它的一半也就是19.25.然后在开始--样式--找到如图框选的样式
8、然后右键--修改
9、点击 格式--制表符
10、点击全部清除,就是清除掉以前不正确的制表位,然后开始设置
11、先设置19.25,居中,前导符选择1,设置;在设置38.5,居右,前导符选择1,设置,确定,确定,然后就可以实现右对齐公式
温馨提示:本篇文章的数值设置是根据我自己的论文设置的,具体的修改数值还请参照自己文章的实际情况。
Anaconda 安装和换源,CUDA+Pytorch 一、Anaconda 安装1.1、下载方法1.2、一些使用帮助1.3、安装方法 二、conda 的基本使用命令2.1、conda 的初始化2.2、conda 创建虚拟环境、2.3、conda 列出所有虚拟环境2.4、conda 激活虚拟环境2.5、退出虚拟环境2.6、conda 删除虚拟环境 三、conda 换源3.1、查看anaconda的已经存在源3.2、添加清华大学镜像源3.3、设置搜索时显示的通道地址3.4 、删除已存在的镜像源3.5、临时换源 四、安装CUDA+CUDNN4.1、查看电脑4.2、根据显卡的算力和架构确定 CUDA Runtime 版本4.3、Pytorch安装 记录一些学习。 一、Anaconda 安装 1.1、下载方法 anaconda官网
清华大学开源软件镜像站
Miniconda 是一个 Anaconda 的轻量级替代,默认只包含了 python 和 conda,但是可以通过 pip 和 conda 来安装所需要的包。
Miniconda 安装包可以到 https://mirrors.bfsu.edu.cn/anaconda/miniconda/ 下载
1.2、一些使用帮助 Anaconda 仓库与第三方源(conda-forge、msys2、pytorch等,查看完整列表,更多第三方源可以前往校园网联合镜像站查看)的镜像,各系统都可以通过修改用户目录下的 .condarc 文件来使用 TUNA 镜像源。Windows 用户无法直接创建名为 .condarc 的文件,可先执行 conda config --set show_channel_urls yes 生成该文件之后再修改
1.3、安装方法 二、conda 的基本使用命令 2.1、conda 的初始化 conda init 2.2、conda 创建虚拟环境、 conda create -n env_name python=3.x 2.
效果如图
代码
from PyQt5.QtCore import QVariant, Qt # 禁用 combobox.setItemData(1, QVariant(0), Qt.UserRole-1) # 启用 combobox.setItemData(1, QVariant(1 | 32), Qt.UserRole-1)
本文内容:代理ip使用原理,如何在自己的爬虫里设置代理ip,如何知道代理ip有没有生效,没生效的话是哪里出了问题,个人使用的代理ip(付费)。
目录
代理ip原理
输入网址后发生了什么呢?
代理ip做了什么呢?
为什么要用代理呢?
爬虫代码中使用代理ip
代理ip的获取
检验代理ip是否生效
未生效问题排查
1.请求协议不匹配
2.代理失效
代理ip原理 输入网址后发生了什么呢?
浏览器获取域名浏览器渲染结果四次挥手释放TCP连接服务器将查询结果返回给浏览器浏览器通过HTTP协议向服务器发送数据请求通过DNS协议获取域名对应服务器的ip地址浏览器和对应的服务器通过三次握手建立TCP连接 其中涉及到了:
应用层:HTTP和DNS
传输层:TCP UDP
网络层:IP ICMP ARP
代理ip做了什么呢?
简单一点来说,使用代理ip就是:
原本你的访问目标网站
使用代理ip后你的访问目标网站
为什么要用代理ip呢?
我们在爬取数据的时候,如果使用自己的真实ip去访问目标网站,会有很大的风险被网站记录。而怎么才能避免我们的真实ip被网站记录呢,那就需要使用代理ip来给我们套上一层伪装,来让目标网站检测不到我们的真实ip地址。除了这种情况,有的网站限制了一些地区的ip地址,如果不使用代理的话,我们就无法正常访问目标网站了,所以我们很多时候需要使用代理ip:
爬虫代码中使用代理ip
就像是请求时伪装头一样,伪装ip,注意是 { }
代理ip的获取
像我们刚刚的proxies存储的代理,是可以直接作为参数传进requests里面使用的。那现在我们就来做这个proxies。
首先打开一个代理IP提供商,我这里选择的是站大爷,我们一般使用api获取,也就是接口直接获取我们需要的ip,由供应商返回提供给我们的ip信息:
可以根据自己需要的情况设置:
这里从URL点击进去使用生成的API接口链接来做演示,会生成一个url链接,我们requests直接去请求这个链接,就可以获得这个代理IP的详细信息。
# 拿到供应商给我们的代理IP URL = "https://www.zdaye.net/?utm-source=csdnhao&utm-keyword=%3Fcsdnhao" # 这里参数控制了数量 格式 和ip协议等等 这也算是它的一个优势吧,多的话可以提取几百,而且可以指定城市从固定地点提取ip,更符合爬虫模拟人类的行为特征。 url = "http://api.proxy.zdaye.io/getProxyIp?num=1&return_type=txt&lb=1&sb=0&flow=1®ions=&protocol=http" # 输出ip res = requests.get(url) print(res.text) # 这个ip就可以放在我们实际要请求的网页requests中了 检验代理ip是否生效
我们访问一个网站,这个网站会返回我们的ip地址:
print(requests.get('http://httpbin.org/ip', proxies=proxies, timeout=3).text) 重点来了,我使用代理IP进行访问,如果返回来不是我们自己的IP,说明代理ip可用,可以伪装,也可以帮我们带回想要的信息。
我们看一下刚才我使用了四个不同的代理ip,结果是全部生效了,
未生效问题排查
如果你返回的还是本机地址,99%试一下两种情况之一:
1.请求协议不匹配
简单一点来说那就是,你请求的是http格式,那就要使用http的协议,是https格式,就要使用https的协议。
2023.4.11
努力学习python,想为以后可以有一份额外收入,如果有大神可以指点一下,我将不胜感激
如果有大神想以后拥有一个合作伙伴进行交流,我会尽力赶上您的脚步!
for...in循环 循环结构的作用就是让指定的代码重复的运行,例如我们想重复打印输出hello,20遍。不使用循环则需要20行代码输出,使用循环最少使用两行即可输出,这是学习循环的必要点
循环和判断是各语言中重要的一部分,认真对待!
for...in循环举例,打印20次hello
for i in range(20): print('hello') 在这两行代码中,将打印输出20次hello ,那么for,in,range(20)的含义是什么?
for 代表 变量 in 代表 容器 for i (代表变量) in range(20) (代表容器)
这里的range(20) 代表循环次数,或范围。20的范围是 0 - 19(包括0和19),范围公式为: (n) 0-n-1就是0到n减1,这是容器,代表我承载的范围,这里的i可以输出数量的变量,i 是可以任意定义
小练习: 吃馒头
for i in range(5): print('张三饿坏了,开始吃{}个馒头'.format(i+1)) range函数
重点重复一下range函数,他一共有三个类型
第一个类型 range内只有一个数
首先在range括号内只有一个数字的,举例为range(3) 他控制的位置是结束位置 ,输出范围为0、1、2。范围: 0 -- [3-1] 公式:0 -- [n-1]
第二个类型 range内有两个数
举例为 range(1,4),控制的是起始位置和结束位置,并不是如果调到range(5,6)就会运行5次,而是变量的起始位置是5,并且该range(5,6)只会循环一次,因为范围为5-5
第三个类型 range内有三个数
举例为 range(1,10,2) 这里的前两位数字也是可控制的和起始位置和结束位置,最后一位称为布长,布长如何理解? 这里的布长为2,则可以代表 每两个数字为一布长,取布长的第一位数字
1,2为一布长,取1为变量,3,4为一布长,取3为变量,以此类推
for...in如何跳过某个循环输出?
#题目:不输出3,其他都输出,如何解决? for i in (1,6): if i == 3: #添加判断语句,等于3则pass掉 pass else: #不符合3的则直接输出 print(i) if .
2023/4/10
努力学习python,想为以后可以有一份额外收入,如果有大神可以指点一下,我将不胜感激
如果有大神想以后拥有一个合作伙伴进行交流,我会尽力赶上您的脚步!
为什么要有判断语句?
判断语句在生活场景中是随处可见的!例如如果我没有在CSDN登录账号,则不能发表文章,此时需要判定我是否登录账号,其次也需要判断我得账号密码是否正确,是否符合格式要求!
在Python中默认的小知识点
如果在Python中if后面的判断变量为' '(空字符串),0,None等,则默认判断为False,如果变量为非空字符,整型等,则判断为True。if后面必须是True才会执行后面语句,否则跳过,例如
if '': print('这里不输出') print('输出我') if 'baidu' print('这里输出') Python语句中的if判断
一般学习期间常用于比较,为True则执行,为False则不执行,举例
a = 10 b = 5 if a > b: print('如果a>b将输出我') if a < b: print('如果a<b将输出我') #将数值a更改为4则输出该行 #进阶使用 a = int(input('请输入a的值')) b = int(input('请输入b的值')) if a > b: print('您输入的值为{}>{}'.format(a,b)) if a < b: print('您输入的值为{}<{}'.format(a.b)) Python中的双项分支判断 基本格式为: if 判断语句: 执行语句(判断为True) else: 执行语句(判断为False) 在这里需要注意,else需要与上面的if语句顶格写 。总体可以理解为,如果if冒号后面的条件不成立,那么他就执行else后面的执行语句,双项练习
age = int(input('请输入您的年龄')) if age >= 18: print('允许进入网吧') else: print('拒绝进入网吧') #如果输入条件不满足18,则运行该条件 随机数 *