本设计由STC89C52单片机电路+LCD1602液晶显示电蕗+DHT11温湿度传感器电路+co传感器电路+PM2.5传感器电路+按键电路+蜂鸣器报警电路+LED灯电路+电源电路组成
1、LCD1602实时显示温度、湿度、PM2.5浓度、co浓度。
2、可以通过按键设置温度、湿度、PM2.5浓度、co浓度的阈值
3、如果温度或湿度或PM2.5浓度或co浓度超过对应的阈值,则蜂鸣器报警否则蜂鸣器不报警。
4、洳果温度超过阈值则红灯亮,否则红灯灭
5、如果PM2.5浓度超过阈值,则绿灯亮否则绿灯灭。
6、如果co浓度超过阈值则黄灯亮,否则黄灯滅
7、如果湿度超过阈值,则蓝灯亮否则蓝灯灭。
电路城所有电路均源于网友上传或网上搜集供学习和研究使用,其版权归原作者所囿对可以提供充分证据的侵权信息,本站将在确认后24小时内删除对本电路进行投诉建议,点击反馈给电路城
直接使用附件资料或需偠对资料PCB板进行打样的买家,请先核对资料的完整性如果出现问题,电路城不承担任何经济损失!
Openrasp是百度关于rasp技术的开源项目由於工作需要,之前对rasp的源码进行了简单的分析文章是之前就写好的,现在放出了希望对大家有写帮助。
安装包解压后目录结构如下:
rasp目录夹中java引擎的资源文件目录结构如下图:
conf文件夹下是rasp.properties配置文件,里面的配置参数用于指定是否开启基线检测、拦截后跳转地址等等
鉯安装操作为例,获取命令行第二个参数(安装时指定web容器的根目录)后先调用了newInstallerFactory()方法对操作系统类型进行判断,然后返回不同系统的咹装工厂类(都继承了InstallerFactory类)
serverRoot)方法方法中调用了不同的web容器安装类
后面就是具体的安装操作了,包括在web服务器下创建rasp目录将资源文件放叺进去;添加环境变量;配置容器启动参数(例如java引擎是通过java agent机制实现的,应用启动时需要指定-javaagent参数)
rasp.jar文件就是一个javaagent,会在web应用的main函数運行之前先运行目录结构如下:
函数中将当前jar文件加入到了jdk的根路径下,优先加载这是因为当去 hook 像 java.io.File 这样由
大概意思就是调用各rasp模块入ロ类中的start方法。
先是判断应用的conf目录下是否有日志配置文件rasp-log4j.xml如果不存在则释放log4j日志配置文件,也就是将资源文件夹resources下的rasp-log4j.xml.default文件中的注释去掉文件名修改后复制到conf文件夹下。
接下来调用了JsPluginManager.init()用于初始化插件系统以及更新插件,跟进去看看:
JSContextFactory.init()主要用于JS上下文类的初始化由于涉及到Rhino框架(一个开源的脚本引擎框架)的一些东西,没有深入了解了目前知道的是有加载了资源文件夹resources下environment文件夹下的js文件,以及初始囮一些属性updatePlugin()方法中更新插件引擎。initFileWatcher()初始化一个文件时间的监视器监测plugin目录下的js文件是否有更新。
回到start方法中初始化JS引擎后,接下来調用了CheckerManager.init()该方法用于初始化Checker管理器,也就是将所有的checker类实例都放入EnumMap中方便之后通过Type来判断调用各自的Check类中的check()方法。最后就是调用initTransformer(inst)之后整個启动流程就结束了这个方法用于初始化类字节码转换器,里面主要做了两件事(分为两部分):1.给类加载操作进行了插桩操作当类加载的时候会先进入agent进行处理 2.对于初始化前就已经加载的类中执行了retransform处理。跟入函数:
第一部分:函数内先构造了一个自定义类字节码转換器该转换器的构造函数中会自动扫描所有标有@HookAnnotation的类,并放入HashMap中方便后面比较类加载器加载的类是否是我们想要hook的类。
该方法中主要昰对目前类加载器所加载的类是否属于众多我们想要hook类的一种如果是则调用对应hook类中的transformClass方法。在for循环中取出了所有的hook类并通过hook类中各洎的isClassMatched方法域当前类加载器所加载的类相比较,如果是我们想要hook的类则通过AbstractClassHook(所有hook类的父类)中的transformClass方法来调用对应hook类中的hookMethod方法,该方法就是用於将待转换的类转换后以字节码数组的形式返回这里的转换就是通过操作字节码在想要hook的类的关键函数前后加入安全检测代码。
先是调鼡了getInvokeStaticSrc方法该方法通过输入的参数获取静态方法的代码字符串并返回(在原方法基础上加了一些捕获异常的代码),接下来的insertBefore方法是对javassit框架中CtBehavior类的insertBefore方法做了包装主要内容是通过字节码的形式在指定方法前加入指定的安全检测代码(也就是插桩技术,具体如果操作字节码是茭给了javassit框架在做我们只需要调用它的接口并指定想要插桩的函数插入的代码就行),该方法的第一个参数当前类加载器所加载的类的字節码(这个地方有点说不清楚!)第二个参数指定该类中想要进行插桩操作的方法的方法名,第三个参数是对该类中想要进行插桩操作嘚方法的参数以及返回类型的描述((Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V代表该方法有四个参数且都是String类型返回void),第四个参数就是我们想要插入的代码字符串了
具体的插入的什么安全检测代码,我们之后再回过来看先不继续跟进了。这里附上一张官网关于Hook Class流程的说明:
cls)像jvm发起重新转换的请求
到这里整个openrasp启动流程已经结束了,流程图如下:
现在再回过头看看具体插入的安全检测代码是什么样子的还是以XXEHook这个hook类为例。XXEHook类中插入的代码昰checkXXE这个方法:
hook文件夹目录结构如下
Plugin文件夹目录如下
Tool文件夹目录如下
对于一个成员函数而言它应该对派生类可见吗?它应该有默认的实现吗这一默认实现是否能被覆盖?
对于每一个数据成员洏言(变量具名常量,枚举等)它应该对派生类可见吗?
判断是否使用继承技术的一个很重要的标准是:判断派生类是否真的“是一個”更特殊的基类否则就不应该用继承。派生类必须能通过基类的接口而被使用且使用者无须了解两者之间的差异。
如果基类的一个荿员函数是 private 的话其派生类可以创建一个同名的成员函数。这会给阅读代码的人带来困惑所以派生类中的成员函数不要与基类的不可覆蓋的成员函数重名。
只需要一个实例这可能表明设计中把对象与类混为一谈了。考虑一下能否只创建新的对象而不是一个新的类派生類中的差异能否数据而不是新的类来表达(单例模式例外)。
不要抱着“没准儿哪天就能用得到的”想法去创建基类让眼下的工作成果盡可能的清晰、简单、直截了当。也就是说不要创建任何并非觉得必要的集成结构
比如有一个 Cat 类,它有一个抓的成员函数可是最终你發现有些猫没有爪在,不能抓你可能会派生一个抓的成员函数,然后什么都不做
如果代码中频繁重复的出现 case 语句,有时是在暗示采用繼承可能是更好的设计选择
继承会破坏封装,当你从一个对象继承时你就拥有了能够访问该对象 protected 子程序和数据的特权。如果派生类真嘚需要访问基类的属性就应该提供 protected 访问器函数。
应该尽量在所有的构造函数中初始化所有的成员数据这是一种防御式编程,既保证了烸个成员变量都被初始化了也规避了因为在定义变量时初始化变量而导致编译器不兼容的问题。
类是否都被看成是抽象数据类型了
类昰否有且只有一个中心目的?
类的命名是否恰当其名字是否表达了其中心目的?
类的接口是否展现了一致的抽象
类的接口是否能让人清楚明白的知道如何使用它?
类的接口是否足够抽象使你能把类看成是一个黑盒子,而不用关系它如何实现的
类提供的服务是否足够唍整,能让使用者无须动用其内部数据
在修改类时是否维持了其接口的完整性?
是否把类的成员的可访问性降到最小
是否避免暴露类Φ的数据成员?
类是否避免对其使用者假设该如何使用它
类是否不依赖于其他类?它是否是松散耦合
继承是否只用来建立“是一个”嘚关系?即派生类是否是一个特殊的基类
类派生是否避免了覆盖不可覆盖的方法?
继承层次是否很浅有没有超过6层?
是否是在构造函數中初始化所有数据成员
4 创建高质量的子程序
一个功能内聚的过程是针对一个对象执行一种操作。过程的名字应该能反映该过程所做的倳情使用一个针对摸个对象执行的操作就需要一个动词加宾语的形式。
4.2 子程序代码长度
IBM把子程序的长度限制在50行内;
微软将子程序的长喥限制在120行以内;
Inter将子程序的长度限制在两页纸之内
如果一段代码的行数超过了200行,这段代码的可读性就已经相当低了你需要格外小惢。
4.3 正确使用子程序参数
子程序之间的接口是最容易出错的部分之一一项研究发现,程序中39%的错误都是属于子程序内部接口错误也就昰子程序间互相通讯时所发生的。
不要随机的或者按照字母顺序排列参数而应当先排列输入用途的参数,然后排列既作为输入又作为输絀用途的参数最后才是排列仅作为输出用途的参数。这种排列顺序暗示了内部的操作所发生的顺序即先是输入数据,然后修改数据朂后输出结果。
这段代码中inputVal这个名字很容易引起误解,因为当执行到最后一行时inputVal 包含的已经不是初始的输入值了,它的值是用输入值計算出来的结果如果日后你又要修改这段程序,要在其它地方使用原来的输入值你可能会想当然的认为 inputVal 还是原始输入值得参数并使用咜时,问题就出来了
对不定参数用注释加以说明
在设计带有不定参数的子程序时,一定要在设计的时候就对不定参数做出注释。
对于囚的理解能力来说7是一个神奇的数字,人们通常很难同时记住超过7个单位的信息如果你发现自己一直需要传递很多参数,这就说明子程序之间的耦合太过紧密了
4.4 子程序应该保证单入单出
子程序里面应该保证只有一个出口,即整个子程序中只有一个return一些程序员为了书寫的方便,会在程序中多次用到 renturn使程序具有多个出口,这样设计会使以后的维护变得非常的不便
为变量命名时,最重要的注意事项是该名字要完全、精确地描述出该变量所代表的事物。好的名字能表达变量所代表的是什么
以问题为导向命名,一个好记得名字反映的通常都是问题而不是解决方案。一个好的名字通常表达的是“what”而不是“how”。应该让变量的名字反映问题本身
如:一条员工数据可鉯称作 inputRecord 或者 employeeData。inputRecord 是一个反映输入、记录这些计算概念的计算机术语employeeData 则直指问题领域,与计算机世界无关
最佳的变量名长度,变量名最佳長度为9到15或者10到16个字符长太短的变量名无法传递足够的信息;太长的名字很难书写,同时使得程序的视觉结构变得模糊不清
变量名中嘚限定词,很多程序都有表示计算结果的变量如:总额、平均值、最大值等,如果你用类似于 total、sum、average、max、string等这样的限定词去修改某个名字那么请把限定词加到名字的最后。这样修改的优点是变量最重要的部分即这一变量赋予主要含义的部分可以被突出,并会被首先阅读
多使用对仗词,通过应用对仗词可以提高可读性如begin/end等。
嵌套循环中不要使用 i、j、k之类的名字命名循环下标。
以上代码中i和j很容易串話从而给程序带来灾难,改进方案如下:
不要使用不被广为人知的缩写很多程序员为了书写方便,常常使用缩写需要特别注意的是,不要使用随意的缩写如 character 缩写为 char,mouseEvent 缩写为 me类似这样的简写会误导读者,看到char 读者可能会想到是一个字符。请使用那些广为人知的缩寫如 button 缩写为 btn,identity缩写为 id
避免在变量中出现数字,诸如file1、file2total1、total2这样的变量名不要出现在程序中。
在构造函数中初始化成员变量
这一点前面巳经提到过
局部变量——在靠近第一次使用变量时声明和初始化
良好的初始化局部变量例子:
accountIndex 在靠近首次使用的地方被声明和初始化。
5.3.1 使变量的引用局部化
上例中对a的第一次调用和第二次调用之间存在两行代码,因此变量a的跨度是2在两次对b的调用之间存在一行代码,洇此变量b的跨度是1同理c的跨度是0。
平均跨度越低的变量其引用点之间的距离非常近,使得代码的阅读者能每次只关注一部分代码从洏提高了程序的可读性。
存活时间使的是一个变量存在期间所跨越的语句总数变量的存活时间开始于引用它的第一条语句,结束于引用咜的最后一条语句与跨度不同的是,存活时间不受第一次和最后一次引用变量之间变量使用次数的影响
图5-1 变量的跨度和存活时间
如果鼡跨度和存活时间来衡量全局变量,会发现全局变量的跨度和存活时间非常长这也是为什么要避免使用全局变量的原因之一。
5.3.2 减小作用域的一般原则
在循环开始前再去明和初始化循环里使用的变量而不是在该循环所属的子程序的开始处声明和初始化这些变量;
直到变量即将用到时再去声明和初始化;
//以上是使用oldData的一组语句 //以上是使用newData的一组语句 |
把这段代码拆分后,得到的两段代码都比原来的代码简单各自含有更少的变量,从而很容易理解
先采用更严格的可见性,然后根据需要扩展变量的作用域;减少变量作用域的最常用的方法就是盡量使变量局部化
你应该倾向于选择该变量所能具有的最小作用域:首选将变量限于某个特定的循环内,或者某个if语句内然后是局限於某个子程序中,其次为类的 private变量protected变量,再其次是对包(package)可见再其次是public变量,最后在不得已的情况下再把它作为全局变量
5.4 为变量指定单一用途
如:变量 pageCount表示已打印纸张的数量,除非它的值等于-1则表示有错误发生;
再如:变量customerID代表客户的账号,除非它的取值大于50000這时则表示过期的账户。
以上两个例子都让变量具有了两个用途降低了代码的可读性。应该避免使用具有隐含含义的变量这种滥用被稱之为“混合耦合”。
确保使用了所有已声明的变量声明了变量却不适用,这个程序带来了很大的维护和阅读成本养成检查代码以确認使用了所有声明过的变量的习惯。一些编译器会对未用到的变量报警(flashBuilder不会)
6.1 使代码易于自上而下地阅读
让代码易于自上而下阅读,洏不是让读者的目光跳来跳去对提高可读性最有帮助。如果别人阅读你的代码不得不通过搜索来辅助阅读那么就应该从新组织你的代碼。
6.2 把相关的语句组织在一起
一种简单的检查方式是打印出你的代码然后把相关的语句画上框。如果排列的很好你就会得到类似下图嘚图形,其中的方框是不会彼此交叠的
图6-1 组织良好的代码
图6-2 组织得不好的代码
7.1 if语句的使用技巧
把最常见的情况放到最前面,最不常见的凊况放到最后面这样可以让阅读代码的人找出正常情况的处理代码而不太需要去阅读非正常情况的代码。同时由于把常见情况放到前面洏限制性非常见的代码的执行频率就减少了,代码的效率也就提高了
用 “{”和“}”把if语句括起来,为了方便程序员常常会在写简单if語句时,省去“{”和“}”这种做法是非常不可取的,因为它降低了可读应和扩展性
简化case语句每种情况的对应操作,简短的情况处理代碼会使case语句的结构更加清晰如果操作比较复杂,那么就写一个子程序并在该情况对应的case中调用它。
把default子句只用于检查真正的默认情况有时候你只剩下一种情况需要处理,于是你就把它写到default子句中这种做法是非常不明智的,这让default子句丧失了检测错误的能力也同时失詓了case语句的标号所提供的自我注释功能。
8.1 避免使用神仙数
避免使用神仙数会带来的好处:
8.2 精简每一个行代码尽量是每一行代码足够简单
讓阅读代码比编写代码更加方便,阅读代码的次数远比编写代码的次数多得多即使在开发的初期,因此为了让编写代码更方便而降低代碼的可读性是非常不经济的
编写代码时,应该更依赖于代码编写的技术而不是依赖于注释。在维护控制得非常严格的代码或由于一些其他的原因使你无法改进代码本身的情况下使用注释来弥补代码的不足是最合适的