diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2125666 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/CONTENT.md b/CONTENT.md new file mode 100644 index 0000000..31c86ef --- /dev/null +++ b/CONTENT.md @@ -0,0 +1,41 @@ +# PHP面试指南 + +> 忘掉你的经验,试试我的方法。 + +## 市场前景 + +### php份额 + +### 技术成熟度曲线 + +## 薪资水平 + +### 程序员 + +### php + +## 作者介绍 + +### 从业经验 + +### 为什么要写这个 + +## 使用指南 + +### 应聘者 + +### 面试官 + +## 我理解的面试 + +### 岗位的来由 + +### 面试流程 + +### 工作能力证明 + +## 问答列表 + +## 参考资料 + +## 版权声明 \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/PHP-Interview-QA.md b/PHP-Interview-QA.md deleted file mode 100644 index 05f8981..0000000 --- a/PHP-Interview-QA.md +++ /dev/null @@ -1,795 +0,0 @@ -# MySQL - -## 1.MySQL体系结构 - -- 结构体系组成 - ->SQL接口,解析器,优化器,缓存,存储引擎 - -- 体系结构图 - -![体系结构图](./assets/mysql-01.png) - ->Connectors:不同语言中与SQL的交互 - ->Management Serveices & Utilities: 系统管理和控制工具 - ->Connection Pool: 连接池 - ->SQL Interface: SQL接口 - ->Parser: 解析器 - ->Optimizer: 查询优化器 - ->Cache和Buffer:查询缓存 - ->Engine:存储引擎 - -## 2.字段类型 - -- 数字类型 - -|类型|长度|备注| -|-|-|-| -|tinyint|1bit| -|smallint|2bit| -|mediumint|3bit| -|int|4bit| -|bigint|8bit| -|float|4bit|8位精度| -|double|8bit|16位精度| - -- 字符类型 - -|类型|长度|备注| -|-|-|-| -|char|2^8长度|固定长度| -|varchar|2^16长度|可变长度| -|tinytext|2^8长度|可变长度| -|text|2^16长度|可变长度| -|mediumtext|2^24长度|可变长度| -|longtext|2^32长度|可变长度| -|blob|二进制数据| - -- 日期类型 - -|类型|示例| -|-|-| -|date|'2018-7-30' 日期| -|datetime|'2018-7-30 11:20:01' 日期时间| -|timestamp|自动存储记录修改时间| -|time|'11:19:47' 时间| -|year|'2018' 年份| - -- 数据类型属性 - -|关键字|含义| -|-|-| -|NULL|数据列可包含NULL值| -|NOT NULL|数据列不可NULL值| -|DEFAULT|默认值| -|PRIMARY KEY|主键| -|AUTO_INCREMENT|自动递增| -|UNSIGNED|无符号| -|CHARACTER SET name|指定字符集| - -## 3.char和varchar数据类型区别 - -- char - ->善于存储经常改变的值,或者长度相对固定的值,比如type、ip地址或md5之类的数据,不容易产生碎片 - -- varchar - ->善于存储值的长短不一的列,也是用的最多的一种类型,节省磁盘空间 ->保存可变长度字符串,范围0-65535(但受到单行最大64kb的限制)。比如用 varchar(30) 去存放abcd,实际使用5个字节,因为还需要使用额外1个字节来标识字串长度(0-255使用1个字节,超过255需要2个字节) ->update时varchar列时,如果新数据比原数据大,数据库需要重新开辟空间,这一点会有性能略有损耗,但innodb引擎下查询效率比char高一点。这也是innodb官方推荐的类型 - -## 4.存储引擎 - -- MyISAM - ->不支持事务,SELECT/INSERT速度较快,非聚簇索引 - -- InnoDB - ->支持事务 - ->更新密集型表,并发场景 - ->行级锁定 - ->自动容灾恢复 - ->外键约束 - ->聚簇索引 - -- MyISAM和InnoDB比较 - ->MyISAM必须依靠操作系统来管理读取与写入的缓存,而InnoDB则是有自己的读写缓存管理机制 - -- Merge存储引擎(MRG_MyISAM) - ->允许将一组使用MyISAM存储引擎的并且表结构相同(即每张表的字段顺序、字段名称、字段类型、索引定义的顺序及其定义的方式必须相同)的数据表合并为一个表,方便了数据的查询。常用于分表日志查询 - -## 5.常见索引 - -- 索引概念 - ->索引好比一本书的目录,用来更快的找到内容,索引不是越多越好,索引也需要占用空间 - -- 索引分类 - -![索引分类](./assets/mysql-02.png) - -- 索引创建 - -```mysql -普通索引 ALTER TABLE `table_name` ADD INDEX index_name (`column`) -唯一索引 ALTER TABLE `table_name` ADD UNIQUE (`column`) -主键索引 ALTER TABLE `table_name` ADD PRIMARY KEY (`column`) -全文索引 ALTER TABLE `table_name` ADD FULLTEXT (`column`) -组合索引 ALTER TABLE `table_name` ADD INDEX index_name (`column1`, `column2`, `column3`) -``` - -- 索引区别 - ->普通索引:最基本的索引,没有任何限制 - ->唯一索引:与"普通索引"类似,不同的就是:索引列的值必须唯一,但允许有空值 - ->主键索引:它是一种特殊的唯一索引,不允许有空值 - ->全文索引:仅可用于MyISAM表,针对较大的数据,生成全文索引很耗时好空间 - ->组合索引:为了更多的提高mysql效率可建立组合索引,遵循"最左前缀"原则 - -## 6.聚族索引与非聚族索引的区别 - ->按物理存储分类:聚簇索引(clustered index)、非聚簇索引(non-clustered index) - ->聚簇索引的叶子节点就是数据节点,而非聚簇索引的叶子节点仍然是索引节点,只不过有指向对应数据块的指针 - -## 7.事务机制 - -- 数据库事务(Database Transaction) - ->是指作为单个逻辑工作单元执行的一系列操作,要么完全执行,要么完全地不执行 - -- ACID特性 - ->原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability) - ->原子性:指事务包含的所有操作要么全部成功,要么全部失败回滚 - ->一致性:指事务必须使数据库从一个一致的状态变到另外一个一致的状态,也就是执行事务之前和之后的状态都必须处于一致的状态 - ->隔离性:指当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离 - ->持久性:指一个事务一旦被提交了,那么对于数据库中的数据改变就是永久性的,即便是在数据库系统遭遇到故障的情况下也不会丢失提交事务的操作 - -- 事务隔离性 - ->当多个线程都开启事务操作数据库中数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性 - -- 无隔离产生问题 - ->脏读(Dirty Read):一个事务处理过程里读取了另一个未提交的事务中的数据 - ->不可重复读(NonRepeatable Read):一个事务范围内多次查询却返回了不同的数据值 - ->幻读(Phantom Read):在一个事务中读取到了别的事务插入的数据,导致前后不一致 - -- 隔离级别 - ->Serializable、Repeatable read、Read committed、Read uncommitted - -|隔离级别|脏读|不可重复读|幻读| -|-|-|-|-| -|未提交读(Read uncommitted)|可能|可能|可能| -|已提交读(Read committed)|不可能|可能|可能| -|可重复读(Repeatable read)|不可能|不可能|可能| -|可串行化(Serializable)|不可能|不可能|不可能| - -- 锁方案 - ->一次性锁、两端锁 - -- MySQL锁方案 - ->表锁:对一整张表加锁,并发能力低下 - ->行锁:只锁住特定行的数据,并发能力强,MySQL一般都是用行锁来处理并发事务 - -## 8.BTree与BTree-/BTree+索引原理 - -- BTree - ->二叉树导致树高度非常高,逻辑上很近的节点,物理上非常远,无法利用局部性,IO次数多,查找效率低 - -- BTree- - ->每个节点都是二元数组[key,data],所有节点都可以存储数据,key为索引,data为索引外的数据。插入删除数据会破坏BTree性质,插入数据时候,需要对数据进行分裂、合并、转移等操作保持BTree性质,造成IO操作频繁 - -- BTree+ - ->非叶子节点不存储data,只存储索引key,只有叶子节点才存储data - -- MySQL中的BTree+ - ->在经典BTree+的基础上进行了优化,增加了顺序访问指针。在BTree+的每个叶子节点增加了一个指向相邻叶子节点的指针,形成了带顺序访问指针的BTree+,提高了区间访问性能 - -## 参考资料 -- [MySQL体系结构](http://www.cnblogs.com/yjf512/archive/2012/02/06/2339496.html) -- [MySQL字符数据类型char与varchar的区别](http://seanlook.com/2016/04/28/mysql-char-varchar-set/) -- [MySQL有哪些索引](https://segmentfault.com/q/1010000003832312) -- [MySQL聚簇索引](https://yq.aliyun.com/articles/142879) -- [MySQL事务处理机制](https://www.jianshu.com/p/bcc614524024) -- [MySQL索引背后的数据结构及算法原理](http://blog.codinglabs.org/articles/theory-of-mysql-index.html) - -# Redis -## 1.Redis主要特点 - -> Redis是一个高性能的KV数据库,支持丰富的数据类型,提供多种语言的API,支持数据的持久化,性能极高,常用于Cache - -## 2.Redis数据类型 - -> STRING,HASH,LIST,SET,SORTEDSET,GEO,PUB/SUB - -## 3.跳跃表与Redis - -- 跳跃表 - ->跳跃表是一种随机化数据结构,查找、添加、删除操作都可以在对数期望时间下完成 - -- 跳跃表在Redis的应用 - ->跳跃表在Redis的唯一作用,就是实现有序集数据类型 - ->跳跃表将指向有序集的score值和member域的指针作为元素,并以score值为索引对有序集元素进行排序 - -## 参考资料 -- [跳跃表](https://redisbook.readthedocs.io/en/latest/internal-datastruct/skiplist.html) - -# Web - -## 1.JavaScript事件的三个阶段 - -> 捕获,目标,冒泡阶段,低版本IE不支持捕获阶段 - -## 2.闭包原理及应用 - ->闭包就是将函数内部和函数外部连接起来的一座桥梁 - ->读取函数内部的变量,让这些变量的值始终保持在内存中 - -## 3.跨域 - -- 什么是跨域 - ->简单地理解就是因为JavaScript同源策略的限制 - -- 什么是同源策略 - ->在JavaScript中,同源策略是一个很重要的安全理念,它保证数据的安全性有着重要的意义。同源策略规定跨域之间脚本是隔离的 - -- 相同域 - ->相同协议,相同端口,相同host - -- 跨域资源 - ->单向数据请求(JSONP)、双向消息通信 - -## 4.JSONP原理 - ->HTML里面所有带src属性的标签都可以跨域,如iframe,img,script等 - ->所以可以把需要跨域的请求改成用script脚本加载即可,服务器返回执行字符串,但是这个字符串是在window全局作用域下执行的,你需要把他返回到你的代码的作用域内,这里就需要临时创建一个全局的回调函数,并把到传到后台,最后再整合实际要请求的数组,返回给前端,让浏览器直接调用,用回调的形式回到你的原代码流程中 - -## 5.CSS选择器的优先级 - -- 优先级 - ->!important > 内联 > id选择器 > 类选择器 > 标签选择器 - -- CSS选择器的种类: - ->1.id选择器(# myid) - ->2.类选择器(.myclassname) - ->3.标签选择器(div, h1, p) - ->4.相邻选择器(h1 + p) - ->5.子选择器(ul > li) - ->6.后代选择器(li a) - ->7.通配符选择器( * ) - ->8.属性选择器(a[rel = "external"]) - ->9.伪类选择器(a: hover, li:nth-child) - -## 6.CSS盒子模型 - ->属性:element、padding、border、margin - ->显示方式:block、inline、flex - -## 7.CSS清除浮动 - -## 8.相对定位relative、浮动float、绝对定位absolute区别 - ->相对定位:按一定偏移量依次排列定位 - ->浮动定位:浮动框可以向左/右便宜,不影响后续框 - ->绝对定位:每个定位框都是一个单独的图层,不会对其他层框的定位产生影响 - -## 9.VUE双向绑定原理 - ->发布者-订阅模式(backbone.js)、脏值检查(angular.js)、数据劫持(vue.js) - ->vue.js则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调 - -## 10.性能优化 - ->js/css合并、使用CDN、图片合并、利用HTTP缓存机制、开启gzip压缩、浏览器加载/解析/渲染机制 - -## 参考资料 -- [大部分人都会做错的经典JS闭包面试题](http://www.cnblogs.com/xxcanghai/p/4991870.html) -- [学习Javascript闭包](http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html) -- [跨域-知识](http://www.cnblogs.com/scottckt/archive/2011/11/12/2246531.html) -- [剖析Vue原理&实现双向绑定MVVM](https://segmentfault.com/a/1190000006599500) -- [网站性能优化实践总结](https://juejin.im/entry/596c77536fb9a06ba4745e3c) - -# 安全问题 - -## 1.CSRF攻击 - ->全称Cross-site request forgery,跨站请求伪造,攻击者盗用了你的身份,以你的名义发送恶意请求 - ->登录受信任网站A,并在本地生成Cookie。在不登出A的情况下,访问危险网站B - ->如何防御:在客户端页面增加随机数、提交表单增加CSRF token - -- 具体实例 - -## 2.XSS攻击 - ->全称Cross SiteScript,跨站脚本攻击 - ->分类:存储型XSS、反射型XSS、DOM-XSS - ->如何防御:从输入到输出都需要过滤、转义 - -- 具体实例 - -## 3.SQL注入 - ->SQL注入就是通过操作输入来修改后台SQL语句达到代码执行进行攻击目的的技术 - -- 如何防御 - ->严格限制web应用数据库权限,给用户提供仅仅能满足工作的最低权限 - ->检查输入的数据是否具有所期望的数据格式 - ->避免打印SQL错误信息,暴露SQL语句 - -- 具体实例 - -```mysql -SELECT * FROM table WHERE id=1;DELECT FROM table WHERE id=1; -SELECT * FROM table WHERE id=1 AND (SELECT COUNT(*) FROM try_table)>-1; -``` - -## 4.IP地址能被伪造吗 - -## 5.include请求参数 - -## 6.md5逆向原理 - -## 7.DOS攻击 - -## 参考资料 -- [浅谈CSRF攻击方式](http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html) -- [XSS攻击和防御详解](https://juejin.im/entry/58a598dc570c35006b5cd6b4) -- [避免SQL注入](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/09.4.md) - -# 网络协议 - -## 1.UDP的主要特点 - ->无连接的、尽最大努力交付、面向报文、没有拥塞控制、支持一对一,一对多和多对多的交互通信、首部开销小 - -## 2.TCP握手三次,断开四次,TIME-WAIT - -![建立TCP连接](./assets/tcp-01.png) - -![TCP连接释放](./assets/tcp-02.png) - -## 3.socket - ->socket:网络中进程通过socket进行通信 - ->基本操作:socket()、bind()、listen()/connect()、accept()、read()/write()、close() - -## 4.HTTP协议 - ->HTTP方法 - ->HTTP首部字段 - ->HTTP状态码 - ->HTTP2特点:二进制协议、多路复用、头压缩、服务器推送 - -## 5.websocket协议 - ->HTTP握手,通过首部字段upgrade标识websocket,切换协议,进行通信 - -## 6.GET与POST请求方式区别 - ->GET请求会将参数跟在URL后进行传递,而POST请求则是作为HTTP消息实体内容发送,在AJAX请求中,这种区别对用户不可见 - ->GET方式对传输有大小限制,而POST则大的多 - ->GET请求和数据会被浏览器缓存起来 - ->GET方式和POST方式传递的数据在服务器的获取也不同 - -## 7.RESTful API - -## 参考资料 -[HTTP/2协议–特性扫盲篇](http://www.cnblogs.com/yingsmirk/p/5248506.html) -[Socket通信原理和实践](https://blog.csdn.net/dlutbrucezhang/article/details/8577810) - -# PHP - -## 1.echo、print、print_r、var_dump的区别 - ->echo:输出一个或多个字符串 - ->print:输出字符串 - ->print_r:打印关于变量的易于理解的信息 - ->var_dump:打印关于变量的易于理解的信息(带类型) - -## 2.超全局变量 - -> `$GLOBALS`、`$_SERVER`、`$_GET`、`$_POST`、`$_FILES`、`$_COOKIE`、`$_SESSION`、`$_REQUEST`、`$_ENV` - -## 3.PHP支持回调的函数,实现一个 - ->array_map、array_filter、array_walk、usort - ->is_callable + callbacks + 匿名函数实现 - -## 4.发起HTTP请求有哪几种方式,它们有何区别 - ->cURL、file_get_contents、fopen、fsockopen - -## 5.对象关系映射/ORM(Object Relational Mapping) - -- 优点 - ->缩短编码时间、减少甚至免除对model的编码,降低数据库学习成本 - ->动态的数据表映射,在表结构发生改变时,减少代码修改 - ->可以很方便的引入附加功能(cache层) - -- 缺点 - ->映射消耗性能、ORM对象消耗内存 - ->SQL语句较为复杂时,ORM语法可读性不高(使用原生SQL) - -## 6.MVC的理解 - ->MVC架构中M是指数据模型,V是指用户界面,C则是控制器;MVC的思想是模块化分离,为了代码的重用和增强代码的维护性和扩展性出发的,其中MVC的实现有一定的思想和原则 - -## 7.类的静态调用和实例化调用 - ->调用前初始化,调用时初始化 - -## 8.常见PHP框架特点 - -- ThinkPHP - ->URL模式:系统支持普通模式、PATHINFO模式、REWRITE模式和兼容模式的URL方式,支持不同的服务器和运行模式的部署,配合URL路由功能,可以随心所欲地构建需要的URL地址和进行SEO优化工作 - ->查询语句:内建丰富的查询机制,包括组合查询、符合查询、区域查询、统计查询、定位查询、动态查询、和原生查询、让数据查询简洁高效 - ->分组模块:不用担心大项目的分工协调和部署问题,分组模块解决跨项目的难题 - -- Laravel - ->包含Web开发、包管理、代码生成、ORM、常见组件(cache/log)、路由管理、中间件、依赖注入 - -- Biny - ->支持跨库连表,条件复合筛选,查询PK缓存等 - ->同步异步请求分离,类的自动化加载管理 - ->支持Form表单验证,支持事件触发机制 - ->支持浏览器端调试,快速定位程序问题和性能瓶颈 - ->具有sql防注入,html自动防xss等特性 - -## 9.设计模式(design pattern) - -> 设计模式是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案 - -- 常见设计模式: - ->单例模式 ->>定义:确保一个类只有一个实例,并提供一个全局访问点 ->>使用场景:单入口模式 - ->简单工厂模式 ->>定义:简单工厂模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例 ->>使用场景:对象管理(初始化) - ->工厂方法模式 ->>定义:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类 ->>设计原则:依赖抽象,不要依赖具体类。(依赖倒置) ->>使用场景: - ->抽象工厂模式 ->>定义:提供了一接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类 ->>设计原则:依赖抽象,不要依赖具体类。(依赖倒置) ->>使用场景: - ->策略模式 ->>定义:定义算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户 ->>设计原则:1.封装变化。2.多用组合,少用继承。3.针对接口编程,不针对实现编程。 ->>使用场景:SDK封装 - ->观察者模式 ->>定义:在对象之间定义一对多的依赖,这样一来当一个对象改变状态,依赖它的对象都会收到通知,并自动更新 ->>设计原则:为了交互对象之间的松耦合设计而努力 ->>使用场景:回调机制 - ->适配器模式 ->>定义:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间 ->>使用场景:代理服务器协议转换 - ->装饰者模式 ->>定义:动态的将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案 ->>设计原则:对扩展开放,对修改关闭 ->>使用场景:路由功能 - -## 10.工厂方法模式与抽象工厂模式区别 - ->工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个 - ->工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个 - -## 11.base64编码原理 - -![base64编码原理](./assets/base64.png) - -## 12.ip2long实现 - -![ip2long实现](./assets/ip2long.png) - -## 13.代码执行过程 - ->PHP代码 => 启动php及zend引擎,加载注册拓展模块 => 对代码进行词法/语法分析 => 编译成opcode(opcache) => 执行opcode - ->当前作用域分配内存,充当运行栈,局部变量分配在当前栈,函数调用时压栈,返回时出栈 - -## 14.弱类型变量如何实现 - ->PHP中声明的变量,在zend引擎中都是用结构体zval来保存,通过共同体实现弱类型变量声明 - -## 15.垃圾回收机制 - ->引用计数器 - -## 16.进程间通信方式 - ->消息队列、socket、信号量、共享内存、信号、管道 - -## 17.链式调用实现 - ->类定义一个内置变量,让类中其他定义方法可访问到 - -## 18.多进程同时写一个文件 - ->加锁、队列 - -## 19.PHP拓展 - -## 20.PHP7新特性 - ->标量类型声明、返回值类型声明、通过define()定义常量数组、匿名类、相同命名空间类一次性导入 - -## 21.PHP7底层优化 - ->ZVAL结构体优化,占用由24字节降低为16字节 - ->内部类型zend_string,结构体成员变量采用char数组,不是用char* - ->PHP数组实现由hashtable变为zend array - ->函数调用机制,改进函数调用机制,通过优化参数传递环节,减少了一些指令 - -## 22.构造函数和析构函数 - -## 23.PHP不实例化调用方法 - ->CLASS::METHOD() 静态方法 - -## 参考资料 - -- [深入理解PHP内核](http://www.php-internals.com/book/) -- [PHP中的回调、匿名函数与闭包](http://jalan.space/2017/07/21/2017-07-21-php-callback/) -- [从PHP 5.6.x 移植到 PHP 7.0.x](http://php.net/manual/zh/migration70.php) -- [PHP7革新与性能优化](http://hansionxu.blog.163.com/blog/static/24169810920158704014772/) -- [常用设计模式汇总](https://juejin.im/entry/58041fc10e3dd9005713384e) -- [腾讯开源Biny框架](https://github.com/Tencent/Biny) -- [类与对象](http://php.net/manual/zh/language.oop5.php) - -# 服务器 - -## 1.进程、线程、协程区别 - -- 进程(process) - ->进程是一个程序在一个数据集中的一次动态执行过程,可以简单理解为“正在执行的程序”,它是CPU资源分配和调度的独立单位 - -- 线程(thread) - ->线程是在进程之后发展出来的概念。 线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合和堆栈共同组成。一个进程可以包含多个线程 - -- 协程(coroutine) - ->协程是一种用户态的轻量级线程,又称微线程,英文名Coroutine,协程的调度完全由用户控制 - -## 2.Linux进程 - ->进程属性:进程号pid、父进程号ppid、进程组号pgid - ->进程状态:就绪、运行、可中断、不可中断、僵死、停止 - -## 3.反向代理 - -- 概述 - ->反向代理(Reverse Proxy)方式是指以代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络上的服务器;并将从服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器 - -- 工作原理 - ->反向代理服务器通常有两种模型:作内容服务器的替身、作为内容服务器的负载均衡器 - -## 4.负载均衡 - -- 概述 - ->负载均衡(Load Balance),意思是将负载(工作任务,访问请求)进行平衡、分摊到多个操作单元(服务器,组件)上进行执行。是解决高性能,单点故障(高可用),扩展性(水平伸缩)的终极解决方案 - -- 负载均衡原理 - ->系统的拓展可以分为纵向(垂直)拓展和横向(水平)拓展 - ->采用横向拓展方式,通过添加机器来满足大型网站服务的处理能力 - ->应用集群:将同一应用部署到多台机器上,组成处理集群,接收负载均衡设备分发的请求,进行处理,并返回相应数据。 - ->负载均衡设备:将用户访问的请求,根据负载均衡算法,分发到集群中的一台处理服务器 - -- 负载均衡分类 - ->DNS负载均衡:DNS服务器配置多个A记录,记录对应的服务器构成集群 - ->IP负载均衡:在网络层通过修改请求目标地址进行负载均衡 - ->链路层负载均衡:通信协议的数据链路层修改mac地址,进行负载均衡,最广泛方式 - ->混合型负载均衡 - -## 5.nginx中fastcgi_pass监听,unix socket和tcp socket的区别 - ->nginx和fastcgi通信方式有两种:TCP、unix socket - ->TCP和socket方式区别 - ->socket可以很方便进行进程通信,可以使用字节流和数据队列方式,而管道通信只能通过字节流 - ->socket比TCP方式消耗资源更少,高并发时tcp方式更稳定 - ->TCP方式做负载均衡更方便 - -## 6.消息队列 - ->https://cloud.tencent.com/developer/article/1006035 ->https://www.jianshu.com/p/689ce4205021 - -## 7.穿透、雪崩 - ->https://blog.csdn.net/mikeszhang/article/details/47728167 - -## 参考资料 -- [进程、线程与协程的比较](https://blog.csdn.net/Blateyang/article/details/78088851) -- [反向代理服务器的工作原理](https://blog.csdn.net/keyeagle/article/details/6723408) -- [大型网站架构系列:负载均衡详解](http://www.cnblogs.com/itfly8/p/5043435.html) -- [nginx和php-fpm 通信使用unix socket还是TCP,及其配置](https://blog.csdn.net/pcyph/article/details/46513521) - -# 业务设计 - -## 1.网易盖楼 - -## 2.秒杀设计 - -## 3.消息队列 - -## 4.共享SESSION - -## 5.下单后30分钟未支付取消订单 - -## 6.IP对应省市效率尽可能高 - -## 7.详细描述输入地址到打开网页过程 - -## 8.千万用户表设计 - -## 参考资料 - -# 线上故障 - -## 1.客户端热更新失败 - -## 2.Redis实例used_memory达到80% - -## 3.游戏任务完成了进度未更新 - -## 4.测试服HTTP请求未响应 - -## 5.游戏账号被盗 - -## 参考资料 - -# 个人简历 - -# 自我介绍 - -# 离职原因 - -## 1.跳槽频繁 - -## 2.这次换工作原因 - -# 职业规划 - -# 准备问题 - -- 1.工作挑战大不大? - -- 2.项目开发是否写测试用例,项目上线先是否会进行压力测试 - -- 3.业务前景如何? - -- 4.技术氛围如何? - -- 5.根据这次面试,对个人进行评价,帮助成长 - -- 6.融资计划 - -- 7.是否有加班费/调休,公司福利,社保公积金缴纳基数 - -# 结束声明 - -> 本资料仅供参考,不保证正确性 - -> 作者:凌枫 Email:colinlets@gmail.com 链接:https://github.com/colinlet/PHP-Interview-QA \ No newline at end of file diff --git a/PHP-Interview-QA.pdf b/PHP-Interview-QA.pdf deleted file mode 100644 index 6e81ffc..0000000 Binary files a/PHP-Interview-QA.pdf and /dev/null differ diff --git a/README.md b/README.md index ddd2ce6..413e778 100644 --- a/README.md +++ b/README.md @@ -1,154 +1,388 @@ -# PHP面试问答 - -> 结合实际PHP面试,汇总自己遇到的问题,以及网上其他人遇到的问题,尝试提供简洁准确的答案 - -> 包含MySQL、Redis、Web、安全、网络协议、PHP、服务器、业务设计、线上故障、个人简历、自我介绍、离职原因、职业规划、准备问题等部分 - -# 一般面试流程 - -![面试流程](./assets/interview.png) - -# QA目录 - -MySQL - -- MySQL体系结构 -- 字段类型 -- char和varchar数据类型区别 -- 常见索引 -- 聚族索引和非聚族索引的区别 -- 事务机制 -- BTree与BTree-/BTree+索引原理 -- 参考资料 - -Redis - -- Redis主要特点 -- Redis数据类型 -- 跳跃表与Redis -- 参考资料 - -Web - -- JavaScript事件的三个阶段 -- 闭包原理及应用 -- 跨域 -- JSONP原理 -- CSS选择器的优先级 -- CSS盒子模型 -- CSS清除浮动 -- 相对定位relative、浮动float、绝对定位absolute区别 -- VUE双向绑定原理 -- 性能优化 -- 参考资料 - -安全问题 - -- CSRF攻击 -- XSS攻击 -- SQL注入 -- IP地址能被伪造吗 -- include请求参数 -- md5逆向原理 -- DOS攻击 -- 参考资料 - -网络协议 - -- UDP的主要特点 -- TCP握手三次,断开四次,TIME-WAIT -- socket -- HTTP协议 -- websocket协议 -- GET与POST请求方式区别 -- 参考资料 - -PHP - -- echo、print、print_r、var_dump的区别 -- 超全局变量 -- PHP支持回调的函数,实现一个 -- 发起HTTP请求有哪几种方式,它们有何区别 -- 对象关系映射/ORM(Object Relational Mapping) -- MVC的理解 -- 类的静态调用和实例化调用 -- 常见PHP框架特点 -- 设计模式(design pattern) -- 工厂方法模式与抽象工厂模式区别 -- base64编码原理 -- ip2long实现 -- 代码执行过程 -- 弱类型变量如何实现 -- 垃圾回收机制 -- 进程间通信方式 -- 链式调用实现 -- 多进程同时写一个文件 -- PHP拓展 -- PHP7新特性 -- PHP7底层优化 -- 构造函数和析构函数 -- PHP不实例化调用方法 -- 参考资料 - -服务器 - -- 进程、线程、协程区别 -- Linux进程 -- 反向代理 -- 负载均衡 -- nginx中fastcgi_pass监听,unix socket和tcp socket的区别 -- 消息队列 -- 参考资料 +# PHP 面试问答 + +> PHP 很没前途,请把工作留给我,谢谢! + +## 一、PHP 篇 + +- [【试读】编程语言基础](./docs/php/编程语言简介.md) +- 【试读】PHP 与编程范式 +- [【试读】当下最流行的 PHP 本地环境搭建方式](./docs/php/当下最流行的PHP本地环境搭建方式.md) +- 【试读】代码风格指南 +- 【试读】代码注释 +- [【试读】将变量打印出来,你知道哪些方式](./docs/php/将变量打印出来你知道哪些方式.md) +- 【试读】使用 xdebug 调试你的代码 +- 【试读】基础知识 +- [【试读】单引号和双引号的区别](./docs/php/单引号和双引号的区别.md) +- [【试读】isset 和 empty 的区别之如何判空](./docs/php/isset和empty的区别之如何判空.md) +- 【试读】面向对象编程 +- 【试读】PHP 与函数式编程 +- 【试读】PHP 与元编程 +- 【试读】命名空间 +- 【试读】PHP 标准库(SPL) +- 【试读】日期和时间 +- 【试读】请使用 UTF-8 编码 +- 【试读】本地化与国际化 +- 【试读】2025 年有哪些流行的框架 +- 【试读】如何进行依赖管理 +- 【试读】依赖注入 +- 【试读】错误与异常 +- [【试读】composer 包升级](./docs/php/composer包升级.md) +- 【试读】MySQL 扩展 +- 【试读】PDO 扩展 +- 【试读】单元测试 +- 【试读】Postman + +## 二、数据结构与算法篇 -业务设计 +## 三、网络篇 -- 网易盖楼 -- 秒杀设计 +## 四、设计模式篇 + +- 【试读】如何解决复杂问题 + +## 五、数据基础设施篇 + +## 六、架构篇 + +-【试读】领域驱动设计(DDD) + +## 七、服务器篇 + +## 八、安全篇 + +- 【试读】密码学简介 +- 【试读】加密与编码 + +## 九、客户端篇 + +## 十、番外篇 + +- [【试读】你的编码热情是如何消退的?](./docs/番外/你的编码热情是如何消退的.md) + + +结合实际PHP面试,汇总自己遇到的问题,以及网上其他人遇到的问题,尝试提供简洁准确的答案 + +包含网络、数据结构与算法、PHP、Web、MySQL、Redis、Linux、安全、设计模式、架构、面试等部分 + +> 本仓库将持续更新,fork 无法看到最新内容,建议 Watch 或 Star ~~ + +**温馨提示** + +- 分享面试遇到的问题,通过提交 Issue + +- 参与项目内容完善,通过提交 PR,提交内容请尽量保证`准确性`和`可读性` + +- 本仓库需要什么内容:`实际经典面试题` + `靠谱简答` + `详细深入文章(必要的话)` + +## 一、面试准备 + +![面试流程](./docs/assets/interview.png) + +> 简历 -> 笔试 -> 技术一面 -> 技术二面 -> 技术三面 -> HR面 -> offer + +- [技术岗面试潜规则](./docs/面试/技术岗面试潜规则.md) +- [设计一份吸引面试官的简历](./docs/面试/设计一份吸引面试官的简历.md) +- [读懂岗位精准投递](./docs/面试/读懂岗位精准投递.md) +- [做好充分的准备去面试](./docs/面试/做好充分的准备去面试.md) +- [把握面试时的关键点](./docs/面试/把握面试时的关键点.md) +- [捕捉面试官微表情,做出应对策略](./docs/面试/捕捉面试官微表情,做出应对策略.md) +- [巧妙推销自己的3个技巧](./docs/面试/巧妙推销自己的3个技巧.md) +- [判断公司背景,做出合理选择](./docs/面试/判断公司背景,做出合理选择.md) +- [了解行业薪资,清晰找准定位](./docs/面试/了解行业薪资,清晰找准定位.md) +- [目标明确,阐明沟通](./docs/面试/目标明确,阐明沟通.md) +- [工作交接流程福利衔接](./docs/面试/工作交接流程福利衔接.md) +- [如何让工作年限变成优势](./docs/面试/如何让工作年限变成优势.md) + +## 二、问答列表 + +问题和内容分级 + +|星标|难度|岗位|关键字| +|---|---|---|---| +|*|一星|助理工程师|基础知识| +|**|二星|工程师|灵活使用| +|***|三星|高级工程师|深入原理| +|****|四星|资深工程师|疑难杂症| +|****|五星|架构师/专家|领域话语| + +问答模版: + +1、工作内容->实际问题->用到知识->设置问题->期望回答->评分表->面试评价 + +2、被安排充当面试官->网上找题库->题库(自己熟悉的)->八股文->筛选 + +### 1、PHP篇 + +- [static、self、$this 的区别](./docs/03.PHP/QA.md#staticselfthis-的区别) +- [include、require、include_once、require_once 的区别](./docs/03.PHP/QA.md#includerequireinclude_oncerequire_once-的区别) +- [数组处理函数](./docs/03.PHP/QA.md#常见数组函数) +- [Cookie 和 Session](./docs/03.PHP/QA.md#cookie-和-session) +- [预定义变量](./docs/03.PHP/QA.md#预定义变量) +- [传值和传引用的区别](./docs/03.PHP/QA.md#传值和传引用的区别) +- [构造函数和析构函数](./docs/03.PHP/QA.md#构造函数和析构函数) +- [魔术方法](./docs/03.PHP/QA.md#魔术方法) +- [public、protected、private、final 区别](./docs/03.PHP/QA.md#publicprotectedprivatefinal-区别) +- [客户端/服务端 IP 获取,了解代理透传 实际IP 的概念](./docs/03.PHP/QA.md#客户端服务端-ip-获取了解代理透传-实际ip-的概念) +- [类的静态调用和实例化调用](./docs/03.PHP/QA.md#类的静态调用和实例化调用) +- [接口类和抽象类的区别](./docs/03.PHP/QA.md#接口类和抽象类的区别) +- [PHP 不实例化调用方法](./docs/03.PHP/QA.md#php-不实例化调用方法) +- [php.ini 配置选项](./docs/03.PHP/QA.md#phpini-配置选项) +- [php-fpm.conf 配置](./docs/03.PHP/QA.md#php-fpmconf-配置) +- [502、504 错误产生原因及解决方式](./docs/03.PHP/QA.md#502504-错误产生原因及解决方式) +- [如何返回一个301重定向](./docs/03.PHP/QA.md#如何返回一个301重定向) +- [PHP 与 MySQL 连接方式](./docs/03.PHP/QA.md#php-与-mysql-连接方式) +- [MySQL、MySQLi、PDO 区别](./docs/03.PHP/QA.md#mysqlmysqlipdo-区别) +- [MySQL 连接池](./docs/03.PHP/QA.md#mysql-连接池) +- [代码执行过程](./docs/03.PHP/QA.md#代码执行过程) +- [base64 编码原理](./docs/03.PHP/QA.md#base64-编码原理) +- [ip2long 实现](./docs/03.PHP/QA.md#ip2long-实现) +- [MVC 的理解](./docs/03.PHP/QA.md#mvc-的理解) +- [主流 PHP 框架特点](./docs/03.PHP/QA.md#主流-php-框架特点) +- [对象关系映射/ORM](./docs/03.PHP/QA.md#对象关系映射orm) +- [串行、并行、并发的区别](./docs/03.PHP/QA.md#串行、并行、并发的区别) +- [同步与异步的理解](./docs/03.PHP/QA.md#同步与异步的理解) +- [阻塞与非阻塞的理解](./docs/03.PHP/QA.md#阻塞与非阻塞的理解) +- [同步阻塞与非同步阻塞的理解](./docs/03.PHP/QA.md#同步阻塞与非同步阻塞的理解) +- [一条echo输出语句是如何执行的](./docs/php/一条echo输出语句是如何执行的.md) +- [FastCGI Process Manager](./docs/php/FastCGI-Process-Manager.md) +- [php支持哪些注释风格](./docs/php/php支持哪些注释风格.md) + +### 2、存储篇 + +- [体系结构](./docs/05.MySQL/QA.md#体系结构) +- [基础操作](./docs/05.MySQL/QA.md#基础操作) +- [数据库设计范式](./docs/05.MySQL/QA.md#数据库设计范式) +- [数据库设计原则](./docs/05.MySQL/QA.md#数据库设计原则) +- [CHAR 和 VARCHAR 数据类型区别](./docs/05.MySQL/QA.md#char-和-varchar-数据类型区别) +- [LEFT JOIN 、RIGHT JOIN、INNER JOIN](./docs/05.MySQL/QA.md#left-join-right-joininner-join) +- [UNION、UNION ALL](./docs/05.MySQL/QA.md#unionunion-all) +- [常用 MySQL 函数](./docs/05.MySQL/QA.md#常用-mysql-函数) +- [锁](./docs/05.MySQL/QA.md#锁) +- [事务](./docs/05.MySQL/QA.md#事务) +- [常见存储引擎](./docs/05.MySQL/QA.md#常见存储引擎) +- [常见索引](./docs/05.MySQL/QA.md#常见索引) +- [聚族索引与非聚族索引的区别](./docs/05.MySQL/QA.md#聚族索引与非聚族索引的区别) +- [BTree 与 BTree-/BTree+ 索引原理](./docs/05.MySQL/QA.md#btree-与-btree-btree-索引原理) +- [分表数量级](./docs/05.MySQL/QA.md#分表数量级) +- [EXPLAIN 输出格式](./docs/05.MySQL/QA.md#explain-输出格式) +- my.cnf 配置 +- 慢查询 +- [一条SQL查询语句是如何执行的](./docs/存储/一条SQL查询语句是如何执行的.md) +- [一条SQL更新语句是如何执行的](./docs/存储/一条SQL更新语句是如何执行的.md) +- [事务隔离:为什么你改了我还看不见?](./docs/存储/事务隔离-为什么你改了我还看不见.md) +- [深入浅出索引(上)](./docs/存储/深入浅出索引-上.md) +- [深入浅出索引(下)](./docs/存储/深入浅出索引-下.md) +- [全局锁和表锁:给表加个字段怎么有这么多阻碍](./docs/存储/全局锁和表锁-给表加个字段怎么有这么多阻碍.md) +- [行锁功过:怎么减少行锁对性能的影响](./docs/存储/行锁功过-怎么减少行锁对性能的影响.md) +- [事务到底是隔离的还是不隔离的](./docs/存储/事务到底是隔离的还是不隔离的.md) +- [普通索引和唯一索引,应该怎么选择](./docs/存储/普通索引和唯一索引,应该怎么选择.md) +- [MySQL为什么有时候会选错索引](./docs/存储/MySQL为什么有时候会选错索引.md) +- [怎么给字符串字段加索引](./docs/存储/怎么给字符串字段加索引.md) +- [为什么我的MySQL会抖一下](./docs/存储/为什么我的MySQL会抖一下.md) +- [为什么表数据删掉一半,表文件大小不变](./docs/存储/为什么表数据删掉一半,表文件大小不变.md) +- [count()这么慢,我该怎么办](./docs/存储/count()这么慢,我该怎么办.md) +- [order by是怎么工作的](./docs/存储/order-by是怎么工作的.md) +- [如何正确地显示随机消息](./docs/存储/如何正确地显示随机消息.md) +- [为什么这些SQL语句逻辑相同,性能却差异巨大](./docs/存储/为什么这些SQL语句逻辑相同,性能却差异巨大.md) +- [为什么我只查一行的语句,也执行这么慢](./docs/存储/为什么我只查一行的语句,也执行这么慢.md) +- [幻读是什么,幻读有什么问题](./docs/存储/幻读是什么,幻读有什么问题.md) +- [为什么我只改一行的语句,锁这么多](./docs/存储/为什么我只改一行的语句,锁这么多.md) +- [MySQL有哪些饮鸩止渴提高性能的方法](./docs/存储/MySQL有哪些饮鸩止渴提高性能的方法.md) +- [MySQL是怎么保证数据不丢的](./docs/存储/MySQL是怎么保证数据不丢的.md) +- [MySQL是怎么保证主备一致的](./docs/存储/MySQL是怎么保证主备一致的.md) +- [MySQL是怎么保证高可用的](./docs/存储/MySQL是怎么保证高可用的.md) +- [备库为什么会延迟好几个小时](./docs/存储/备库为什么会延迟好几个小时.md) +- [主库出问题了,从库怎么办](./docs/存储/主库出问题了,从库怎么办.md) +- [读写分离有哪些坑](./docs/存储/读写分离有哪些坑.md) +- [如何判断一个数据库是不是出问题了](./docs/存储/如何判断一个数据库是不是出问题了.md) +- [Redis 介绍](./docs/06.Redis/QA.md#redis-介绍) +- [Redis 特点](./docs/06.Redis/QA.md#redis-特点) +- [Redis 支持哪些数据结构](./docs/06.Redis/QA.md#redis-支持哪些数据结构) +- [Redis 与 Memcache 区别](./docs/06.Redis/QA.md#redis-与-memcache-区别) +- [发布订阅](./docs/06.Redis/QA.md#发布订阅) +- [持久化策略](./docs/06.Redis/QA.md#持久化策略) +- [Redis 事务](./docs/06.Redis/QA.md#redis-事务) +- [如何实现分布式锁](./docs/06.Redis/QA.md#如何实现分布式锁) +- [Redis 过期策略及内存淘汰机制](./docs/06.Redis/QA.md#redis-过期策略及内存淘汰机制) +- [为什么 Redis 是单线程的](./docs/06.Redis/QA.md#为什么-redis-是单线程的) +- [如何利用 CPU 多核心](./docs/06.Redis/QA.md#如何利用-cpu-多核心) +- [集合命令的实现方法](./docs/06.Redis/QA.md#集合命令的实现方法) +- [有序集合命令的实现方法](./docs/06.Redis/QA.md#有序集合命令的实现方法) +- redis.conf 配置 +- 慢查询 + +### 3、网络篇 + +- [计算机网络体系结构](./docs/01.网络.md#1-计算机网络体系结构) +- [UDP 的主要特点](./docs/01.网络.md#2-udp-的主要特点) +- [TCP 的主要特点](./docs/01.网络.md#3-tcp-的主要特点) +- [简述三报文握手建立 TCP 连接](./docs/01.网络.md#4-简述三报文握手建立-tcp-连接) +- [建立 TCP 连接为什么最后还要发送确认](./docs/01.网络.md#5-建立-tcp-连接为什么最后还要发送确认) +- [简述 TCP 连接的释放](./docs/01.网络.md#6-简述-tcp-连接的释放) +- [TIME-WAIT 是什么,为什么必须等待 2MLS](./docs/01.网络.md#7-time-wait-是什么为什么必须等待-2mls) +- [TCP 粘包问题](./docs/01.网络.md#8-tcp-粘包问题) +- [UDP、TCP 区别,适用场景](./docs/01.网络.md#9-udptcp-区别适用场景) +- [建立 socket 需要哪些步骤](./docs/01.网络.md#10-建立-socket-需要哪些步骤) +- [DNS 主要作用是什么](./docs/01.网络.md#11-dns-主要作用是什么) +- [HTTP 报文组成](./docs/01.网络.md#12-http-报文组成) +- [HTTP 状态码](./docs/01.网络.md#13-http-状态码) +- [常见的 HTTP 方法](./docs/01.网络.md#14-常见的-http-方法) +- [GET 与 POST 请求方式区别](./docs/01.网络.md#15-get-与-post-请求方式区别) +- [HTTP 优缺点](./docs/01.网络.md#16-http-优缺点) +- [HTTPS 通信原理](./docs/01.网络.md#17-https-通信原理) +- [HTTP 2.0](./docs/01.网络.md#18-http-20) +- [WebSocket](./docs/01.网络.md#19-websocket) +- [IPv6 与 IPv4 有什么变化](./docs/01.网络.md#20-ipv6-与-ipv4-有什么变化) +- [什么是心跳机制](./docs/01.网络.md#21-什么是心跳机制) +- [什么是长连接](./docs/01.网络.md#22-什么是长连接) + +### 4、数据结构与算法篇 + +- [概述](./docs/02.数据结构与算法.md#1-概述) +- [实现基础](./docs/02.数据结构与算法.md#2-实现基础) +- [线性结构](./docs/02.数据结构与算法.md#3-线性结构) +- [树](./docs/02.数据结构与算法.md#4-树) +- [散列查找](./docs/02.数据结构与算法.md#5-散列查找) +- [图](./docs/02.数据结构与算法.md#6-图) +- [排序](./docs/02.数据结构与算法.md#7-排序) +- [补充](./docs/02.数据结构与算法.md#8-补充) +- [经典算法题](./docs/02.数据结构与算法.md#9-经典算法题) + +### 5、设计模式篇 + +- [什么是设计模式](./docs/09.设计模式/QA.md#什么是设计模式) +- [如何理解框架](./docs/09.设计模式/QA.md#如何理解框架) +- [主要设计模式](./docs/09.设计模式/QA.md#主要设计模式) +- [怎样选择设计模式](./docs/09.设计模式/QA.md#怎样选择设计模式) +- [单例模式](./docs/09.设计模式/QA.md#单例模式) +- [抽象工厂模式](./docs/09.设计模式/QA.md#抽象工厂模式) +- [工厂方法模式](./docs/09.设计模式/QA.md#工厂方法模式) +- [适配器模式](./docs/09.设计模式/QA.md#适配器模式) +- [观察者模式](./docs/09.设计模式/QA.md#观察者模式) +- [策略模式](./docs/09.设计模式/QA.md#策略模式) +- [OOP 思想](./docs/09.设计模式/QA.md#oop-思想) +- [抽象类和接口](./docs/09.设计模式/QA.md#抽象类和接口) +- [控制反转](./docs/09.设计模式/QA.md#控制反转) +- [依赖注入](./docs/09.设计模式/QA.md#依赖注入) + +### 6、服务器篇 + +- [Linux 目录结构](./docs/07.Linux/QA.md#linux-目录结构) +- [Linux 基础](./docs/07.Linux/QA.md#linux-基础) +- [命令与文件查找](./docs/07.Linux/QA.md#命令与文件查找) +- [数据流重定向](./docs/07.Linux/QA.md#数据流重定向) +- [sed](./docs/07.Linux/QA.md#sed) +- [awk](./docs/07.Linux/QA.md#awk) +- [计划任务](./docs/07.Linux/QA.md#计划任务) +- [Vim](./docs/07.Linux/QA.md#vim) +- [负载查看](./docs/07.Linux/QA.md#负载查看) +- Linux 内存管理 +- [进程、线程、协程区别](./docs/07.Linux/QA.md#进程线程协程区别) +- 进程间通信与信号机制 + +### 7、架构篇 + +- [OAuth 2.0](./docs/10.架构/QA.md#oauth-20) +- [单点登录](./docs/10.架构/QA.md#单点登录) +- [REST](./docs/10.架构/QA.md#rest) +- [API 版本兼容](./docs/10.架构/QA.md#api-版本兼容) +- [JWT](./docs/10.架构/QA.md#jwt) +- [画出 PHP 业务架构图](./docs/10.架构/QA.md#画出-php-业务架构图) +- [LVS](./docs/10.架构/QA.md#lvs) +- [Ngnix](./docs/10.架构/QA.md#ngnix) +- 服务化 +- 微服务 +- 服务注册发现 +- [数据库读写分离](./docs/10.架构/QA.md#数据库读写分离) +- [数据库拆分](./docs/10.架构/QA.md#数据库拆分) +- [分布式事务](./docs/10.架构/QA.md#分布式事务) +- ID 生成器 +- [一致性哈希](./docs/10.架构/QA.md#一致性哈希) +- [Redis 集群](./docs/10.架构/QA.md#redis-集群) - 消息队列 -- 共享SESSION -- 下单后30分钟未支付取消订单 -- IP对应省市效率尽可能高 -- 详细描述输入地址到打开网页过程 -- 参考资料 +- 穿透、雪崩 +- 限流(木桶、令牌桶) +- 服务降级 +- 语言对比 + +### 8、安全篇 + +- [跨站脚本攻击(XSS)](./docs/08.安全/QA.md#跨站脚本攻击xss) +- [跨站点请求伪造(CSRF)](./docs/08.安全/QA.md#跨站点请求伪造csrf) +- [SQL 注入](./docs/08.安全/QA.md#sql-注入) +- [应用层拒绝服务攻击](./docs/08.安全/QA.md#应用层拒绝服务攻击) +- [PHP 安全](./docs/08.安全/QA.md#php-安全) +- [伪随机数和真随机数](./docs/08.安全/QA.md#伪随机数和真随机数) + +### 9、面试篇 + +### 10、web篇 + + +- [SEO 有哪些需要注意的](./docs/04.Web/QA.md#seo-有哪些需要注意的) +- [img 标签的 title 和 alt 有什么区别](./docs/04.Web/QA.md#img-标签的-title-和-alt-有什么区别) +- [CSS 选择器的分类](./docs/04.Web/QA.md#css-选择器的分类) +- [CSS sprite 是什么,有什么优缺点](./docs/04.Web/QA.md#css-sprite-是什么有什么优缺点) +- [display: none 与 visibility: hidden 的区别](./docs/04.Web/QA.md#display-none-与-visibility-hidden-的区别) +- [display: block 和 display: inline 的区别](./docs/04.Web/QA.md#display-block-和-display-inline-的区别) +- [CSS 文件、style 标签、行内 style 属性优先级](./docs/04.Web/QA.md#css-文件style-标签行内-style-属性优先级) +- [link 与 @import 的区别](./docs/04.Web/QA.md#link-与-import-的区别) +- [盒子模型](./docs/04.Web/QA.md#盒子模型) +- [容器包含若干浮动元素时如何清理(包含)浮动](./docs/04.Web/QA.md#容器包含若干浮动元素时如何清理包含浮动) +- [如何水平居中一个元素](./docs/04.Web/QA.md#如何水平居中一个元素) +- [如何竖直居中一个元素](./docs/04.Web/QA.md#如何竖直居中一个元素) +- [flex 与 CSS 盒子模型有什么区别](./docs/04.Web/QA.md#flex-与-css-盒子模型有什么区别) +- [Position 属性](./docs/04.Web/QA.md#position-属性) +- [PNG,GIF,JPG 的区别及如何选](./docs/04.Web/QA.md#pnggifjpg-的区别及如何选) +- [为什么把 JavaScript 文件放在 Html 底部](./docs/04.Web/QA.md#为什么把-javascript-文件放在-html-底部) +- [JavaScript 数据类型](./docs/04.Web/QA.md#javascript-数据类型) +- [JavaScript 操作 DOM 的方法有哪些](./docs/04.Web/QA.md#javascript-操作-dom-的方法有哪些) +- [JavaScript 字符串方法有哪些](./docs/04.Web/QA.md#javascript-字符串方法有哪些) +- [JavaScript 字符串截取方法有哪些?有什么区别](./docs/04.Web/QA.md#javascript-字符串截取方法有哪些有什么区别) +- [setTimeout 和 setInterval 的区别](./docs/04.Web/QA.md#settimeout-和-setinterval-的区别) +- [使用 new 操作符实例化一个对象的具体步骤](./docs/04.Web/QA.md#使用-new-操作符实例化一个对象的具体步骤) +- [如何实现 ajax 请求](./docs/04.Web/QA.md#如何实现-ajax-请求) +- [同源策略是什么](./docs/04.Web/QA.md#同源策略是什么) +- [如何解决跨域问题](./docs/04.Web/QA.md#如何解决跨域问题) +- [引起内存泄漏的操作有哪些](./docs/04.Web/QA.md#引起内存泄漏的操作有哪些) +- [闭包理解及应用](./docs/04.Web/QA.md#闭包理解及应用) +- [对 JavaScript 原型的理解](./docs/04.Web/QA.md#对-javascript-原型的理解) +- [对 JavaScript 模块化的理解](./docs/04.Web/QA.md#对-javascript-模块化的理解) +- [如何判断网页中图片加载成功或者失败](./docs/04.Web/QA.md#如何判断网页中图片加载成功或者失败) +- [如何实现懒加载](./docs/04.Web/QA.md#如何实现懒加载) +- [JSONP 原理](./docs/04.Web/QA.md#jsonp-原理) +- [Cookie 读写](./docs/04.Web/QA.md#cookie-读写) +- 从浏览器地址栏输入 URL 到显示页面的步骤 +- [Vue.js 双向绑定原理](./docs/04.Web/QA.md#vuejs-双向绑定原理) +- 如何进行网站性能优化 +- [渐进增强](./docs/04.Web/QA.md#渐进增强) + +## 为何要写这个 -线上故障 +从事软件开发,已经接近五个年头了,去年面试中,发现自己依然处于尴尬的位置。简单重复,缺乏挑战的工作,已经没有多大吸引力了,优秀的平台,面试缺屡次碰壁。人上年纪之后,思维敏感度、记忆力都明显有所下滑。 -- 客户端热更新失败 -- Redis实例used_memory达到80% -- 游戏任务完成了进度未更新 -- 测试服HTTP请求未响应 -- 游戏账号被盗 +程序开发不要被限制在语言层面,这是大家都懂的道理。但是作为一个 PHP 开发者,很多时候都是缠绕在业务的沟壑,理想和现实总是相差甚大。去年由于部门重组,本来将近十余人负责的项目,之后只剩两三人负责,各种坑只能靠人肉解决,深感无力。 -个人简历 +工作可能只是你的一部分,你必须有自己的能力定位。以前总觉得学什么,做什么都无所谓,需要学习的技术,花点时间快速学习就行,有新的技术出来,赶紧紧跟了解下。但这些年下来,发现自己却没有一样能够拿的出手的,甚至连一个像样的作品也没有。其实 PHP 的技术栈还是比较广的,在对整个技术栈有一定的掌握之后,可能还需要深挖几个自己喜欢的领域,否则在现今的就业市场里面,没有任何的竞争力。所以可以看到很多招聘者都在感慨,中高级工程师都去哪里了。 -自我介绍 +面试或者面试他人,无法逃避,那就选择面对。撰写《PHP 面试问答》,构建一个面试体系,而不必慌张的临时准备,时时刻刻都充分准备好,对自己负责,也对别人负责。 -离职原因 +结合实际 PHP 面试,系统的汇总面试中的各种各样的问题,尝试提供简洁准确的答案。如果你在 PHP 面试中遇到问题,欢迎提 Issues 交流。包含网络、数据结构与算法、PHP、Web、MySQL、Redis、Linux、安全、设计模式、架构、自我介绍、离职原因、职业规划、准备问题等部分。 -- 跳槽频繁 -- 这次换工作原因 +最后,祝愿大家在日后的求职中,都能拿到满意的 offer~~ -职业规划 +## 参考 -准备问题 +[术语对照表](./docs/术语对照表.md):顾名思义,帮助联想知识点 -- 工作挑战大不大? -- 项目开发是否写测试用例,项目上线先是否会进行压力测试 -- 业务前景如何? -- 技术氛围如何? -- 根据这次面试,对个人进行评价,帮助成长 -- 融资计划 -- 是否有加班费/调休,公司福利,社保公积金缴纳基数 +[参考资料](./docs/参考资料.md):站在巨人的肩膀上,你将能看的更远 -# 声明 +## 声明 -> 本资料仅供参考,不保证正确性 +本资料仅供参考,水平有限,难免存在纰漏错误之处 -> 作者:凌枫 Email:colinlets@gmail.com 链接:https://github.com/colinlet/PHP-Interview-QA +欢迎转载,转载请标明来源出处,谢谢~~ -# 参考 +作者:凌枫 Email:colinlets@gmail.com -- [这几天找工作遇到的面试题目,20 多家公司 100 多道题目](https://laravel-china.org/articles/9983/over-the-past-few-days-i-have-interviewed-100-topics-for-more-than-20-companies) -- [2016十家公司前端面试小记](http://www.cnblogs.com/xxcanghai/p/5205998.html) -- [3年PHPer的面试总结](https://juejin.im/post/59c8f4d55188257e8f03ab80) -- [八年phper的高级工程师面试之路](https://zhuanlan.zhihu.com/p/27493130) \ No newline at end of file +链接:https://github.com/colinlet/PHP-Interview-QA \ No newline at end of file diff --git a/assets/ip2long.png b/assets/ip2long.png deleted file mode 100644 index eaf06aa..0000000 Binary files a/assets/ip2long.png and /dev/null differ diff --git a/assets/tcp-01.png b/assets/tcp-01.png deleted file mode 100644 index 2f584d2..0000000 Binary files a/assets/tcp-01.png and /dev/null differ diff --git a/assets/tcp-02.png b/assets/tcp-02.png deleted file mode 100644 index e8e8f10..0000000 Binary files a/assets/tcp-02.png and /dev/null differ diff --git "a/docs/01.\347\275\221\347\273\234.md" "b/docs/01.\347\275\221\347\273\234.md" new file mode 100644 index 0000000..1240a51 --- /dev/null +++ "b/docs/01.\347\275\221\347\273\234.md" @@ -0,0 +1,229 @@ +# 问题与简答 + +## 网络篇 + +### 1. 计算机网络体系结构 + +![计算机网络体系结构](./assets/network-architecture-02.png) + +#### 各层作用 + +- 应用层:应用层协议定义的是应用进程间通信和交互的规则 +- 运输层:运输层的任务就是负责向`两台主机中进程之间的通信`提供`通用的数据传输`服务 +- 网络层:把运输层产生的报文段或用户数据报封装成`分组`或`包`进行传送 +- 数据链路层:将网络层交下来的 IP 数据报组装成帧,并在两个相邻结点间的链路上传送 +- 物理层:利用物理媒体以`比特`形式传送数据 + +拓展阅读 [《计算机网络体系结构》](./01.网络/01.计算机网络体系结构.md) + +### 2. UDP 的主要特点 + +- UDP 是`无连接的`,即发送数据之前不需要建立连接(发送数据结束时也没有连接可释放),减少了开销和发送数据之前的时延 +- UDP 使用`尽最大努力交付`,即不保证可靠交付,主机不需要维持复杂的连接状态表 +- UDP 是`面向报文`的,发送方的 UDP 对应用程序交下来的报文,在添加首部后就向下交付 IP 层。UDP 对应用层交下来的报文,既不合并,也不拆分,而是`保留这些报文的边界` +- UDP `没有拥塞控制`,网络出现的拥塞不会使源主机的发送速率降低。这对某些实时应用是很重要的 +- UDP 支持一对一、一对多、多对一和多对多的交互通信 +- UDP 的`首部开销小`,只有8个字节,比 TCP 的20个字节的首部要短 + +拓展阅读 [《用户数据报协议 UDP》](./01.网络/02.用户数据报协议UDP.md) + +### 3. TCP 的主要特点 + +- TCP 是`面向连接的运输层协议`。应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接 +- 每一条 TCP 连接只能有两个`端点`,每一条 TCP 连接只能是`点对点`的(一对一) +- TCP 提供`可靠交付`的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达 +- TCP 提供`全双工通信`。TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据 +- `面向字节流`。TCP 中的“流”指的是`流入到进程或从进程流出的字节序列` + +拓展阅读 [《传输控制协议 TCP》](./01.网络/03.传输控制协议TCP.md) + +### 4. 简述三报文握手建立 TCP 连接 + +- 服务器进程先创建传输控制块 TCB,并处于监听状态,等待客户端的连接请求;此时状态为LISTEN +- 客户端创建传输控制块 TCB,并向服务器发出连接请求报文段SYN,此时状态为SYN-SEND +- 服务器收到连接请求报文段后,如同意建立连接,则发送确认报文段ACK,此时状态为SYN-RECV,并且客户端链接进入半链接队列,系统参数/proc/sys/net/ipv4/tcp_max_syn_backlog +- 客户端进程收到服务器的确认报文段后,立即回复确认报文段ACK,并进入已建立连接状态,此时状态为ESTABLISHED +- 服务器收到确认报文段之后,也进入已建立连接状态,此时状态为ESTABLISHED,并且客户端链接进入全连接队列,系统参数/proc/sys/net/core/somaxconn + +> 传输控制块 TCB(Transmission Control Block)存储了每一个连接中的一些重要信息 + +### 5. 建立 TCP 连接为什么最后还要发送确认 + +这主要是为了防止已失效的连接请求报文段突然又传到了 TCP 服务器,避免产生错误 +简述为了保证数据的正确性和顺序 + +### 6. 简述 TCP 连接的释放 + +- 客户端应用进程发出连接释放报文段,并停止再发送数据,进入 FIN-WAIT-1(终止等待1)状态,等待服务器确认 +- 服务器收到连接释放报文段后即发出确认,进入 CLOSE-WAIT(关闭等待)状态,服务器若发送数据,客户端扔要接收 +- 客户端收到来自服务器的确认后,进入 FIN-WAIT-2(终止等待2)状态,等待服务器发出连接释放报文段 +- 服务器没有要发送的数据,发出连接释放报文段,进入 LAST-ACK(最后确认)状态,等待客户端确认 +- 客户端收到连接释放报文段后,发出确认,进入 TIME-WAIT(时间等待)状态,经过时间等待计时器设置的时间 2MSL 后,进入 CLOSED(关闭) 状态 +- 服务器收到客户端报文段后,进入 CLOSED 状态 + +### 7. TIME-WAIT 是什么,为什么必须等待 2MLS + +TIME-WAIT 是一种 TCP 状态。等待 2MLS 可以保证客户端最后一个报文段能够到达服务器,如果未到达,服务器则会超时重传连接释放报文段,使得客户端、服务器都可以正常进入到 CLOSE(关闭) 状态 + +### 8. TCP 粘包问题 + +#### 粘包问题 + +在 TCP 这种字节流协议上做`应用层分包`是网络编程的基本需求。分包指的是在发生一个消息(message)或一帧(frame)数据时,通过一定的处理,让接收方能从字节流中识别并截取(还原)出一个个消息。因此,“粘包问题”是个伪命题 + +#### 长连接分包 + +- 消息长度固定 +- 使用特殊的字符或字符串作为消息的边界,例如 HTTP 协议的 headers 以“\r\n”为字段的分隔符 +- 在每条消息的头部加一个长度字段,这恐怕是最常见的做法 +- 利用消息本身的格式来分包,例如 XML 格式的消息中 ``...`` 的配对,或者 JSON 格式中的 { ... } 的配对。解析这种消息格式通常会用到状态机(state machine) + +拓展阅读 [《TCP粘包拆包》](./01.网络/04.TCP粘包拆包.md) + +### 9. UDP、TCP 区别,适用场景 + +|对比项|UDP|TCP| +|-|-|-| +|连接性|无连接|面向连接| +|可靠性|不可靠|可靠| +|报文|面向报文-数据报模式|面向字节流-流模式| +|双工性|一对一、一对多、多对一、多对多|全双工| +|流量控制|无|有(滑动窗口)| +|拥塞控制|无|有(慢开始、拥塞避免、快重传、快恢复)| +|传输速度|快|慢| +|资源要求|较少|较多| +|首部开销|8字节|20字节| +|数据顺序|不保证|保证| + +#### UDP 适用场景 + +面向数据报方式、网络数据大多为短消息、拥有大量 Client、对数据安全性无特殊要求、网络负担非常重,但对响应速度要求高 + +#### TCP 适用场景 + +- 文件传输(FTP HTTP 对数据准确性要求较高,速度可以相对慢) +- 发送或接收邮件(POP IMAP SMTP 对数据准确性要求高,非紧急应用) +- 远程登录(telnet SSH 对数据准确性有要求,有连接的概念) + +### 10. 建立 socket 需要哪些步骤 + +- 创建 socket +- 绑定 socket 到指定地址和端口 +- 开始监听连接 +- 读取客户端输入 +- 关闭 socket + +### 11. DNS 主要作用是什么 + +计算机既可以被赋予 IP 地址,也可以被赋予主机名和域名。用户通常使用主机名或域名来访问对方的计算机,而不是直接通过 IP 地址访问 + +但要让计算机去理解名称,相对而言就变得困难,因为计算机更擅长处理一长串数字 + +为了解决上述问题,DNS 服务应运而生。DNS 协议提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务 + +### 12. HTTP 报文组成 + +HTTP 报文是由简单字符串组成,HTTP 报文都是纯文本,不是二进制代码,可以很方便地对其进行读写 + +![HTTP报文](./assets/network-http-message.png) + +从客户端发往服务器的 HTTP 报文称为`请求报文`(request message)。从服务器发往客户端的报文称为`响应报文`(response message)。HTTP 请求和响应报文的格式很类似 + +HTTP 报文组成部分 + +- 起始行:报文的第一行就是起始行,在请求报文中用来说明要做些什么,在响应报文中说明出现了什么情况 +- 首部字段:起始行后面有零个或多个首部字段。每个首部字段都包含一个名字和一个值 +- 主体:空行之后就是可选的报文主体了,其中包含了所有类型的数据 + +### 13. HTTP 状态码 + +> HTTP 状态码用来告诉客户端,发生了什么事情,状态码位于响应的起始行中 + +#### 状态码分类 + +|状态码|整体范围|已定义范围|分类| +|-|-|-|-| +|1XX|100~199|100~101|信息提示| +|2XX|200~299|200~206|成功| +|3XX|300~399|300~305|重定向| +|4XX|400~499|400~415|客户端错误| +|5XX|500~599|500~505|服务器错误| + +#### 常见状态码 + +|状态码|原因短语|含义|考察概率| +|-|-|-|-| +|200|OK|请求没有问题|***| +|206|Partial Content|部分或 Range(范围) 请求|*| +|301|Moved Permanently|在请求的链接被移除时使用|**| +|302|Found|在请求临时的链接使用|**| +|304|Not Modified|资源未被修改可使用旧资源|**| +|307|Temporary Redirect|在请求临时的链接使用|**| +|400|Bad Request|告知客户端发送了错误请求|***| +|403|Forbidden|请求被服务器拒绝|***| +|404|Not Found|无法找到所请求的 URL|***| +|413|Request entiry too large|请求实体过大|*| +|500|Internal Server Error|服务器遇到错误|***| +|502|Bad Gateway|代理或网关错误(无法连接到其父网关)|***| +|503|Service Unavailable|无法为请求提供服务|***| +|504|Gateway Timeout|代理或网关超时(等待另一服务器响应超时)|***| + +拓展阅读 [《HTTP状态码》](./01.网络/05.HTTP状态码.md) + +拓展阅读 [《5xx系列错误》](../03.PHP/QA.md#502504-错误产生原因及解决方式) + +### 14. 常见的 HTTP 方法 + +![HTTP方法](./assets/network-http-method.png) + +拓展阅读 [《HTTP方法详解》](./01.网络/06.HTTP方法详解.md) + +### 15. GET 与 POST 请求方式区别 + +|GET|POST| +|-|-| +|后退按钮/刷新无害|数据会被重新提交| +|数据长度限制/URL长度2048字符|长度无限制(协议不限制;实际上受nginx和PHP的限制,php还会限制post报文变量的个数)| +|数据可见/安全性差|不可见/更安全| +|可以被缓存|不可以被缓存| +|书签可收藏|书签不可收藏| +|产生一个TCP数据包| 产生两个TCP数据包 | + +### 16. HTTP 优缺点 + +基于应用级的接口,使用方便 + +传输速度慢,数据包大;如实现实时交互,服务器性能压力大;数据传输安全性差 + +### 17. HTTPS 通信原理 + +![HTTPS通信原理](./assets/network-https.png) + +拓展阅读 [《HTTPS细节介绍》](./01.网络/07.HTTPS细节介绍.md) + +### 18. HTTP 2.0 + +多路复用、客户端拉拽/服务器推送、流量控制、WebSocket + +### 19. WebSocket + +WebSocket 是一种通信协议,定义了一个全双工通信信道,仅通过 Web 上的一个 Socket 即可进行通信 + +#### 主要特点 + +- 推送功能:支持由服务器向客户端推送数据的推送功能 +- 减少通信量:只要建立起 WebSocket 连接,就希望一直保持连接状态 + +![websocket](./assets/network-websocket.png) + +### 20. IPv6 与 IPv4 有什么变化 + +更大的地址空间、扩展的地址层次结构、灵活的首部格式、改进的选项、允许协议继续扩充、支持资源的预分配 + +### 21. 什么是心跳机制 + +心跳机制是定时发送一个自定义的结构体(心跳包),让对方知道自己还活着,以确保连接的有效性的机制 + +### 22. 什么是长连接 + +长连接,指在一个连接上可以连续发送多个数据包,在连接保持期间,如果没有数据包发送,需要双方发链路检测包 diff --git "a/docs/01.\347\275\221\347\273\234/01.\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\275\223\347\263\273\347\273\223\346\236\204.md" "b/docs/01.\347\275\221\347\273\234/01.\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\275\223\347\263\273\347\273\223\346\236\204.md" new file mode 100755 index 0000000..1f18b11 --- /dev/null +++ "b/docs/01.\347\275\221\347\273\234/01.\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\275\223\347\263\273\347\273\223\346\236\204.md" @@ -0,0 +1,125 @@ +# 计算机网络体系结构 + +在计算机网络的基本概念中,分层次的体系结构是最基本的 + +## 计算机网络体系结构的形成 + +### 分层 + +相互通信的两个计算机系统必须高度协调工作才行,而这种“协调”是相当复杂的。为了设计这样复杂的计算机网络,最初提出了分层的方法。“`分层`”可将庞大而复杂的问题,转化为若干较小的局部问题,而这些较小的局部问题比较易于研究和处理 + +### 国际标准 + +全球经济的发展使得不同网络体系结构的用户迫切要求能够互相交换信息,国际标准化组织 ISO 提出了 OSI。只要遵循 OSI 标准,一个系统就可以和位于世界上任何地方的、也遵循这同一标准的其他任何系统进行通信 + +现今规则最大的、覆盖全球的、基于 TCP/IP 的互联网并未使用 OSI 标准。在20世纪90年代初期,虽然整套的 OSI 国际标准已制定出来,但基于 TCP/IP 的互联网已抢先在全球相当大的范围成功地运行了,而同时却几乎找不到有厂家生产出符合 OSI 标准的商业产品。OSI 只获得了一些理论研究的成果,市场化方面则彻底失败了 + +TCP/IP 常被称为是`事实上的国际标准` + +## 协议与划分层次 + +### 网络协议 + +在计算机网络中要做到有条不紊地交换数据,就必须准守一些事先约定好的规则。这些规则明确了所交换的数据的格式以及有关的同步问题 + +为进行网络中的数据交换而建立的规则、标准或约定称为`网络协议`。网络协议也可简称为`协议` + +**协议组成要素** + +- `语法`,即数据与控制信息的结构或格式 +- `语义`,即需要发出何种控制信息,完成何种动作以及做出何种响应 +- `同步`,即事件实现顺序的详细说明 + +协议通常有两种不同的形式。一种是使用便于人来阅读和理解的文字描述。另一种是使用让计算机能够理解的程序代码。这两种不同形式的协议都必须能够对网络上的信息交换过程做出精确的解释 + +### 划分层次 + +对于非常复杂的计算机网络协议,其结构应该是层次式的 + +![网络体系结构-图1](./assets/network-architecture-01.png) + +**分层可以带来很多好处** + +- `各层之间是独立的`。某一层并不需要知道它的下一层是如何实现的,而仅仅需要知道该层通过层间接口(即界面)所提供的服务 +- `灵活性好`。当任何一层发送变化时(例如由于技术的变化),只要层间接口关系保持不变,则在这层以上或以下各层均不受影响 +- `结构上可分割开`。各层都可以采用最合适的技术来实现 +- `易于实现和维护`。这种结构使得实现和调试一个庞大而又复杂的系统变得易于处理,因为整个的系统已被分解为若干个相对独立的子系统 +- `能促进标准化工作`。因为每一层的功能及其所提供的服务都已有了精确的说明 + +**各层所要完成的功能** + +- `差错控制` 使相应层次对等方的通信更加可靠 +- `流量控制` 发送端的发送速率必须使接收端来得及接收,不要太快 +- `分段和重装` 发送端将要发送的数据块划分为更小的单位,在接收端将其还原 +- `复用和分用` 发送端几个高层会话复用一条逻辑连接,数据传送结束后释放连接 +- `连接建立和释放` 交换数据前先建立一条逻辑连接,数据传送结束后释放连接 + +**分层缺点** + +有些功能会在不同的层次中重复出现,因而产生了额外开销 + +**体系结构** + +计算机网络的各层及其协议的集合就是网络的`体系结构`。计算机网络的体系结构就是这个计算机网络及其构件所应完成的功能的精确定义 + +体系结构是抽象的,而实现则是具体的,是真正在运行的计算机硬件和软件 + +## 具有五层协议的体系结构 + +### 五层协议 + +OSI 的七层协议体系结构的概念清楚,理论也较完整,但它既复杂又不实用。TCP/IP 体系结构则不同,但它现在却得到了非常广泛的应用。TCP/IP 是一个四层的体系结构。在学习计算机网络的原理时往往采用折中的办法,即综合 OSI 和 TCP/IP 的优点,采用一种只有五层协议的体系结构,这样既简洁又能将概念阐述清楚 + +![网络体系结构-图2](./assets/network-architecture-02.png) + +### 各层作用 + +- 应用层:应用层协议定义的是应用进程间通信和交互的规则 +- 运输层:运输层的任务就是负责向`两台主机中进程之间的通信`提供`通用的数据传输`服务 +- 网络层:把运输层产生的报文段或用户数据报封装成`分组`或`包`进行传送 +- 数据链路层:将网络层交下来的 IP 数据报组装成帧,并在两个相邻结点间的链路上传送 +- 物理层:利用物理媒体以`比特`形式传送数据 + +### 小结 + +- 把应用层交互的数据单元称为`报文` +- 运输层主要协议:传输控制协议 TCP、用户数据报协议 UDP +- 在 TCP/IP 体系中,由于网络层使用 IP 协议,因此分组也叫 `IP 数据报`,或简称为数据报 + +![网络体系结构-图3](./assets/network-architecture-03.png) + +## 实体、协议、服务和服务访问点 + +当研究开放系统中的信息交换时,往往使用`实体`(entity)这一较为抽象的名词表示`任何可发送或接受信息的硬件或软件进程` + +协议是控制两个对等实体(或多个实体)进行通信的规则的集合 + +在协议的控制下,两个对等实体间的通信使得本层能够向上一层提供服务。要实现本层协议,还需要使用下面一层所提供的服务 + +协议是“水平的”,即协议是控制对等实体之间通信的规则。但服务是“垂直的”,即服务是由下层向上层通过层间接口提供的 + +![网络体系结构-图4](./assets/network-architecture-04.png) + +### 计算机网络协议特点 + +协议必须把所有不利的条件事先都估计到,而不能假定一切都是正常的和非常理想的 + +非常仔细地检查这个协议能否应付各种异常情况 + +## TCP/IP 的体系结构 + +TCP/IP 的体系结构比较简单,只有四层 + +![网络体系结构-图5](./assets/network-architecture-05.png) + +应当指出,技术的发展并不是遵循严格的 OSI 分层概念。实际上现在的互联网使用的 TCP/IP 体系结构有时已经演变成为下图所示的那样,即某些应用程序可以直接使用 IP 层,或甚至直接使用最下面的网络接口层 + +![网络体系结构-图6](./assets/network-architecture-06.png) + +还有一种方法,就是分层次画出具体的协议表示 TCP/IP 协议族,它的特点是上下两头大而中间小:应用层和网络接口层都有多种协议,而中间的 IP 层很小,上层的各种协议都向下汇聚到一个 IP 协议中 + +TCP/IP 协议可以为各式各样的应用提供服务,同时 TCP/IP 协议也允许 IP 协议在各式各样的网络构成的互联网上运行。IP 协议在互联网中充当着核心的作用 + +![网络体系结构-图7](./assets/network-architecture-07.png) + +**《计算机网络体系结构》 原文链接:[https://blog.maplemark.cn/2019/04/计算机网络体系结构.html](https://blog.maplemark.cn/2019/04/计算机网络体系结构.html)** \ No newline at end of file diff --git "a/docs/01.\347\275\221\347\273\234/02.\347\224\250\346\210\267\346\225\260\346\215\256\346\212\245\345\215\217\350\256\256UDP.md" "b/docs/01.\347\275\221\347\273\234/02.\347\224\250\346\210\267\346\225\260\346\215\256\346\212\245\345\215\217\350\256\256UDP.md" new file mode 100755 index 0000000..77d8e6f --- /dev/null +++ "b/docs/01.\347\275\221\347\273\234/02.\347\224\250\346\210\267\346\225\260\346\215\256\346\212\245\345\215\217\350\256\256UDP.md" @@ -0,0 +1,49 @@ +# 用户数据报协议 UDP + +## UDP 概述 + +用户数据报协议 UDP 只在 IP 的数据报服务之上增加了很少一点的功能,这就是复用和分用的功能以及查错检测的功能 + +### UDP 的主要特点 + +1. UDP 是`无连接的`,即发送数据之前不需要建立连接(发送数据结束时也没有连接可释放),减少了开销和发送数据之前的时延 +2. UDP 使用`尽最大努力交付`,即不保证可靠交付,主机不需要维持复杂的连接状态表 +3. UDP 是`面向报文`的,发送方的 UDP 对应用程序交下来的报文,在添加首部后就向下交付 IP 层。UDP 对应用层交下来的报文,既不合并,也不拆分,而是`保留这些报文的边界` + +![UDP协议-图1](./assets/network-udp-01.png) + +4. UDP `没有拥塞控制`,网络出现的拥塞不会使源主机的发送速率降低。这对某些实时应用是很重要的 +5. UDP 支持一对一、一对多、多对一和多对多的交互通信 +6. UDP 的`首部开销小`,只有8个字节,比 TCP 的20个字节的首部要短 + +### 存在问题 + +1. 某些实时应用需要使用没有拥塞控制的 UDP,但很多的源主机同时都向网络发送高速率的实时视频流时,网络就有可能发生拥塞,导致大家都无法正常接收。 +2. 还有一些使用 UDP 的实时应用,需要对 UDP 的不可靠传输进行适当的改进,以减少数据的丢失。应用进程可以在不影响应用的实时性的前提下,增加一些提高可靠性的措施,如采用前向纠错或重传已丢失的报文 + +## UDP 的首部格式 + +用户数据报 UDP 有两个字段:`数据字段`和`首部字段`。首部字段很简单,只有8个字节,由四个字段组成,每个字段都是两个字节 + +### 首部字段 + +- `源端口` 源端口号。在需要对方回信时。不需要时可用全0 +- `目的端口` 目的端口号。这在终点交付报文时必须使用 +- `长度` UDP 用户数据报的长度,其最小值是8(仅有首部) +- `检验和` 检测 UDP 用户数据报在传输中是否有错。有错就丢弃 + +![UDP协议-图2](./assets/network-udp-02.png) + +### 端口分用 + +当运输层从 IP 层收到 UDP 数据报时,就根据首部中的目的端口,把 UDP 数据报通过相应的端口,上交最后的终点——应用进程 + +![UDP协议-图3](./assets/network-udp-03.png) + +如果接受方 UDP 发现收到的报文中的目的端口号不正确(即不存在对应于该端口号的应用程序),就丢弃该报文,并由网际控制报文协议 ICMP 发送“端口不可达”差错报文给发送方 + +### 伪首部 + +UDP 用户数据报首部中检验和的计算方法有些特殊。在计算检验和时,要在 UDP 用户数据报之前增加 12 个字节的`伪首部`。所谓“伪首部”是因为这种伪首部并不是 UDP 用户数据报真正的首部。只是在计算检验和时,临时添加在 UDP 用户数据报前面,得到一个临时的 UDP 用户数据报。检验和就是按照这个临时用户数据报来计算的。伪首部既不向下传也不向上递交,而仅仅是为了计算检验和 + +**《用户数据报协议UDP》 原文链接:[https://blog.maplemark.cn/2019/04/用户数据报协议udp.html](https://blog.maplemark.cn/2019/04/用户数据报协议udp.html)** \ No newline at end of file diff --git "a/docs/01.\347\275\221\347\273\234/03.\344\274\240\350\276\223\346\216\247\345\210\266\345\215\217\350\256\256TCP.md" "b/docs/01.\347\275\221\347\273\234/03.\344\274\240\350\276\223\346\216\247\345\210\266\345\215\217\350\256\256TCP.md" new file mode 100755 index 0000000..8d828c9 --- /dev/null +++ "b/docs/01.\347\275\221\347\273\234/03.\344\274\240\350\276\223\346\216\247\345\210\266\345\215\217\350\256\256TCP.md" @@ -0,0 +1,381 @@ +# 传输控制协议 TCP + +## 传输控制协议 TCP 概述 + +### TCP 最主要的特点 + +- TCP 是`面向连接的运输层协议`。应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接 +- 每一条 TCP 连接只能有两个`端点`,每一条 TCP 连接只能是`点对点`的(一对一) +- TCP 提供`可靠交付`的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达 +- TCP 提供`全双工通信`。TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据 +- `面向字节流`。TCP 中的“流”指的是`流入到进程或从进程流出的字节序列` + +### 面向字节流 + +“面向字节流”的含义是:虽然应用程序和 TCP 的交互式一次一个数据块(大小不等),但 TCP 把应用程序交下来的数据仅仅看成是一连串的`无结构的字节流`。TCP 并不知道所传送的字节流的含义 + +TCP 不保证接收方应用程序所收到的数据块和发送方应用程序所发出的数据块具有对应大小的关系 + +> 例如,发送方应用程序交给发送方的 TCP 共10个数据块,但接收方的 TCP 可能只用了4个数据块就把收到的字节流交付上层的应用程序 + +接收方应用程序收到的字节流必须和发送方应用程序发出的字节流完全一样。接收方的应用程序必须有能力识别收到的字节流,把它还原成有意义的应用层数据 + +![TCP协议-图1](./assets/network-tcp-01.png) + +TCP 和 UDP 在发送报文时采用的方式完全不同。TCP 并不关心应用进程一次把多长的报文发送到 TCP 的缓存中,而是根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少个字节(UDP 发送的报文长度是应用进程给出的)。如果应用进程传送到 TCP 缓存的数据块太长,TCP 就可以把它划分短一些再传送。如果应用进程一次只发来一个字节,TCP 也可以等待积累有足够多的字节后再构成报文段发送出去 + +### TCP 的连接 + +TCP 把`连接`作为`最基本的抽象`。TCP 的许多特性都与 TCP 是面向连接的这个基本特性有关 + +TCP 连接的端点叫做`套接字(socket)或插口`,根据 RFC 793 的定义:端口号拼接到(concatenated with) IP 地址即构成了套接字 + +> 套接字 socket = (IP 地址:端口号) + +`每一条 TCP 连接唯一地被通信两端的两个端点(即两个套接字)所确定` + +> TCP 连接 ::= {socket1, socket2} = {(IP1: port1), (IP2: port2)} + +TCP 连接就是由协议软件所提供的一种抽象。`TCP 连接的端口是个很抽象的套接字`,即( `IP地址`: `端口号`)。同一个 IP 地址可以有多个不同的 TCP 连接,而同一个端口号也可以出现在多个不同的 TCP 连接中 + +### 易混淆的 socket + +同一个名词 socket 却可表示多种不同的意思,以下 socket 的意思跟本文中所引用的 RFC 793 定义的 socket(指端口号拼接到 IP 地址)不同 + +- 允许应用程序访问连网协议的`应用编程接口 API(Application Programming Interface)`,即运输层和应用层之间的接口,称为 socket API,并简称为 socket +- 在 socket API 中使用的一个`函数名`也叫做 socket +- 调用 socket 函数的`端点`称为 socket,如“创建一个数据报 socket” +- 调用 socket 函数时,其`返回值`称为 socket 描述符,可简称为 socket +- 在操作系统内核中连网协议的 Berkeley 实现,称为 socket `实现` + +## 可靠传输的工作原理 + +### 理想的传输条件 + +理想的传输条件有以下两个特点 + +- 传输信道不产生差错 +- 不管发送方以多快的速度发送数据,接收方总是来得及处理收到的数据 + +实际的网络不具备以上两个理想条件。需要使用一些可靠的传输协议,当出现差错时让发送方重传出现差错的数据,同时在接收方来不及处理收到的数据时,及时告诉发送方适当减低发送数据的速度。这样,不可靠的传输信道就能够实现可靠传输了 + +### 停止等待协议 + +全双工通信的双方既是发送方也是接收方。把传送的数据单元都称为分组。“停止等待”就是每发完一个分组就停止发送,等待对方的确认。在收到确认后再发送下一个分组 + +#### 无差错情况 + +![TCP协议-图2](./assets/network-tcp-02.png) + +#### 出现差错 + +只要超过一段时间没有收到确认,就认为刚才发送的分组丢失了,因而重传前面发送过的分组。这就叫做`超时重传`。要实现超时重传,就要在每发送完一个分组时设置一个`超时计时器` + +- 发送完一个分组后,`必须暂时保留已发送的分组的副本`(在发生超时重传时使用)。只有在收到相应的确认后才能清除暂时保留的分组副本 +- 分组和确认分组都必须进行`编号`。这样才能明确是哪一个发送出去的分组收到了确认,而哪一个分组还没有收到确认 +- 超时计时器的重传时间`应当比数据在分组传输的平均往返时间更长一些` + +#### 确认丢失和确认迟到 + +![TCP协议-图3](./assets/network-tcp-03.png) + +使用上述的确认和重传机制,我们就可以`在不可靠的传输网络上实现可靠的通信` + +像上述的这种可靠传输协议常称为`自动重传请求 ARQ(Automatic Repeat reQuest)`。重传的请求是自动进行的。接收方不需要请求发送方重传某个出错的分组 + +#### 信道利用率 + +停止等待协议的优点是简单,但缺点是信道利用率太低 + +![TCP协议-图4](./assets/network-tcp-04.png) + +为了提高传输效率,发送方可以不使用低效率的停止等待协议,而是采用`流水线传输`。流水线传输就是发送方可连续发送多个分组,不必每发完一个分组就停顿下来等待对方的确认。这样可使信道上一直有数据不间断地在传送。这种传输方式可以获得很高的信道利用率 + +![TCP协议-图5](./assets/network-tcp-05.png) + +### 连续 ARQ 协议 + +位于发送窗口内的5个分组都可以连续发送出去,而不需要等待对方的确认。可以提高信道利用率 + +![TCP协议-图6](./assets/network-tcp-06.png) + +接收方一般都是采用`累积确认`的方式。接收方不需要对收到的分组逐个发送确认,而是在收到几个分组后,`对按序到达的最后一个分组发送确认` + +积累确认有优点也有缺点。优点是:容易实现,即使确认丢失也不必重传。缺点是不能向发送方反映出接收方已经正确收到的所有分组的信息 + +## TCP 报文段的首部格式 + +TCP 虽然是面向字节流的,但 TCP 传送的数据单元却是报文段。一个 TCP 报文段分为首部和数据两部分。TCP 报文段首部的前20个字节是固定的,后面有4n字节是根据需要而增加的选项(n是整数)。因此 TCP 首部的最小长度是20字节 + +![TCP协议-图7](./assets/network-tcp-07.png) + +### 首部字段 + +- `源端口`和`目的端口` 各占2个字节,分别写入源端口号和目的端口号 +- `序号` 占4字节。序号范围是[0, 232-1],共232(即4 294 967 296)个序号。序号增加到232-1后,下一个序号就又回到0。在一个 TCP 连接中传送的字节流中的`每一个字节都按顺序编号` +- `确认号` 占4字节,是`期望收到对方下一个报文段的第一个数据字节的序号` +- `数据偏移` 占4字节,它指出 TCP 报文段的数据起始处距离 TCP 报文段的起始处有多远。这个字段实际上是指出 TCP 报文段的首部长度 +- `保留` 占6位,保留为今后使用,但目前应置为0 + +下面有6个`控制位`,用来说明本报文段的性质 + +- `紧急 URG(URGent)` 当 URG=1 时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据),而不是按原先的排队顺序来传送 +- `确认 ACK(ACKnowledgment)` 仅当 ACK=1 时确认号字段才有效。当 ACK=0 时,确认号无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置1 +- `推送 PSH(Push)` 当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能够收到对方的响应 +- `复位 RST(ReSeT)` 当 RST=1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接 +- `同步 SYN(SYNnchronization)` 在连接建立时用来同步序号。当 SYN=1 而 ACK=0 时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使 SYN=1 和 ACK=1 +- `终止 FIN(FINis)` 用来释放一个连接。当 FIN=1 时,表明此报文段的发送发的数据已发送完毕,并要求释放运输连接 + +- `窗口` 占2字节。窗口值是[0, 216-1]之间的整数。窗口值作为接收方让发送方设置其发送窗口的依旧 +- `检验和` 占2字节。检验和字段检验的范围包括首部和数据这两部分 +- `紧急指针` 占2字节。紧急指针仅在 URG=1 时才有意义,它指出本报文段中的紧急数据的字节数 +- `选项` 长度可变,最长可达40字节 + +## TCP 可靠传输的实现 + +### 以字节为单位的滑动窗口 + +#### 发送窗口构造 + +TCP 的滑动窗口是以字节为单位的。假定 A 收到了 B `发来`的确认报文段,其中窗口是20字节,而确认号是31(这表明 B 期望收到的下一个序号是31,而序号30为止的数据已经收到了)。根据这两个数据,A 就构造出自己的发送窗口 + +![TCP协议-图8](./assets/network-tcp-08.png) + +发送窗口标识:在没有收到 B 的确认的情况下,A 可以连续把窗口内的数据都发送出去。凡是已经发送出去的数据,在未收到确认之前都必须暂时保留,以便在超时重传时使用 + +#### 发送窗口变化 + +发送窗口的位置由窗口前沿和后沿的位置共同确定。发送窗口后沿的变化情况有两种,即不动(没有收到新的确认)和前移(收到了新的确认)。发送窗口后沿不可能向后移动,因为不能撤销已收到的确认 + +发送窗口前沿通常是不断向前移动,但也有可能不动。这对应于两种情况: + +- 一是没有收到新的确认,对应通知的窗口大小也不变 +- 二是收到了新的窗口单对方通知的窗口缩小了,使得发送窗口前沿正好不动 + +发送窗口前沿也有可能`向后收缩`。这发生在对方通知的窗口缩小了。但 TCP 的标准`强烈不赞成这样做`。因为很可能发送方在收到这个通知以前已经发送了窗口中的许多数据,现在又要收缩窗口,不让发送这些数据,这样就会产生一些错误 + +![TCP协议-图9](./assets/network-tcp-09.png) + +要描述一个发送窗口的状态需要三个指针:P1,P2,P3。指针都指向字节的序号。这三个指针指向的几个部分的意义如下: + +- 小于 P1 的是已发送并已收到确认的部分,而大于 P3 的是不允许发送的部分 +- P3 - P1 = `A 的发送窗口` +- P2 - P1 已发送但尚未收到确认的字节数 +- P3 - P2 允许发送但当前尚未发送的字节数(又称为`可用窗口`或`有效窗口`) + +B 的接收窗口大小是20。在接收窗口外面,到30号为止的数据是已经发送过确认,并且已经交付主机了。因此在 B 可以不再保留这些数据。接收窗口内的序号(31\~50)是允许接收的。在上图中,B 收到了序号为32和33的数据。这些数据没有按序到达,因为序号为31的数据没有收到(也许丢失了,也许滞留在网络中的某处)。请注意,B 只能对按序收到的数据中的最高序号给出确认,因此 B 发送的确认报文段中的确认号仍然是31(即期望收到的序号),而不是32或33 + +现在假定 B 收到了序号为31的数据,并把序号为31\~33的数据交付主机,然后 B 删除这些数据。接着把接收窗口向前移动3个序号,同时给 A 发送确认,其中窗口值仍为20,但确认号是34。这表明 B 已经收到了到序号33为止的数据。B 还收到了序号为37,38和40的数据,但这些都没有按序到达,只能先暂存在接收窗口中。A 收到 B 的确认后,就可以把发送窗口向前滑动3个序号,但指针 P2 不动。现在 A 的可用窗口增大了,可发送的序号范围是42\~53 + +![TCP协议-图10](./assets/network-tcp-10.png) + +A 在继续发送完序号42\~53的数据后,指针 P2 向前移动和 P3 重合。发送窗口内的序号都已用完,但还没有再收到确认。由于 A 的发送窗口已满,可用窗口已减小到零,因此必须停止发送。发送窗口内所有的数据都已正确到达 B,B 也早已发出了确认。但所有这些确认都滞留在网络中。在没有收到 B 的确认时,A 不能猜测:”或许 B 收到了吧!“为了保证可靠传输,A 只能认为 B 还没有收到这些数据。于是,A 在经过一段时间后(由超时计时器控制)就重传这部分数据,重新设置超时计时器,直到收到 B 的确认为止。如果 A 收到确认号落在发送窗口内,那么 A 就可以发送窗口继续向前滑动,并发送新的数据 + +![TCP协议-图11](./assets/network-tcp-11.png) + +#### 缓存和窗口 + +发送方维持的发送缓存和发送窗口,以及接收方维持的接收缓存和接收窗口 + +![TCP协议-图12](./assets/network-tcp-12.png) + +发送缓存用来暂时存放: + +- 发送应用程序传送给对方 TCP 准备发送的数据 +- TCP 已发送出但尚未收到确认的数据 + +已被确认的数据应当从发送缓存中删除,因此发送缓存和发送窗口的后沿是重合的。发送应用程序必须控制写入缓存的速率,不能太快,否则发送缓存就会没有存放数据的空间 + +接收缓存用来暂时存放: + +- 按序到达的、但尚未被接收应用程序读取的数据 +- 未按序到达的数据 + +收到的分组被检测出有差错,则丢弃。接收应用程序来不及读取收到的数据,接收缓存最终就会被填满,使接收窗口减小到零。接收应用程序能够及时从接收缓存中读取收到的数据,接收窗口就可以增大,最大亦不能超过接收缓存的大小 + +要点小结: + +- 虽然 A 的发送窗口是根据 B 的接收窗口设置的,但在同一时刻,A 的发送窗口并不总是和 B 的接收窗口一样大。通过网络传送窗口值需要经历一定的时间滞后,该时间并不确定的 +- 对于不按序到达的数据,TCP 通常是先临时存放在接收窗口,等字节流中所缺少的字节收到后,在`按序交付上层的应用进程` +- TCP 要求接收方必须有累积确认的功能,这样可以减少传输开销 + +### 超时重传时间的选择 + +TCP 的发送方在规定的时间内没有收到确认就要重传已发送的报文段。这种重传的概念是很简单的,但重传时间的选择却是 TCP 最复杂的问题之一 + +由于 TCP 的下层是互联网环境,发送的报文段可能只经过一个高速率的局域网,也可能经过多个低速率的网络,并且每个 IP 数据报所选择的路由还可能不同。如果把超时重传时间设置得太短,就会引起很多报文段的不必要的重传,使网络负荷增大。但若把超时重传时间设置的过长,则又使网络的空闲时间增大,降低了传输效率 + +TCP 采用了一种自适应算法,它记录一个报文段发出的时间,以及收到相应的确认的时间。这两个时间之差就是`报文段的往返时间 RTT` + +> 新的 RTTs = (1 - α) x (旧的 RTTs) + α x (新的 RTT 样本) + +RTT:报文段往返时间 +RTTs:加权平均往返时间 +α: 0 ≤ α < 1,RFC 6298 推荐的 α 值为 1/8,即 0.125 + +> RTO = RTTs + 4 x RTTD + +RTO:超时重传时间 +RTTD:RTT 的偏差的加权平均值 + +> 新的 RTTD = (1 - β) x (旧的 RTTD) + β x |RTTs - 新的 RTT 样本| + +β:小于1的系数,推荐值是 1/4,即 0.25 + +## TCP 流量控制 + +### 利用滑动窗口实现流量控制 + +流量控制(flow control):让发送方的发送速率不要太快,要让接收方来得及接收 + +利用滑动窗口机制可以很方便地在 TCP 连接上实现对发送方的流量控制 + +![TCP协议-图13](./assets/network-tcp-13.png) + +`发送方的发送窗口不能超过接收方给出的接收窗口的数值`。TCP 的`窗口单位是字节,不是报文段` + +避免死锁:TCP 为每一个连接设有一个`持续计时器(persistence timer)`。只要 TCP 连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口`探测报文段`(仅携带1字节的数据),而对方就在确认这个探测报文段时给出了现在的窗口值。如果窗口仍是零,那么收到这个报文段的一方就重新设置持续计时器。如果窗口不是零,那么死锁的僵局就可以打破了 + +### TCP 的传输效率 + +#### 发送机制 + +- TCP 维持一个变量,它等于`最大报文段长度 MSS`。只要缓存中存放的数据达到 MSS 字节时,就组装成一个 TCP 报文段发送出去 +- 由发送方的应用进程指明要求发送报文段,即 TCP 支持的`推送(push)`操作 +- 发送方的一个计时器期限到了,这时把当前已有的缓存数据装入报文段(但长度不能超过 MSS)发送出去 + +#### Nagle 算法 + +> 在 TCP 的实现中广泛使用 Nagle 算法 + +若发送应用进程把要发送的数据逐个字节地送到 TCP 的发送缓存,则发送方就把第一个数据字节先发送出去,把后面到达的数据字节都缓存起来。当发送方收到对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段发送出去,同时继续对随后到达的数据进行缓存。只有在收到对前一个报文段的确认后才继续发送下一个报文段。当数据达到较快而网络速率较慢时,用这样的方法可明显地减少所用的网络宽带。Nagle 算法还规定,当到达的数据已达到发送窗口大小的一半或已达到报文段的最大长度时,就立即发送一个报文段。这样可以有效提高网络的吞吐量 + +#### 糊涂窗口综合征 + +> TCP 接收方的缓存已满,仅剩一个字节,并还将保持这种状态持续一段时间。导致发送方只能发送一个字节。导致网络的效率很低 + +为了解决这个问题,可以`让接收方等待一段时间`,使得或者接受缓存已有足够空间容纳一个最长的报文段,或者`等到接受缓存已有一半空闲的空间`。只要出现这两种情况之一,接收方就发出确认报文,并向发送方通知当前的窗口大小。发送方也不要发送大小的报文段,而是把数据积累成足够大的报文段,或达到接收方缓存的空间的一半大小 + +## TCP 的拥塞控制 + +### 拥塞控制的一般原理 + +在计算机网络中的链路容量(即宽带)、交换结点中的缓存和处理机等,都是网络资源。在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫做`拥塞`(congestion) + +`拥塞控制`就是`防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载`。拥塞控制所要做的都是一个前提,就是`网络能够承受现有的网络负荷` + +![TCP协议-图14](./assets/network-tcp-14.png) + +### TCP 的拥塞控制方法 + +TCP 进行拥塞控制的算法有四种,即`慢开始`(slow-start)、`拥塞避免`(congestion avoidance)、`快重传`(fast retransmit)和`快恢复`(fast recovery) + +#### 慢开始 + +当主机开始发送数据时,由于并不清楚网络的负荷情况,如果立即把大量数据字节注入到网络,就有可能引起网络发生拥塞。经验证明,较好的方法是先探测一下,即`由小到大逐渐增大发送窗口`,也就是说,`由小到大逐渐增大拥塞窗口数值` + +cwnd:发送方的拥塞窗口,开始发送方设置 cwnd = 1 + +#### 拥塞避免 + +让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加1,而不是像慢开始阶段那样加倍增加。因此在拥塞避免阶段就有“`加法增大`” AI(Additive Increase)的特点。这表明在拥塞避免阶段,拥塞窗口 cwnd `按线性规律缓慢增长`,比慢开始算法的拥塞窗口增长速率缓慢得多 + +“拥塞避免”并非完全能够避免拥塞,而是把拥塞窗口控制为按线性规律增长,`使网络比较不容易出现拥塞` + +在执行慢开始算法时,发送方每收到一个对新报文段的确认 ACK,就把拥塞窗口值加1,然后开始下一轮的传输。因此拥塞窗口 cwnd 随着传输轮次按指数规律增长。当拥塞窗口 cwnd 增长到慢开始门限值 ssthresh 时,就改成执行拥塞避免算法,拥塞窗口按线性规律增长 + +ssthresh:慢开始门限,一般的,会有一个初始值,下图中为16个报文段 + +![TCP协议-图15](./assets/network-tcp-15.png) + +当拥塞窗口 cwnd = 24 时,网络出现了超时,发送方判断为网络拥塞。于是调整门限值 ssthresh = cwnd / 2 = 12,同时设置拥塞窗口 cwnd = 1,进入慢开始阶段 + +#### 快重传 + +采用快重传算法可以让发送方`尽早知道发生了个别报文段的丢失`。快重传算法首先要求接收方不要等待自己发送数据时才进行捎带确认,而是要`立即发送确认`,即使收到了`失序的报文段`也要立即发出对已收到的报文段的重复确认 + +![TCP协议-图16](./assets/network-tcp-16.png) + +#### 快恢复 + +发送方知道当前只是丢失了个别的报文段。于是不启动慢开始,而是执行`快恢复`算法。这时,发送方调整门限值 ssthresh = cwnd / 2 = 8,同时设置拥塞窗口 cwnd = ssthresh = 8,并开始执行拥塞避免算法 + +TCP Reno 版本:区别于老的 TCP Tahao 版本 + +## TCP 的运输连接管理 + +TCP 是面向连接的协议。运输连接是用来传送 TCP 报文的。TCP 运输连接的建立和释放是每一次面向连接的通信中必不可少的过程。运输连接有三个阶段,`连接建立`、`数据传送`和`连接释放`。运输的连接管理就是使运输连接的建立和释放都能够正常地进行 + +在 TCP 连接建立过程中要解决以下三个问题: + +- 要使每一方能够确知对方的存在 +- 要允许双方协商一些参数(最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等) +- 能够对运输实体资源(缓存大小、连接表中的项目等)进行分配 + +### TCP 的连接建立 + +TCP 建立连接的过程叫做握手,握手需要在客户和服务器之间交换三个 TCP 报文段 + +![TCP协议-图17](./assets/network-tcp-17.png) + +#### 连接建立过程 + +1. 最初客户/服务器的 TCP 进程都处于 `CLOSED(关闭)`状态。在本实例中,A `主动打开连接`,而 B `被动打开连接` +2. B 的 TCP 服务器进程先创建`传输控制块` TCB,并处于 `LISTEN(收听)` 状态,等待客户的连接请求 +3. A 的 TCP 客户进程创建`传输控制模块` TCB。并向 B 发出连接请求报文段,首部中的同部位 SYN = 1,选择一个初始序号 seq = x。TCP 客户端进程进入 `SYN-SENT(同步已发送)` 状态。TCP 规定,SYN 报文段(即 SYN = 1 的报文段)不能携带数据,但要`消耗一个序号` +4. B 收到连接请求报文段后,如同意建立连接,则向 A 发送确认。在确认报文段中应把 SYN 位和 ACK 位都置1,确认号是 ack = x + 1,同时也为自己选择一个初始序号 seq = y。这时 TCP 服务器进程进入 `SYN-RCVD(同步收到)` 状态。这个报文段也不能携带数据,但同样`要消耗掉一个序号` +5. TCP 客户进程收到 B 的确认后,还要向 B 给出确认。确认报文段的 ACK 置1,确认号 ack = y + 1,而自己的序号 seq = x + 1。TCP 的标准规定,ACK 报文段可以携带数据。但`如果不携带数据则不消耗序号`,在这种情况下,下一个数据报文段的序号仍是 seq = x + 1。这时,TCP 连接已经建立,A 进入 `ESTABLISHED(已建立连接)` 状态 +6. 当 B 收到 A 的确认后,也进入 `ESTABLISHED` 状态 + +> `传输控制块` TCB(Transmission Control Block)存储了每一个连接中的一些重要信息,如:TCP 连接表,指向发送和接收缓存的指针,指向重传队列的指针,当前的发送和接收序号等等 + +#### 四报文握手 + +B 发送给 A 的报文段,可拆成两个报文段。先发送一个确认报文段(ACK = 1,ack = x + 1),然后再发送一个同步报文段(SYN = 1,seq = y)。这样的过程就变成了`四报文握手`,与三报文握手效果一致 + +#### 异常情况 + +为什么 A 最后还要发送一次确认呢?这主要是为了防止已失效的连接请求报文段突然又传到了 B,因而产生错误 + +正常情况:A 发出连接请求,但因连接请求报文丢失而未收到确认。于是 A 再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接。A 共发送了两个连接请求报文段,其中第一个丢失,第二个到达了 B,没有“已失效的连接请求报文段” + +异常情况:A 发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达 B。本来这是一个早已失效的报文段。但 B 收到此失效的连接请求报文段后,就误认为是 A 又发出一次新的连接请求。于是就向 A 发出确认报文段,同意建立连接。假定不采用报文握手,那么只要 B 发出确认,新的连接就建立了。 + +> 现在 A 并没有发出建立连接的请求,因此不会理睬 B 的确认,也不会向 B 发送数据。但 B 却以为新的运输连接已经建立了,并一直等待 A 发来数据。B 的`许多资源就这样被浪费了`。 + +> 采用三报文握手的办法,可以防止上述现象的发生 + +### TCP 的连接释放 + +![TCP协议-图18](./assets/network-tcp-18.png) + +#### 连接释放过程 + +1. A 的应用进程先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。A 把连接释放报文段首部的终止控制位 FIN 置1,其序号 seq = u,它等于前面已传送过的数据的最后一个字节的序号加1。这时 A 进入 `FIN-WAIT-1(终止等待1)` 状态,等待 B 的确认。TCP 规定,FIN 报文段即使不携带数据,也消耗一个序号 +2. B 收到连接释放报文段后即发出确认,确认号是 ack = u + 1,而这个报文段自己的序号是 v,等于 B 前面已传送过的数据的最后一个字节的序号加1。B随即进入 `CLOSE-WAIT(关闭等待)` 状态。TCP 服务器进程这时应通知高层应用进程,因而从 A 到 B 这个方向的连接就释放了,这时的 TCP 连接处于 `半关闭(half-close)` 状态,即 A 已经没有数据要发送了,但 B 若发送数据,A 仍要接收。也就是说,从 B 到 A 这个方向的连接并未关闭,这个状态可能会持续一段时间 +3. A 收到来自 B 的确认后,就进入 `FIN-WAIT-2(终止等待2)` 状态,等待 B 发出的连接释放报文段 +4. 若 B 已经没有要向 A 发送的数据,其应用进程就通知 TCP 释放连接。这时 B 发出的连接释放报文段必须使 FIN = 1。现假定 B 的序号为 w(在半关闭状态 B 可能又发送了一些数据)。B 还必须重复上次已发送过的确认号 ack = u + 1。这时 B 就进入 `LAST-ACK(最后确认)`状态,等待 A 的确认 +5. A 在收到 B 的连接释放报文段后,必须对此发出确认。在确认报文段中把 ACK 置1,确认号 ack = w + 1,而自己的序号是 seq = u + 1(根据 TCP 标准,前面发送过的 FIN 报文段要消耗一个序号)。然后进入到 `TIME-WAIT(时间等待)`状态。此时 TCP 连接还没有释放掉。必须经过`时间等待计时器(TIME-WAIT timer)`设置的时间2MSL后,A 才进入到 `CLOSED` 状态 +6. 当 A 撤销相应的传输控制块 TCB 后,就结束了这次的 TCP 连接 + +> 时间 MSL 叫做`最长报文段寿命`(Maximum Segment Lifetime),RFC 793建议设为2分钟。但这完全是从工程上来考虑的,对于现在的网络,MSL = 2分钟可能太长了一些 + +#### TIME-WAIT 等待时间 + +为什么 A 在 TIME-WAIT 状态必须等待 2MSL 的时间呢? + +为了保证 A 发送的最后一个 ACK 报文段能够到达 B。这个 ACK 报文段有可能丢失,因而使处在 LAST-ACK 状态的 B 收不到对已发送的 FIN + ACK 报文段的确认。B 会超时 重传这个 FIN + ACK 报文段,而 A 就能在 2MSL 时间内收到这个重传的 FIN + ACK 报文段。接着 A 重传一次确认,重新启动 2MSL 计时器。最后,A 和 B 都正常进入到 CLOSED 状态。如果 A 在 TIME-WAIT 状态不等待一段时间,而是在发完 ACK 报文段后立即释放连接,那么就无法收到 B 重传的 FIN + ACK 报文段,因而也不会再发送一次确认报文段。这样,B 就无法安装正常步骤进入 CLOSED 状态 + +防止前面提到的“已失效的连接请求报文段”出现在本连接中。A 在发送完最后一个 ACK 报文段后,再经过时间 2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段 + +B 只要收到 A 发出的确认,就进入 CLOSED 状态。同样,B 在撤销相应的传输控制 TCB 后,就结束了这次的 TCP 连接。B 结束 TCP 连接的时间要比 A 早一些 + +`保活计时器(keepalive timer)`:服务器每收到一次客户的数据,就重新设置保活计时器,时间的设置通常是两小时。若两小时没有收到客户的数据,服务器就发送一个探测报文段,以后则每隔75秒发送一次。若一连发送10个探测报文段后仍无客户的响应,服务器就认为客户端出了故障,接着就关闭这个连接 + +### TCP 的有限状态机 + +为了更清晰地看出 TCP 连接的各种状态之间的关系,下图为 TCP 的有限状态机。图中每一个方框即 TCP 可能具有的状态。每个方框中的大写英文字符串是 TCP 标准所使用的 TCP 连接状态名。状态之间的箭头表示可能发生的状态变迁。箭头旁边的字,表明引起这种变迁的原因,或表明发生状态变迁后又出现什么动作。请注意图中有三种不同的箭头。`粗实线箭头`表示对`客户进程的正常变迁`。`粗虚线箭头`表示对`服务器进程的正常变迁`。另一种`细线箭头`表示`异常变迁` + +![TCP协议-图19](./assets/network-tcp-19.png) + +**《TCP协议详解》原文链接:[https://blog.maplemark.cn/2019/04/tcp协议详解.html](https://blog.maplemark.cn/2019/04/tcp协议详解.html)** \ No newline at end of file diff --git "a/docs/01.\347\275\221\347\273\234/04.TCP\347\262\230\345\214\205\346\213\206\345\214\205.md" "b/docs/01.\347\275\221\347\273\234/04.TCP\347\262\230\345\214\205\346\213\206\345\214\205.md" new file mode 100755 index 0000000..5272992 --- /dev/null +++ "b/docs/01.\347\275\221\347\273\234/04.TCP\347\262\230\345\214\205\346\213\206\345\214\205.md" @@ -0,0 +1,50 @@ +# TCP 粘包拆包 + +## 粘包问题 + +在 TCP 这种字节流协议上做`应用层分包`是网络编程的基本需求。分包指的是在发生一个消息(message)或一帧(frame)数据时,通过一定的处理,让接收方能从字节流中识别并截取(还原)出一个个消息。因此,`“粘包问题”是个伪命题` + +## 短连接分包 + +对于短连接的 TCP 服务,分包不是一个问题,只要发送方主动关闭连接,就表示一个消息发送完毕,接收方 read() 返回0,从而知道消息的结尾 + +## TCP 发送机制 + +为了提高 TCP 的传输效率,TCP 有一套自己的发送机制 + +- TCP 维持一个变量,它等于`最大报文段长度 MSS`。只要缓存中存放的数据达到 MSS 字节时,就组装成一个 TCP 报文段发送出去 +- 由发送方的应用进程指明要求发送报文段,即 TCP 支持的`推送(push)`操作 +- 发送方的一个计时器期限到了,这时把当前已有的缓存数据装入报文段(但长度不能超过 MSS)发送出去 + +## 长连接分包 + +对于长连接的 TCP 服务,分包有四种方法 + +- 消息长度固定 +- 使用特殊的字符或字符串作为消息的边界,例如 HTTP 协议的 headers 以“\r\n”为字段的分隔符 +- 在每条消息的头部加一个长度字段,这恐怕是最常见的做法 +- 利用消息本身的格式来分包,例如 XML 格式的消息中 ``...`` 的配对,或者 JSON 格式中的 { ... } 的配对。解析这种消息格式通常会用到状态机(state machine) + +## 复杂的分包 + +假如消息格式非常简单,“消息”本身是一个字符串,每条消息有一个4字节的头部,以网络序存放字符串的长度。消息直接没有间隙,字符串也不要求以 '\0' 结尾 + +发送两条消息“hello”和“smartboy”,打包后的字节流共有21字节 + +```text +0x00, 0x00, 0x00, 0x05, 'h', 'e', 'l', 'l', 'o', +0x00, 0x00, 0x00, 0x08, 's', 'm', 'a', 'r', 't', 'b', 'o', 'y' +``` + +假设数据最终都全部到达,数据解析逻辑至少能正确处理以下各种数据到达的次序 + +- 一个字节一个字节到达 +- 数据分两次到达,第一次收到2个字节,不足消息的长度字段 +- 数据分两次到达,第一次收到4个字节,刚好够长度字段,但是没有 body +- 数据分两次到达,第一次收到8个字节,长度完整,但 body 不完整 +- 数据分两次到达,第一次收到9个字节,长度完整,但 body 也完整 +- 数据分两次到达,第一次收到10个字节,第一条消息的长度完整、body 也完整,第二条消息长度不完整 +- 请自行移动和增加分割点,一共有超过 100 万种可能(221-1) +- 数据一次就全部到达 + +**《TCP粘包拆包》 原文链接:[https://blog.maplemark.cn/2019/04/tcp粘包拆包.html](https://blog.maplemark.cn/2019/04/tcp粘包拆包.html)** \ No newline at end of file diff --git "a/docs/01.\347\275\221\347\273\234/05.HTTP\347\212\266\346\200\201\347\240\201.md" "b/docs/01.\347\275\221\347\273\234/05.HTTP\347\212\266\346\200\201\347\240\201.md" new file mode 100755 index 0000000..a8f666d --- /dev/null +++ "b/docs/01.\347\275\221\347\273\234/05.HTTP\347\212\266\346\200\201\347\240\201.md" @@ -0,0 +1,168 @@ +# HTTP 状态码 + +## 状态码 + +状态码是来告诉客户端,发生了什么事情。状态码为客户端提供了一种`理解事务处理结果`的`便捷方式`。状态码位于响应的起始行中 + +> 比如,在行 HTTP/1.0 200 OK 中,状态码就是200 + +客户端向一个 HTTP 服务器发送请求报文时,会遇到很多意想不到的情况,请求不一定能够成功完成。服务器可能会告诉你无法找到所请求的资源,你没有访问资源的权限,或者资源被移到了其他地方 + +状态码是在每条响应报文的起始行中返回的。会返回一个数字状态和一个可读的状态。`数字码`便于程序进行差错处理,而`原因短语`则便于人们理解 + +## 原因短语 + +原因短语是响应起始行中的最后一个组件。它为状态码提供了`文本形式`的解释 + +> 比如,在行 HTTP/1.0 200 OK 中,OK 就是原因短语 + +原因短语和状态码是成对出现的。原因短语是状态码的`可读`版本,应用程序开发者将其传送给用户,用于说明在请求间发生了什么情况。HTTP 规范并没有提供任何硬性规定,要求原因短语以何种形式出现 + +## 状态码分类 + +### 五大类 + +可以通过三位数字代码对不同状态码进行分类 + +- 200 到 299 之间的状态码表示成功 +- 300 到 399 之间的代码表示资源已经被移走了 +- 400 到 499 之间的代码表示客户端的请求出错了 +- 500 到 599 之间的代码表示服务器出错了 + +|状态码|整体范围|已定义范围|分类| +|-|-|-|-| +|1XX|100~199|100~101|信息提示| +|2XX|200~299|200~206|成功| +|3XX|300~399|300~305|重定向| +|4XX|400~499|400~415|客户端错误| +|5XX|500~599|500~505|服务器错误| + +当前的 HTTP 版本只为每类状态定义了几个代码。随着协议的发展,HTTP 规范中会正式地定义更多的状态码。若收到了不认识的状态码,可能是有人将其作为当前协议的`扩展定义`的。可以根据其所处的范围,将它作为那个类别中一个普通的成员来处理 + +> 例如,若收到了状态码 515(在 5XX 代码的已定义范围之外),就应该认为这条响应指出了服务器的错误,这是 5XX 报文的通用类别 + +### 100 ~ 199,信息状态码 + +HTTP/1.1 向协议中引入了信息性状态码。这些状态码相对较新,关于其复杂性和感 +知价值存在一些争论,而受到限制 + +|状态码|原因短语|含义| +|-|-|-| +|100|Continue|说明收到了请求的初始部分,请客户端继续。发送了这个状态码之后,服务器在收到请求之后必须进行响应| +|101|Switching Protocols|说明服务器正在根据客户端的指定,将协议切换成 Update 首部所列的协议| + +100 Continue 状态码的目的是对这样的情况进行优化:HTTP 客户端应用程序有一个实体的主体部分要发送给服务器,但希望在发送之前查看一下服务器是否会接受这个实体。客户端应用程序只有在避免向服务器发送一个服务器`无法处理或使用的大实体`,才应该使用 100 Continue + +### 200 ~ 299,成功状态码 + +客户端发起请求时,这些请求通常都是成功的。服务器有一组用来表示成功的状态码,分别对应于不同类型的请求 + +|状态码|原因短语|含义| +|-|-|-| +|200|OK|请求没问题,实体的主体部分包含了所请求的资源| +|201|Created|用于创建服务器对象的请求(比如:PUT)。响应的实体主体部分中应该包含引用了已创建的资源的URL,Location首部包含的则是最具体的引擎。服务器必须在发送这个状态码之前创建好对象| +|202|Accepted|请求已被接受,服务器还未对其执行任何动作。不能保证服务器会完成这个请求;接受请求时,它看起来是有效的。服务器应在实体的主体部分包含对请求状态的描述,或附加请求预计处理时间、信息获取指针| +|203|Non-Authoritative Information|实体首部包含的信息不是来自于源端服务器,而是来自资源的副本。如果中间节点上有一份副本,但无法或没有对元数据进行验证,就会出现这种情况| +|204|No Content|响应报文中包含若干首部和一个状态行,但没有实体的主体部分。主要用于在浏览器不转为显示新文档的情况下,对其进行更新(比如刷新一个表单页面)| +|205|Reset Content|另一个主要用于浏览器的代码。负责告知浏览器清除当前页面中的所有 HTML 表单元素| +|206|Partial Content|成功执行了一个部分或 Range(范围)请求。客户端可以通过一些特殊的首部来获取部分或某个范围内的文档| + +### 300 ~ 399,重定向状态码 + +重定向状态码要么告知客户端使用替代位置来访问他们所感兴趣的资源,要么就提供一个替代的响应而不是资源的内容。如果资源已被移动,可发送一个重定向状态码和一个可选的 Location 首部来告知客户端资源已被移走,以及现在可以在哪里找到它。这样,浏览器就可以在不打扰使用者的情况下,透明地转入新的位置了 + +请求报文 + +```text +GET /index.php HTTP/1.1 +Host: blog.maplemark.cn +Accept: * +``` + +响应报文 + +```text +HTTP/1.1 301 Moved Permanently +Server: nginx/1.12.2 +Date: Fri, 19 Apr 2019 03:58:59 GMT +Content-Type: text/html; charset=UTF-8 +X-Powered-By: PHP/7.2.16 +Location: https://blog.maplemark.cn/ +``` + +请求报文 + +```text +GET / HTTP/1.1 +Host: blog.maplemark.cn +Accept: * +``` + +响应报文 + +```text +HTTP/1.1 200 OK +Server: nginx/1.12.2 +Date: Fri, 19 Apr 2019 03:59:34 GMT +Content-Type: text/html; charset=UTF-8 +Transfer-Encoding: chunked +Connection: keep-alive +... +``` + +|状态码|原因短语|含义| +|-|-|-| +|300|Multiple Choices|客户端请求一个实际指向多个资源的URL时会返回这个状态码,比如服务器上有某个HTML文档有多个语言版本。返回时会带有一个选项列表,用户可以选择期望使用的那项| +|301|Moved Permanently|在请求的 URL 已被移除时使用。响应的 Location 首部中应该包含资源现在所处的 URL| +|302|Found|与 301 状态码类似;但是,客户端应该使用 Location 首部给出的URL 来临时定位资源。将来的请求仍应使用老的 URL| +|303|See Other|告知客户端应该用另一个 URL 来获取资源。新的 URL 位于响应报文的 Location 首部。其主要目的是允许 POST 请求的响应将客户端定向到某个资源上去| +|304|Not Modified|客户端可以通过所包含的请求首部,使其请求变成有条件的。若用户发起了一个条件 GET 请求,而资源近期未被修改,可以通过该状态码表明。带有这个状态码的响应不应该包含实体的主体部分| +|305|Use Proxy|用来说明必须通过一个代理来访问资源;代理的位置由 Location首部给出。客户端是相对某个特定资源来解析这条响应的,不能假定所有请求,甚至所有对持有所请求资源的服务器的请求都通过这个代理进行。如果客户端错误地让代理介入了某条请求,可能会引发破坏性的行为,而且会造成安全漏洞| +|306|(未使用)|当前未使用| +|307|Temporary Redirect|与 301 状态码类似;但客户端应该使用 Location 首部给出的 URL来临时定位资源。将来的请求应该使用老的 URL| + +> 302、303 和 307 状态码之间存在一些交叉。这些状态码的用法有着细微的差别,大部分差别都源于 HTTP/1.0 和 HTTP/1.1 应用程序对这些状态码`处理方式`的不同,为兼容 HTTP/1.0 而保留了一些状态码(例如 302 状态码) + +### 400 ~ 499,客户端错误状态码 + +有时客户端会发送一些服务器`无法处理`的东西,比如格式错误的请求报文,或者最常见的是,请求一个不存在的 URL + +很多客户端错误都是由浏览器来处理的,甚至不会打扰到你。只有少量错误,比如404,还是会穿过浏览器来到用户面前 + +|状态码|原因短语|含义| +|-|-|-| +|400|Bad Request|用于告知客户端它发送了一个错误的请求| +|401|Unauthorized|与适当的首部一同返回,在这些首部中请求客户端在获取对资源的访问权之前,对自己进行认证| +|402|Payment Required|现在这个状态码还未使用,但已经被保留,以作未来之用| +|403|Forbidden|用于说明请求被服务器拒绝了。如果服务器想说明为什么拒绝请求,可以包含实体的主体部分来对原因进行描述。但这个状态码通常是在服务器不想说明拒绝原因的时候使用的| +|404|Not Found|用于说明服务器无法找到所请求的 URL。通常会包含一个实体,以便客户端应用程序显示给用户看| +|405|Method Not Allowed|发起的请求中带有所请求的 URL 不支持的方法时,使用此状态码。应该在响应中包含 Allow 首部,以告知客户端对所请求的资源可以使用哪些方法| +|406|Not Acceptable|客户端可以指定参数来说明它们愿意接收什么类型的实体。服务器没有与客户端可接受的 URL 相匹配的资源时,使用此代码。通常,服务器会包含一些首部,以便客户端弄清楚为什么请求无法满足| +|407|Proxy Authentication Required|与 401 状态码类似,但用于要求对资源进行认证的代理服务器| +|408|Request Timeout|如果客户端完成请求所花的时间太长,服务器可以回送此状态码,并关闭连接。超时时长随服务器的不同有所不同,但通常对所有的合法请求来说,都是够长的| +|409|Conflict|用于说明请求可能在资源上引发的一些冲突。服务器担心请求会引发冲突时,可以发送此状态码。响应中应该包含描述冲突的主体| +|410|Gone|与 404 类似,只是服务器曾经拥有过此资源。主要用于 Web 站点的维护,这样服务器的管理者就可以在资源被移除的情况下通知客户端了| +|411|Length Required|服务器要求在请求报文中包含 Content-Length 首部时使用| +|412|Precondition Failed|客户端发起了条件请求,且其中一个条件失败了的时候使用。客户端包含了 Expect 首部时发起的就是条件请求| +|413|Request Entity Too Large|客户端发送的实体主体部分比服务器能够或者希望处理的要大时,使用此状态码| +|414|Request URI Too Long|客户端所发请求中的请求 URL 比服务器能够或者希望处理的要长时,使用此状态码| +|415|Unsupported Media Type|服务器无法理解或无法支持客户端所发实体的内容类型时,使用此状态码| +|416|Requested Range Not Satisfiable|请求报文所请求的是指定资源的某个范围,而此范围无效或无法满足时,使用此状态码| +|417|Expectation Failed|请求的 Expect 请求首部包含了一个期望,但服务器无法满足此期望时,使用此状态码。如果代理或其他中间应用程序有确切证据说明源端服务器会为某请求产生一个失败的期望,就可以发送这个响应状态码| + +### 500 ~ 599,服务器错误状态码 + +有时客户端发送了一条有效请求,服务器自身却出错了。这可能是客户端碰上了服务器的缺陷,或者服务器上的子元素,比如某个网关资源,出了错 + +代理尝试着代表客户端与服务器进行交流时,经常会出现问题。代理会发布 5XX 服务器错误状态码来描述所遇到的问题 + +|状态码|原因短语|含义| +|-|-|-| +|500|Internal Server Error|服务器遇到一个妨碍它为请求提供服务的错误时,使用此状态码| +|501|Not Implemented|客户端发起的请求超出服务器的能力范围(比如,使用了服务器不支持的请求方法)时,使用此状态码| +|502|Bad Gateway|作为代理或网关使用的服务器从请求响应链的下一条链路上收到了一条伪响应(比如,它无法连接到其父网关)时,使用此状态码| +|503|Service Unavailable|用来说明服务器现在无法为请求提供服务,但将来可以。如果服务器知道什么时候资源会变为可用的,可以在响应中包含一个 RetryAfter 首部| +|504|Gateway Timeout|与状态码 408 类似,只是这里的响应来自一个网关或代理,它们在等待另一服务器对其请求进行响应时超时了| +|505|HTTP Version Not Supported|服务器收到的请求使用了它无法或不愿支持的协议版本时,使用此状态码。有些服务器应用程序会选择不支持协议的早期版本| + +**《HTTP状态码》 原文链接:[https://blog.maplemark.cn/2019/04/http状态码.html](https://blog.maplemark.cn/2019/04/http状态码.html)** \ No newline at end of file diff --git "a/docs/01.\347\275\221\347\273\234/06.HTTP\346\226\271\346\263\225\350\257\246\350\247\243.md" "b/docs/01.\347\275\221\347\273\234/06.HTTP\346\226\271\346\263\225\350\257\246\350\247\243.md" new file mode 100755 index 0000000..1a4ef84 --- /dev/null +++ "b/docs/01.\347\275\221\347\273\234/06.HTTP\346\226\271\346\263\225\350\257\246\350\247\243.md" @@ -0,0 +1,95 @@ +# HTTP方法详解 + +## 常见的 HTTP 方法 + +HTTP 请求方法用于告诉服务器要做什么。HTTP 规范中定义了一组常用的请求方法。 + +> 例如:GET 方法负责从服务器获取文档,POST 方法会向服务器发送需要处理的数据,OPTIONS 方法用于确定服务器的一般功能,或者服务器处理特定资源的能力 + +下图描述了7种 HTTP 方法,并不是所有服务器都实现了所有7种方法。有些方法的请求报文中有主体,有些则无主体的请求 + +![HTTP方法](./assets/network-http-method.png) + +由于 HTTP 设计易于扩展,除这些方法,其他服务器可能还会实现一些自己的请求方法。这些附加的方法是对 HTTP 规范的扩展,被称为`扩展方法` + +## 安全方法 + +HTTP 定义了一组被称为`安全方法`的方法。GET 方法和 HEAD 方法都被认为是安全的,这就意味着使用 GET 或 HEAD 方法的 HTTP 请求都不会产生什么动作 + +不产生动作,在这里意味着 HTTP 请求不会在服务器上产生什么结果。例如,你在 Colin 的五金商店购物时,点击了“提交购买”按钮。点击按钮时会提交一个带有信用卡信息的 POST 请求,那么在服务器上,就会为你执行一个动作。在这种情况下,为购买行为支付信用卡就是所执行的动作 + +安全方法并不一定是什么动作都不执行的(实际上,这是由 Web 开发者决定的)。使用安全方法的目的就是当使用可能引发某一动作的不安全方法时,`允许 HTTP 应用程序开发者通知用户`。在 Colin 的五金商店的例子中,你的 Web 浏览器可能会弹出一条警告消息,说明你正在用不安全的方法发起请求,这样可能会在服务器上引发一些事件(比如用你的信用卡支付费用) + +## 方法详解 + +### GET 方法 + +GET 是最常用的方法。通常用于请求服务器发送某个资源。HTTP/1.1 要求服务器实现此方法 + +![HTTP方法-GET](./assets/network-http-method-01.png) + +### HEAD 方法 + +HEAD 方法与 GET 方法的行为很类似,但服务器在响应中只返回首部。不会返回实体的主体部分。这就允许客户端在未获取实际资源的情况下,对资源的首部进行检查。使用 HEAD,可以: + +- 在不获取资源的情况下了解资源的情况(比如,判断其类型) +- 通过查看响应中的状态码,看看某个对象是否存在 +- 通过查看首部,测试资源是否被修改了 + +服务器开发者必须确保返回的首部与 GET 请求所返回的首部完全相同。遵循 HTTP/1.1 规范,就必须实现 HEAD 方法 + +![HTTP方法-HEAD](./assets/network-http-method-02.png) + +### PUT 方法 + +与 GET 从服务器读取文档相反,PUT 方法会向服务器写入文档。有些发布系统允许用户创建 Web 页面,并用 PUT 直接将其安装到 Web 服务器上去 + +![HTTP方法-PUT](./assets/network-http-method-03.png) + +PUT 方法的语义就是让服务器用请求的主体部分来创建一个由所请求的 URL 命名的新文档,或者,如果那个 URL 已经存在的话,就用这个主体来替代它。 + +因为 PUT 允许用户对内容进行修改,所以很多 Web 服务器都要求在执行 PUT 之前,用密码登录 + +### POST 方法 + +POST 方法起初是用来向服务器输入数据的。实际上,通常会用它来支持 HTML 的表单。表单中填好的数据通常会被送给服务器,然后由服务器将其发送到它要去的地方(比如,送到一个服务器网关程序中,然后由这个程序对其进行处理) + +![HTTP方法-POST](./assets/network-http-method-04.png) + +### TRACE 方法 + +客户端发起一个请求时,这个请求可能要穿过防火墙、代理、网关或其他一些应用程序。每个中间节点都可能会`修改原始的 HTTP 请求`。TRACE 方法允许客户端在最终将请求发送给服务器时,看看它变成了什么样子 + +TRACE 请求会在目的服务器端发起一个“环回”诊断。行程最后一站的服务器会弹回一条 TRACE 响应,并在响应主体中携带它收到的原始请求报文。这样客户端就可以查看在所有中间 HTTP 应用程序组成的请求/响应链上,原始报文是否,以及如何被毁坏或修改过 TRACE 方法主要用于诊断;也就是说,用于验证请求是否如愿穿过了请求/响应链。它也是一种很好的工具,可以用来查看代理和其他应用程序对用户请求所产生效果 + +尽管 TRACE 可以很方便地用于诊断,但它确实也有缺点,它假定中间应用程序对各种不同类型请求(不同的方法——GET、HEAD、POST 等)的处理是相同的。很多 HTTP 应用程序会根据方法的不同做出不同的事情——比如,代理可能会将 POST 请求直接发送给服务器,而将 GET 请求发送给另一个 HTTP 应用程序(比如Web 缓存)。TRACE 并不提供区分这些方法的机制。通常,中间应用程序会自行决定对 TRACE 请求的处理方式 + +TRACE 请求中不能带有实体的主体部分。TRACE 响应的实体主体部分包含了响应服务器收到的请求的精确副本 + +![HTTP方法-TRACE](./assets/network-http-method-05.png) + +### OPTIONS + +OPTIONS 方法请求 Web 服务器告知其支持的各种功能。可以询问服务器通常支持哪些方法,或者对某些特殊资源支持哪些方法。(有些服务器可能只支持对一些特殊类型的对象使用特定的操作) + +这为客户端应用程序提供了一种手段,使其不用实际访问那些资源就能判定访问各种资源的最优方式 + +![HTTP方法-OPTIONS](./assets/network-http-method-06.png) + +### DELETE + +DELETE 方法所做的事情就是请服务器删除请求 URL 所指定的资源。但是,客户端应用程序无法保证删除操作一定会被执行。因为 HTTP 规范允许服务器在不通知客户端的情况下撤销请求 + +![HTTP方法-DELETE](./assets/network-http-method-07.png) + +### 扩展方法 + +HTTP 被设计成字段可扩展的,这样新的特性就不会使老的软件失效了。扩展方法指的就是没有在 HTTP/1.1 规范中定义的方法。服务器会为它所管理的资源实现一些 HTTP 服务,这些方法为开发者提供了一种扩展这些 HTTP 服务能力的手段。下图列出了一些常见的扩展方法实例。这些方法就是 WebDAV HTTP 扩展包含的所有方法,这些方法有助于通过 HTTP 将 Web 内容发布到 Web 服务器上去 + +![HTTP方法-扩展方法](./assets/network-http-method-08.png) + +并不是所有的扩展方法都是在正式规范中定义的,认识到这一点很重要。如果你定义了一个扩展方法,很可能大部分 HTTP 应用程序都无法理解。同样,你的 HTTP应用程序也可能会遇到一些其他应用程序在用的,而它并不理解的扩展方法 + +在这些情况下,最好对扩展方法宽容一些。如果能够在不破坏端到端行为的情况下将带有未知方法的报文传递给下游服务器,代理应尝试传递这些报文。如果可能破坏端到端行为则应以 501 Not Implemented(无法实现)状态码进行响应。最好按惯例“对所发送的内容要求严一点,对所接收的内容宽容一些”来处理扩展方法(以及一般的 HTTP 扩展) + +**《HTTP方法详解》 原文链接:[https://blog.maplemark.cn/2019/04/http方法详解.html](https://blog.maplemark.cn/2019/04/http方法详解.html)** \ No newline at end of file diff --git "a/docs/01.\347\275\221\347\273\234/07.HTTPS\347\273\206\350\212\202\344\273\213\347\273\215.md" "b/docs/01.\347\275\221\347\273\234/07.HTTPS\347\273\206\350\212\202\344\273\213\347\273\215.md" new file mode 100755 index 0000000..07a6573 --- /dev/null +++ "b/docs/01.\347\275\221\347\273\234/07.HTTPS\347\273\206\350\212\202\344\273\213\347\273\215.md" @@ -0,0 +1,93 @@ +# HTTPS 细节介绍 + +HTTPS 是最常见的 HTTP `安全版本`。它得到了很广泛的应用,所有主要的商业浏览器和服务器上都提供 HTTPS。HTTPS 将 HTTP 协议与一组强大的对称、非对称和基于证书的加密技术结合在一起,使得 HTTPS 不仅很安全,而且很灵活,很容易在处于无序状态的、分散的全球互联网上进行管理 + +HTTPS 加速了因特网应用程序的成长,已经成为基于 Web 的电子商务快速成长的主要推动力。在广域网中对分布式 Web 应用程序的安全管理方面,HTTPS 也是非常重要的 + +## HTTPS 概述 + +HTTPS 就是在安全的传输层上发送的 HTTP。HTTPS 没有将未加密的 HTTP 报文发送给 TCP,并通过世界范围内的因特网进行传输,它在将 HTTP 报文发送给 TCP 之前,先将其发送给了一个安全层,对其进行加密 + +![HTTPS-图01](./assets/network-https-01.png) + +现在,HTTP 安全层是通过 SSL 及其现代替代协议 TLS 来实现的。我们遵循常见的用法,用术语 SSL 来表示 SSL 或者 TLS + +## HTTPS 方案 + +现在,安全 HTTP 是可选的。因此,对 Web 服务器发起请求时,我们需要有一种方式来告知 Web 服务器去执行 HTTP 的安全协议版本。这是在 URL 的方案中实现的。通常情况下,非安全 HTTP 的 URL 方案前缀为 http,如下所示: + +> http://blog.maplemark.cn + +在安全 HTTPS 协议中,URL 的方案前缀为 https,如下所示: + +> https://blog.maplemark.cn + +请求一个客户端(比如 Web 浏览器)对某 Web 资源执行某事务时,它会去检查 URL 的方案 + +- 如果 URL 的方案为 http,客户端就会打开一条到服务器端口 80(默认情况下) +的连接,并向其发送老的 HTTP 命令 +- 如果 URL 的方案为 https,客户端就会打开一条到服务器端口 443(默认情况下) +的连接,然后与服务器“握手”,以二进制格式与服务器交换一些 SSL 安全参数, +附上加密的 HTTP 命令 + +SSL 是个二进制协议,与 HTTP 完全不同,其流量是承载在另一个端口上的(SSL 通常是由端口 443 承载的)。如果 SSL 和 HTTP 流量都从端口 80 到达,大部分 Web 服务器会将二进制 SSL 流量理解为错误的 HTTP 并关闭连接。将安全服务进一步整合到 HTTP 层中去就无需使用多个目的端口了,在实际中这样不会引发严重的问题 + +![HTTPS-图02](./assets/network-https-02.png) + +## 建立安全传输 + +在未加密 HTTP 中,客户端会打开一条到 Web 服务器端口 80 的 TCP 连接,发送一条请求报文,接收一条响应报文,关闭连接 + +由于 SSL 安全层的存在,HTTPS 中这个过程会略微复杂一些。在 HTTPS 中,客户端首先打开一条到 Web 服务器端口 443(安全 HTTP 的默认端口)的连接。一旦建立了 TCP 连接,客户端和服务器就会初始化 SSL 层,对加密参数进行沟通,并交换密钥。握手完成之后,SSL 初始化就完成了,客户端就可以将请求报文发送给安全层了。在将这些报文发送给 TCP 之前,要先对其进行加密 + +## SSL 握手 + +在发送已加密的 HTTP 报文之前,客户端和服务器要进行一次 SSL 握手,在这个握手过程中,它们要完成以下工作 + +- 交换协议版本号 +- 选择一个两端都了解的密码 +- 对两端的身份进行认证 +- 生成临时的会话密钥,以便加密信道 + +![HTTPS-图03](./assets/network-https-03.png) + +在通过网络传输任何已加密的 HTTP 数据之前,SSL 已经发送了一组握手数据来建立通信连接了 + +![HTTPS-图04](./assets/network-https-04.png) + +这是 SSL 握手的简化版本。根据 SSL 的使用方式,握手过程可能会复杂一些,但总 +的思想就是这样 + +## 服务器证书 + +SSL 支持双向认证,将服务器证书承载回客户端,再将客户端的证书回送给服务器。而现在,浏览时并不经常使用客户端证书。大部分用户甚至都没有自己的客户端证书。服务器可以要求使用客户端证书,但实际中很少出现这种情况。 + +另一方面,安全 HTTPS 事务总是要求使用服务器证书的。在一个 Web 服务器上执行安全事务,比如提交信用卡信息时,你总是希望是在与你所认为的那个组织对话。由知名权威机构签发的服务器证书可以帮助你在发送信用卡或私人信息之前评估你对服务器的信任度。 + +服务器证书是一个显示了组织的名称、地址、服务器 DNS 域名以及其他信息的 X.509 v3 派生证书。你和你所用的客户端软件可以检查证书,以确保所有的信息都是可信的 + +![HTTPS-图05](./assets/network-https-05.png) + +## 站点证书的有效性 + +SSL 自身不要求用户检查 Web 服务器证书,但大部分现代浏览器都会对证书进行简单的完整性检查,并为用户提供进行进一步彻查的手段。网景公司提出的一种 Web 服务器证书有效性算法是大部分浏览器有效性验证技术的基础。 + +- 日期检测 + +首先,浏览器检查证书的起始日期和结束日期,以确保证书仍然有效。如果证书过期了,或者还未被激活,则证书有效性验证失败,浏览器显示一条错误信息 + +- 签名颁发者可信度检测 + +每个证书都是由某些证书颁发机构(CA)签发的,它们负责为服务器担保。证书有不同的等级,每种证书都要求不同级别的背景验证。比如,如果申请某个电子商务服务器证书,通常需要提供一个营业的合法证明 + +任何人都可以生成证书,但有些 CA 是非常著名的组织,它们通过非常清晰的流程来验证证书申请人的身份及商业行为的合法性。因此,浏览器会附带一个签名颁发机构的受信列表。如果浏览器收到了某未知(可能是恶意的)颁发机构签发的证书,那它通常会显示一条警告信息。有些证书会携带到受信 CA 的有效签名路径,浏览器可能会选择接受所有此类证书。换句话说,如果某受信 CA 为“Sam 的签名商店”签发了一个证书,而 Sam 的签名商店也签发了一个站点证书,浏览器可能会将其作为从有效 CA 路径导出的证书接受 + +- 签名检测 + +一旦判定签名授权是可信的,浏览器就要对签名使用签名颁发机构的公开密钥,并将其与校验码进行比较,以查看证书的完整性 + +- 站点身份检测 + +为防止服务器复制其他人的证书,或拦截其他人的流量,大部分浏览器都会试着去验证证书中的域名与它们所对话的服务器的域名是否匹配。服务器证书中通常都包含一个域名,但有些 CA 会为一组或一群服务器创建一些包含了服务器名称列表或通配域名的证书。如果主机名与证书中的标识符不匹配,面向用户的客户端要么就去通知用户,要么就以表示证书不正确的差错报文来终止连接 + +**《HTTPS细节介绍》 原文链接:[http://blog.maplemark.cn/2019/05/https细节介绍.html](http://blog.maplemark.cn/2019/05/https细节介绍.html)** \ No newline at end of file diff --git "a/docs/01.\347\275\221\347\273\234/08.SSL-TLS\345\215\217\350\256\256\350\277\220\350\241\214\346\234\272\345\210\266\347\232\204\346\246\202\350\277\260.md" "b/docs/01.\347\275\221\347\273\234/08.SSL-TLS\345\215\217\350\256\256\350\277\220\350\241\214\346\234\272\345\210\266\347\232\204\346\246\202\350\277\260.md" new file mode 100755 index 0000000..c268ba7 --- /dev/null +++ "b/docs/01.\347\275\221\347\273\234/08.SSL-TLS\345\215\217\350\256\256\350\277\220\350\241\214\346\234\272\345\210\266\347\232\204\346\246\202\350\277\260.md" @@ -0,0 +1,142 @@ +# SSL/TLS协议运行机制的概述 + +互联网的通信安全,建立在SSL/TLS协议之上。 + +本文简要介绍SSL/TLS协议的运行机制。文章的重点是设计思想和运行过程,不涉及具体的实现细节。如果想了解这方面的内容,请参阅[RFC文档](http://tools.ietf.org/html/rfc5246)。 + +## 一、作用 + +不使用SSL/TLS的HTTP通信,就是不加密的通信。所有信息明文传播,带来了三大风险。 + +- `窃听风险`(eavesdropping):第三方可以获知通信内容。 +- `篡改风险`(tampering):第三方可以修改通信内容。 +- `冒充风险`(pretending):第三方可以冒充他人身份参与通信。 + +SSL/TLS协议是为了解决这三大风险而设计的,希望达到: + +- 所有信息都是`加密传播`,第三方无法窃听。 +- 具有`校验机制`,一旦被篡改,通信双方会立刻发现。 +- 配备`身份证书`,防止身份被冒充。 + +互联网是开放环境,通信双方都是未知身份,这为协议的设计带来了很大的难度。而且,协议还必须能够经受所有匪夷所思的攻击,这使得SSL/TLS协议变得异常复杂。 + +## 二、历史 + +互联网加密通信协议的历史,几乎与互联网一样长。 + +```text +1994年,NetScape公司设计了SSL协议(Secure Sockets Layer)的1.0版,但是未发布。 +1995年,NetScape公司发布SSL 2.0版,很快发现有严重漏洞。 +1996年,SSL 3.0版问世,得到大规模应用。 +1999年,互联网标准化组织ISOC接替NetScape公司,发布了SSL的升级版TLS 1.0版。 +2006年和2008年,TLS进行了两次升级,分别为TLS 1.1版和TLS 1.2版。最新的变动是2011年TLS 1.2的修订版。 +``` + +目前,应用最广泛的是TLS 1.0,接下来是SSL 3.0。但是,主流浏览器都已经实现了TLS 1.2的支持。 + +TLS 1.0通常被标示为SSL 3.1,TLS 1.1为SSL 3.2,TLS 1.2为SSL 3.3。 + +## 三、基本的运行过程 + +SSL/TLS协议的基本思路是采用公钥加密法,也就是说,客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密。 + +但是,这里有两个问题。 + +- 如何保证公钥不被篡改? + +> 解决方法:将公钥放在数字证书中。只要证书是可信的,公钥就是可信的。 + +- 公钥加密计算量太大,如何减少耗用的时间? + +> 解决方法:每一次对话(session),客户端和服务器端都生成一个"对话密钥"(session key),用它来加密信息。由于"对话密钥"是对称加密,所以运算速度非常快,而服务器公钥只用于加密"对话密钥"本身,这样就减少了加密运算的消耗时间。 + +因此,SSL/TLS协议的基本过程是这样的: + +> 客户端向服务器端索要并验证公钥。 + +> 双方协商生成"对话密钥"。 + +> 双方采用"对话密钥"进行加密通信。 + +上面过程的前两步,又称为"握手阶段"(handshake)。 + +## 四、握手阶段的详细过程 + +"握手阶段"涉及四次通信,我们一个个来看。需要注意的是,"握手阶段"的所有通信都是明文的。 + +### 4.1 客户端发出请求(ClientHello) + +首先,客户端(通常是浏览器)先向服务器发出加密通信的请求,这被叫做ClientHello请求。 + +在这一步,客户端主要向服务器提供以下信息。 + +> 支持的协议版本,比如TLS 1.0版。 + +> 一个客户端生成的随机数,稍后用于生成"对话密钥"。 + +> 支持的加密方法,比如RSA公钥加密。 + +> 支持的压缩方法。 + +这里需要注意的是,客户端发送的信息之中不包括服务器的域名。也就是说,理论上服务器只能包含一个网站,否则会分不清应该向客户端提供哪一个网站的数字证书。这就是为什么通常一台服务器只能有一张数字证书的原因。 + +对于虚拟主机的用户来说,这当然很不方便。2006年,TLS协议加入了一个Server Name Indication扩展,允许客户端向服务器提供它所请求的域名。 + +### 4.2 服务器回应(SeverHello) + +服务器收到客户端请求后,向客户端发出回应,这叫做SeverHello。服务器的回应包含以下内容。 + +> 确认使用的加密通信协议版本,比如TLS 1.0版本。如果浏览器与服务器支持的版本不一致,服务器关闭加密通信。 + +> 一个服务器生成的随机数,稍后用于生成"对话密钥"。 + +> 确认使用的加密方法,比如RSA公钥加密。 + +> 服务器证书。 + +除了上面这些信息,如果服务器需要确认客户端的身份,就会再包含一项请求,要求客户端提供"客户端证书"。比如,金融机构往往只允许认证客户连入自己的网络,就会向正式客户提供USB密钥,里面就包含了一张客户端证书。 + +### 4.3 客户端回应 + +客户端收到服务器回应以后,首先验证服务器证书。如果证书不是可信机构颁布、或者证书中的域名与实际域名不一致、或者证书已经过期,就会向访问者显示一个警告,由其选择是否还要继续通信。 + +如果证书没有问题,客户端就会从证书中取出服务器的公钥。然后,向服务器发送下面三项信息。 + +> 一个随机数。该随机数用服务器公钥加密,防止被窃听。 + +> 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。 + +> 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供服务器校验。 + +上面第一项的随机数,是整个握手阶段出现的第三个随机数,又称"pre-master key"。有了它以后,客户端和服务器就同时有了三个随机数,接着双方就用事先商定的加密方法,各自生成本次会话所用的同一把"会话密钥"。 + +至于为什么一定要用三个随机数,来生成"会话密钥",dog250解释得很好: + +> "不管是客户端还是服务器,都需要随机数,这样生成的密钥才不会每次都一样。由于SSL协议中证书是静态的,因此十分有必要引入一种随机因素来保证协商出来的密钥的随机性。 + +> 对于RSA密钥交换算法来说,pre-master-key本身就是一个随机数,再加上hello消息中的随机,三个随机数通过一个密钥导出器最终导出一个对称密钥。 + +> pre master的存在在于SSL协议不信任每个主机都能产生完全随机的随机数,如果随机数不随机,那么pre master secret就有可能被猜出来,那么仅适用pre master secret作为密钥就不合适了,因此必须引入新的随机因素,那么客户端和服务器加上pre master secret三个随机数一同生成的密钥就不容易被猜出了,一个伪随机可能完全不随机,可是是三个伪随机就十分接近随机了,每增加一个自由度,随机性增加的可不是一。" + +此外,如果前一步,服务器要求客户端证书,客户端会在这一步发送证书及相关信息。 + +### 4.4 服务器的最后回应 + +服务器收到客户端的第三个随机数pre-master key之后,计算生成本次会话所用的"会话密钥"。然后,向客户端最后发送下面信息。 + +> 编码改变通知,表示随后的信息都将用双方商定的加密方法和密钥发送。 + +> 服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时也是前面发送的所有内容的hash值,用来供客户端校验。 + +至此,整个握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的HTTP协议,只不过用"会话密钥"加密内容。 + +## 五、参考链接 + +- MicroSoft TechNet, [SSL/TLS in Detail](http://technet.microsoft.com/en-us/library/cc785811(v=ws.10).aspx) +- Jeff Moser, [The First Few Milliseconds of an HTTPS Connection](http://www.moserware.com/2009/06/first-few-milliseconds-of-https.html) +- Wikipedia, [Transport Layer Security](http://en.wikipedia.org/wiki/Transport_Layer_Security) +- StackExchange, [How does SSL work?](http://security.stackexchange.com/questions/20803/how-does-ssl-work) + +(完) + +**《SSL/TLS协议运行机制的概述》 [原文链接](http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html)** \ No newline at end of file diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-architecture-01.png" "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-01.png" new file mode 100755 index 0000000..33d0563 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-01.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-architecture-02.png" "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-02.png" new file mode 100755 index 0000000..bd52da9 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-02.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-architecture-03.png" "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-03.png" new file mode 100755 index 0000000..1de6110 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-03.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-architecture-04.png" "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-04.png" new file mode 100755 index 0000000..7ff884b Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-04.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-architecture-05.png" "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-05.png" new file mode 100755 index 0000000..5283b9a Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-05.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-architecture-06.png" "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-06.png" new file mode 100755 index 0000000..2c6158d Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-06.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-architecture-07.png" "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-07.png" new file mode 100755 index 0000000..4886f6b Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-architecture-07.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-http-message.png" "b/docs/01.\347\275\221\347\273\234/assets/network-http-message.png" new file mode 100755 index 0000000..e1d813c Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-http-message.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-http-method-01.png" "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-01.png" new file mode 100755 index 0000000..391c22d Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-01.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-http-method-02.png" "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-02.png" new file mode 100755 index 0000000..9364178 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-02.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-http-method-03.png" "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-03.png" new file mode 100755 index 0000000..c3296b3 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-03.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-http-method-04.png" "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-04.png" new file mode 100755 index 0000000..0f22dee Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-04.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-http-method-05.png" "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-05.png" new file mode 100755 index 0000000..837e0f8 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-05.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-http-method-06.png" "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-06.png" new file mode 100755 index 0000000..5f57bb8 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-06.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-http-method-07.png" "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-07.png" new file mode 100755 index 0000000..fbf741c Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-07.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-http-method-08.png" "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-08.png" new file mode 100755 index 0000000..efda61d Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-http-method-08.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-http-method.png" "b/docs/01.\347\275\221\347\273\234/assets/network-http-method.png" new file mode 100755 index 0000000..220bfa4 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-http-method.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-https-01.png" "b/docs/01.\347\275\221\347\273\234/assets/network-https-01.png" new file mode 100755 index 0000000..b7a9c5a Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-https-01.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-https-02.png" "b/docs/01.\347\275\221\347\273\234/assets/network-https-02.png" new file mode 100755 index 0000000..15cf671 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-https-02.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-https-03.png" "b/docs/01.\347\275\221\347\273\234/assets/network-https-03.png" new file mode 100755 index 0000000..ef0a714 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-https-03.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-https-04.png" "b/docs/01.\347\275\221\347\273\234/assets/network-https-04.png" new file mode 100755 index 0000000..293eb11 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-https-04.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-https-05.png" "b/docs/01.\347\275\221\347\273\234/assets/network-https-05.png" new file mode 100755 index 0000000..83c1b01 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-https-05.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-https.png" "b/docs/01.\347\275\221\347\273\234/assets/network-https.png" new file mode 100755 index 0000000..4e1112b Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-https.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-01.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-01.png" new file mode 100755 index 0000000..d0f490d Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-01.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-02.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-02.png" new file mode 100755 index 0000000..cc7f634 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-02.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-03.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-03.png" new file mode 100755 index 0000000..54a72b6 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-03.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-04.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-04.png" new file mode 100755 index 0000000..4a9a4b9 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-04.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-05.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-05.png" new file mode 100755 index 0000000..da2dbaa Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-05.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-06.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-06.png" new file mode 100755 index 0000000..de18485 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-06.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-07.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-07.png" new file mode 100755 index 0000000..ebfbe7a Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-07.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-08.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-08.png" new file mode 100755 index 0000000..52254e8 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-08.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-09.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-09.png" new file mode 100755 index 0000000..1e774e5 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-09.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-10.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-10.png" new file mode 100755 index 0000000..acfab03 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-10.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-11.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-11.png" new file mode 100755 index 0000000..12b8842 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-11.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-12.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-12.png" new file mode 100755 index 0000000..20fe73d Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-12.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-13.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-13.png" new file mode 100755 index 0000000..3ffca77 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-13.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-14.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-14.png" new file mode 100755 index 0000000..88ed98b Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-14.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-15.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-15.png" new file mode 100755 index 0000000..3f32e40 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-15.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-16.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-16.png" new file mode 100755 index 0000000..a8e3267 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-16.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-17.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-17.png" new file mode 100755 index 0000000..2e9f46a Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-17.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-18.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-18.png" new file mode 100755 index 0000000..30d7b8c Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-18.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-tcp-19.png" "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-19.png" new file mode 100755 index 0000000..4585fc1 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-tcp-19.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-udp-01.png" "b/docs/01.\347\275\221\347\273\234/assets/network-udp-01.png" new file mode 100755 index 0000000..bce3af7 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-udp-01.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-udp-02.png" "b/docs/01.\347\275\221\347\273\234/assets/network-udp-02.png" new file mode 100755 index 0000000..2d0e8bc Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-udp-02.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-udp-03.png" "b/docs/01.\347\275\221\347\273\234/assets/network-udp-03.png" new file mode 100755 index 0000000..6215e34 Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-udp-03.png" differ diff --git "a/docs/01.\347\275\221\347\273\234/assets/network-websocket.png" "b/docs/01.\347\275\221\347\273\234/assets/network-websocket.png" new file mode 100755 index 0000000..51f1a5a Binary files /dev/null and "b/docs/01.\347\275\221\347\273\234/assets/network-websocket.png" differ diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.md" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.md" new file mode 100644 index 0000000..6a3523e --- /dev/null +++ "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225.md" @@ -0,0 +1,241 @@ +# 问题与简答 + +## 数据结构与算法篇 + +### 1. 概述 + +#### 解决问题的效率 + +解决问题方法的效率,跟`数据的组织方式`有关,跟`空间的利用效率`有关,也跟`算法的巧妙程度`有关 + +#### 抽象数据类型 + +`抽象数据类型`(Abstract Data Type,ADT)是一种对"数据类型"的描述,这种描述是"抽象"的 + +数据类型描述内容:一是`数据对象集`,二是`与数据集合相关联的操作集` + +#### 算法定义 + +算法是一个`有限指令集`,它接受一些输入,产生输出,并在一定的有限步骤之后终止 + +#### 算法复杂度 + +- 空间复杂度 S(n):根据算法写成的程序在执行时占用存储单元的长度 +- 时间复杂度 T(n):根据算法写成的程序在执行时耗时时间的长度 + +#### 分析算法效率 + +- 最坏情况的复杂度 Tworst(n) +- 平均复杂度 Tavg(n) + +拓展阅读 [《数据结构与算法概述》](./02.数据结构与算法/01.数据结构与算法概述.md) + +### 2. 实现基础 + +数据结构的处理方法是从这些具体应用中`抽象`出共性的数据组织与操作方式,进而采用某种具体的程序设计语言`实现`相应的数据存储与操作 + +#### 数据存储基础 + +- 数组 + +数组是最基本的构造类型,它是一组相同类型数据的有序集合 + +- 结构 + +结构类型是一种允许把一些数据分量聚合成一个整体的数据类型,它能够把有内在联系的不同类型的数据统一成一个整体,使它们相互关联 + +- 链表 + +链表是一种常见而重要的基础数据结构,也是实现复杂数据结构的重要手段 + +#### 流程控制基础 + +程序设计语言除了能够表达各种各样的数据外,还必须提供一种手段来表达数据处理的过程,即`程序的控制过程` + +按照结构化程序设计的观点,任何程序都可以将程序模块通过三种基本的控制结构进行组合来实现。这三种基本的控制结构是`顺序`、`分支`和`循环` + +拓展阅读 [《数据结构实现基础》](./02.数据结构与算法/02.数据结构实现基础.md) + +### 3. 线性结构 + +#### 线性表 + +线性表(Linear List)是由同一类型的数据元素构成的有序序列的线性结构 + +操作集:初始化、指定查找、查找、插入、删除、求表长 + +实现方式:顺序存储、链式存储 + +#### 堆栈 + +堆栈(Stack)可以认为是具有一定约束的线性表,插入和删除操作都作用在一个称为栈顶(Top)的端点位置 + +操作集:生成栈、判断是否满、压栈、判断是否空、出栈 + +实现方式:顺序存储、链式存储 + +#### 队列 + +队列(Queue)是一个有序线性表,队列的插入和删除操作分别是在线性表的两个不同的端点进行 + +操作集:生成队列、判断是否满、压入队列、判断是否为空,移除队列 + +实现方式:顺序存储、链式存储 + +### 4. 树 + +树(Tree)是一种十分重要且广泛应用的非线性数据结构 + +#### 二叉树 + +五种基本形态:空二叉树、只有根节点的二叉树、只有根节点和左子树TL的二叉树、只有根节点和右子树TR的二叉树、具有根节点、左子树TL和右子树TR的二叉树 + +其它二叉树:斜二叉树、满二叉树、完美二叉树 + +实现方式:顺序存储、链式存储 + +操作集:创建二叉树、判断是否为空、遍历(先序遍历、中序遍历、后序遍历、层序遍历) + +#### 二叉搜索树 + +二叉搜索树(Binary Search Tree)是一种对排序和查找都很有用的特殊二叉树 + +定义:左子树 < 根节点 < 右子树 + +实现方式:一般用链表实现 + +操作集:创建二叉树、判断是否为空、遍历、查找、查找最小元素、查找最大元素、插入、删除 + +时间复杂度:最好 O(logN) 最差 O(N) + +#### 平衡二叉树 + +平衡二叉树(Balanced Binary Tree)又称为 AVL 树,AVL 树的插入、删除、查找操作均可在O(logN)时间内完成 + +定义:任一结点的左、右子树均为 AVL 树;根节点左、右子树高度差的绝对值不超过1 + +平衡二叉树的调整:单旋调整、双旋调整 + +#### 树的应用 + +堆及其操作、哈夫曼树、集合及其运算 + +### 5. 散列查找 + +符号表(SymbolTable)是名字(Name)-属性(Attribute)对的集合,符号表最核心的操作是查找、插入和删除 + +操作集:创建符号表、查找指定名字是否存在、获取指定名字对应属性、更改指定名字对应属性、插入新名字及其属性、删除名字及其属性 + +使用散列技术实现符号表的操作集,符号表也叫做`散列表`(Hash Table,即哈希表),散列(Hashing)是一种重要的查找方法 + +散列函数(哈希函数):在查找数据时,由函数 h 对给定值 key 计算出地址,将 key 与该地址单元中数据对象关键字进行比较,确定查找是否成功。散列法又称为"关键字-地址转换法" + +关键字分类:一般把关键字分为`数字型关键字`和`字符串型关键字` + +#### 数字型关键字的散列构造 + +- 直接定址法 + +h(key) = a x key + b (a、b为常数) + +- 除留余数法 + +h(key) = key mod p + +- 数字分析法 + +h(key) = atoi(key + 7) + +#### 字符串型关键字的散列构造 + +- ASCII 码加和法 + +h(key) = (Σkey[i]) mode TableSize + +#### 冲突处理 + +- 开放地址法 + +开放地址法就是一旦产生了冲突,即该地址已经存放了其它数据元素,就去寻找另一个空的散列地址 + +- 链地址法 + +链地址法是将所有关键词为同义词的数据对象通过结点链接存储在同一个单链表中 + +- 影响冲突的因素 + +散列函数是否均匀、处理冲突的方法、散列表的装填因子 α + +### 6. 图 + +图的结构是任意两个数据对象之前都可能存在某种特定关系的数据结构 + +### 7. 排序 + +没有一种排序算法在任何情况下都是最优的,必须根据实际情况选择最优的算法来解决问题 + +算法稳定性:在一组待排序记录中,如果存在任意两个相等的记录 R 和 S,且在待排序记录中 R 在 S 前,如果在排序后 R 依然在 S 前,即它们的前后位置在排序前后不发生改变,则称为排序算法为稳定的 + +#### 选择排序 + +- 简单选择排序 + +简单选择排序(Simple Selection Sort)是一种直观的排序算法,在未排序的序列中,选出最小的元素和序列的首位元素交换,接下来在剩下的未排序序列中再选出最小元素与序列的第二位元素交换,依次类推,最后形成从小到大的已排序序列 + +时间复杂度:O(N2) + +- 堆排序 + +将无序的序列生成一个最大堆,将堆顶元素与最后一个元素对换位置,将剩下元素生成最大堆,依次进行元素交换并生成最大堆 + +时间复杂度:O(NlogN) +空间复杂度:O(1) + +#### 插入排序 + +- 简单插入排序 + +将待排序的一组序列分为已排好序和未排序的两个部分,初始状态时,已排序序列仅包含第一个元素,未排序序列中的元素为除了第一个以外N-1个元素;此后将未排序序列中的元素逐一插入到已排序的序列中。如此往复,经过N-1次插入后,未排序序列中元素个数为0,则排序完成 + +时间复杂度:O(N2) 稳定排序 + +- 希尔排序 + +将待排序的一组元素按一定间隔分为若干个序列,分别进行插入排序。开始时设置的"间隔"较大,在每轮排序中将间隔逐步减小,直到"间隔"为1,也就是最后一步是进行简单插入排序 + +时间复杂度:和增量序列的选取有关 非稳定排序 + +#### 交换排序 + +- 冒泡排序 + +对元素个数为 N 的待排序序列进行排序时,共进行N-1次循环。在第 k 次循环中,对从第1到第N-k个元素从前往后进行比较,每次比较相邻的两个元素,若前一个元素大于后一个元素,则两者互换位置,否则保持位置不变 + +时间复杂度:O(N2) + +- 快速排序 + +将未排序元素根据一个作为基准的"主元"分为两个子序列,其中一个子序列的记录均大于主元,而另一个子序列均小于主元,然后递归地对这两个子序列用类似的方法进行排序 + +时间复杂度:O(Nlog2N) + +#### 归并排序 + +将大小为 N 的序列看成 N 个长度为1的子序列,接下来将相邻子序列两两进行归并操作,形成N/2(+1)个长度为2(或1)的有序子序列;然后再继续进行相邻子序列两两归并操作,如果一直循环,直到剩下1个长度为 N 的序列,则该序列为原序列完成排序后的结果 + +时间复杂度:O(Nlog2N) +空间复杂度:O(N) + +#### 基数排序 + +- 桶排序 + +如果已知 N 个关键字的取值范围是在 0 到 M-1 之间,而 M 比 N 小的多,则桶排序算法将关键字的每个取值建立一个"桶",即建立 M 个桶,在扫描 N 个关键字时,将每个关键字放入相应的桶中,然后按桶的顺序收集一遍就自然有序了 + +- 基数排序 + +基数排序是桶排序的一种推广,它所考虑的待排记录包含不止一个关键字 + +### 8. 补充 + +### 9. 经典算法题 \ No newline at end of file diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/01.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225\346\246\202\350\277\260.md" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/01.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225\346\246\202\350\277\260.md" new file mode 100644 index 0000000..34c072f --- /dev/null +++ "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/01.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225\346\246\202\350\277\260.md" @@ -0,0 +1,132 @@ +# 数据结构与算法概述 + +## 引子 + +什么是数据结构?如果翻阅不同的教材,可以看到五花八门的描述。事实上,这个问题在计算机科学界至今没有标准的定义。 + +```text +在计算机科学中,数据结构(英语:data structure)是计算机中存储、组织数据的方式(维基百科) +``` + +### 思考问题 + +#### 书籍摆放 + +问题:如果你是书店的主人,该如何摆放你的书籍,才能让顾客最快捷的找到想要的书籍? + +- 方法1:随便放 + +> 放书非常方便,有新书直接插到空位。但是查找很不方便,如果没有这本书,需要翻遍整个书架 + +- 方法2:按照书名的拼音字母顺序排放 + +> 新书插入需要给新书腾出空间,造成图书需要向后移动 + +- 方法3:把书架分成几块区域,每块区域指定摆放某种类别的图书;在每种类别内,按照书名的拼音字母顺序排放 + +> 查找和插入工作量都减少很多,但是无法预估每种类别的图书会有多少,容易造成空间的浪费 + +#### 数字打印 + +问题:写程序实现一个函数 PrintN,使得传入一个正整数为 N 的参数,能顺序打印从 1 到 N 的全部正整数 + +```php +//版本一 +function PrintN($n) +{ + for ($i=1; $i <= $n; $i++) { + echo "{$i}\n"; + } +} +//版本二 +function PrintN($n) +{ + if ($n > 0) { + PrintN($n-1); + echo "{$n}\n"; + } +} +``` + +> 当输入 N 为 100、1000、10000...,N 变的越来越大时候,实现版本一和版本二有什么区别?(debug_backtrace) + +#### 一元多项式计算 + +问题:一元多项式的标准表达式可以写成:f(x) = $a_0$ + $a_1$x + ... + $a_{n-1}$$x^{n-1}$ + $a_n$$x^n$。现给定一个多项式的阶数 n,并将全体系数 $\{a_i\}^n_{i=0}$ 存放在数组 a[] 里。请写程序计算这个多项式在给定点 x 处的值 + +```php +function f($n, $a, $x) +{ + $p = $a[0]; + for ($i=1; $i <= $n; $i++) { + $p += $a[$i] * pow($x, $i); + } + return $p; +} +$x = 2; $n = 1; +$a = []; +for ($i=0; $i <= $n; $i++) { + $a[$i] = $i+1; +} +$fn = f($n, $a, $x); +echo $fn . "\n"; +``` + +通过提公因式 x 减少乘法的运算次数,把多项式改写为: + +> f(x) = $a_0$ + x($a_1$ + x(...($a_{n-1}$ + x($a_n$))...)) + +```php +function f2($n, $a, $x) +{ + $p = $a[$n]; + for ($i=$n; $i > 0 ; $i--) { + $p = $a[$i-1] + $x * $p; + } + return $p; +} +``` + +### 解决问题的效率 + +解决一个非常简单的问题,往往也有多种方法,且不同方法之间的效率可能相差甚远。解决问题方法的效率,跟`数据的组织方式`有关,跟`空间的利用效率`有关,也跟`算法的巧妙程度`有关。 + +## 数据结构 + +### 定义 + +- 数据结构的定义,首先应该包含数据对象在计算机中的组织方式——这类似于图书的摆放方法。并且,数据对象必定与一系列加在数据对象上的操作相关联,就如我们在书架上摆放图书是为了能找得到想要的书,或者是插入一本新买的书。 + +- 在讨论数据结构的时候,关心的是`数据对象`本身以及它们在计算机中的`组织方式`,还要关心与它们相关联的`操作集`,以及实现这些操作的最高效的算法。 + +- 关于数据对象在计算机中的组织方式,包含两个概念:数据对象集的逻辑结构、数据对象集在计算机中的物理存储结构 + +### 抽象数据类型 + +- 抽象数据类型(Abstract Data Type)是一种对"数据结构"的描述,这种描述是"抽象"的。数据类型描述内容:数据对象集、与数据集合相关联的操作集。 + +- 抽象:描述数据类型的方法不依赖于具体实现,即数据对象集合操作集的描述与存放数据的机器无关、与数据存储的物理结构无关、与实现操作的算法和编程语言均无关。抽象是计算机求解问题的基本方式和重要手段,使得一种设计可以应用于多种场景。 + +## 算法 + +### 定义 + +> 算法(algorithm)自于9世纪波斯数学家,在数学上提出了算法这个概念。 + +算法是一个`有限指令集`,它接受一些输入(非必须),产生输出,并一定在有限步骤之后终止。 + +> 算法不是程序,算法比程序更抽象,强调表现做什么,忽略细节性怎么做。这样的好处是使整体思路清晰易懂,形成模块化的风格。 + +### 算法复杂度 + +衡量、比较算法的指标主要有以下两个: + +- 空间复杂度 S(n):根据算法写成的程序在执行时占用存储单元的长度 +- 时间复杂度 T(n):根据算法写成的程序在执行时耗时时间的长度 + +分析一般算法效率: + +- 最坏情况复杂度 $T_{worst}$(n) +- 平均复杂度 $T_{avg}$(n) + +**《数据结构与算法概述》 原文链接:[https://blog.maplemark.cn/2019/07/数据结构与算法概述.html](https://blog.maplemark.cn/2019/07/数据结构与算法概述.html)** \ No newline at end of file diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/02.\346\225\260\346\215\256\347\273\223\346\236\204\345\256\236\347\216\260\345\237\272\347\241\200.md" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/02.\346\225\260\346\215\256\347\273\223\346\236\204\345\256\236\347\216\260\345\237\272\347\241\200.md" new file mode 100644 index 0000000..e5f6451 --- /dev/null +++ "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/02.\346\225\260\346\215\256\347\273\223\346\236\204\345\256\236\347\216\260\345\237\272\347\241\200.md" @@ -0,0 +1,170 @@ +# 数据结构实现基础 + +## 引子 + +### 数据统计 + +例子:在日常数据处理中,经常碰到需要对一组数据进行基本的统计分析,包含这些操作:平均数、最大值、最小值、中位数、标准差、方差等。这类统计可能发生在各种情况,比如学生成绩统计、家庭开支情况、GDP 统计等等,都会涉及到这类数据统计。 + +为每个具体应用都编写一个程序不会是一个好方法,程序都具有很大的相似性。数据结构的处理方法是从这些具体应用中`抽象出共性的数据组织与操作方法`,进而采用某种具体的程序设计语言`实现相应的数据存储与操作` + +数据抽象 + +- 类型名称:统计数据集 +- 数据对象集:N 个元素 {x1, x2, ... , xN} 的集合 S +- 操作集: +1. ElementType Average(S, N):求 S 中 N 个元素的平均值 +2. ElementType Max(S, N):求 S 中 N 个元素的最大值 +3. ElementType Min(S, N):求 S 中 N 个元素的最小值 +4. ElementType Median(S, N):求 S 中 N 个元素的中位数 + +### 数据存储 + +数据组织的基本存储方式主要是利用数组和链表方式来实现的,包括很复杂的数据结构,如图、树,也都不外乎应用数组和链表来实现 + +- 若要实现的操作不是基本统计,而是集合运算,需要判断元素是否属于集合、对集合进行并和交运算、元素插入集合等。这些操作虽然在简单数组也可以实现,但是效率不高,使用树的组织方式可以更方便的实现集合的上述运算。 +- 若除了基本的统计操作外,还需要动态的维护一个集合,即经常往集合里加入/删除元素,那应该设计多大的数组来保存这些元素呢,太大浪费空间,太小不够用。使用链表来保存数据或许更适合,但是链表也有缺点,链表需要记录后续节点地址,跟数组存储相比,链表需要更多的存储空间,同时程序实现也比数组更加复杂。 + +数据结构的存储实现跟所需要的操作密切相关,`没有最好的存储方式,只有最合适的存储方式`。 + +### 操作实现 + +- 在确定数据的存储方式后,数据结构涉及的另一个问题是相关的操作如何实现。这些操作的实现需要利用程序设计语言提供的另一个功能,即`流程设计功能`。 + +- 在任何高级程序设计语言都提供了一种的基本流程控制语句,即分支控制语句和循环控制语句。分支控制结构、循环控制结构加上程序自然的语句顺序执行结构,是实现任何算法流程的基本结构。 + +- 在程序中,我们可以将程序的某个基本功能设计为函数,这一方面降低了程序设计的复杂性,另一方面也提高了程序设计的重用性。递归是数据结构算法设计的很重要的手段。 + +## 数据结构存储基础 + +变量是数据存储的基本单位,而变量是有类型的,例如:整型、浮点型、字符型、布尔型 + +### 数组 + +数组是最基本的构造类型,它是一组相同类型数据的有序集合 + +### 指针 + +指针变量用于存放变量的地址,通过指针就能间接访问那个变量 + +### 结构体 + +结构类型是一种允许把一些数据分量聚合成一个整体的数据类型,它能够把有内在联系的不同类型的数据统一成一个整体,使它们相互关联。同时,结构又是一个变量的集合,可以按照与成员类型变量相同的操作方法单独使用其变量成员。结构与数组的区别在于,数组的所有元素必须是相同类型的,而结构的成员可以是不同的数据类型。 + +```text +struct 结构名 { + 类型名 结构成员名 1; + 类型名 结构成员名 2; + ...... + 类型名 结构成员名 n; +}; +``` + +### 链表 + +链表使一种常见而重要的基础数据结构,也是实现复杂数据结构的重要手段。它不按照线性的顺序存储数据,而是由若干个同一结构类型的"结点"依次串联而成的,即每一个结点里保存着下一个结点的地址。使用链表结构可以克服数据需要预先知道数据大小的缺点,可以充分利用计算机内存空间,实现灵活的内存动态管理。但链表失去了数组方便随机存储的优点,同时链表由于增加了结点的指针域,空间开销比较大。 + +### 单向链表 + +#### 单向链表的结构 + +![单向链表的组成示意图](./assets/DSA-single-link.png) + +```php +//单向链表结点 +class node +{ + public $data; + public $next; + + /** + * @param $p1 结点数据 + * @param $p2 下一个结点 + */ + public function __construct($p1, $p2) + { + $this->data = $p1; + $this->next = $p2; + } +} +``` + +#### 单向链表的常见操作 + +- 链表的建立 + +应用链表进行程序设计时,往往需要先建立一个链表,建立链表的过程实际上就是不断在链表中插入结点的过程 + +```php +class singleLinkList +{ + /** + * @param $n int 结点数目 + * @return $head obj 头结点 + */ + public function create($n) + { + $head = new node(0, null); + for ($i=$n; $i > 0; $i--) { + $newNode = new node($i, null); + $newNode->next = $head->next; + $head->next = $newNode; + } + return $head; + } +} +``` + +- 插入结点 + +在单向链表 head 的某个结点 p 之后插入一新结点:找到正确位置 p,申请新结点 t 并对 t 的结点信息赋值,最后将 t 插入在 p 之后 + +- 删除结点 + +从单向链表 head 中删除一个结点:找到被删除结点的前面一个结点 p,删除 p 之后的结点 + +- 单向链表的遍历 + +对单向链表最常见的处理方式:逐个查看链表中每个结点的数据并进行处理 + +### 双向链表 + +![双向链表](./assets/DSA-double-link.png) + +在单向链表基础上增加指向前驱单元指针的链表叫做`双向链表`。结点增加指向其前驱结点的指针,将牺牲一部分空间代价,前驱单元查找可以不必从链头开始查找 + +```php +//双向链表结点 +class node +{ + public $data; + public $next; + public $previous; + + /** + * @param $p1 结点数据 + * @param $p2 下一个结点 + * @param $p3 前一个结点 + */ + public function __construct($p1, $p2, $p3) + { + $this->data = $p1; + $this->next = $p2; + $this->previous = $p3; + } +} +``` + +### 双向循环链表 + +![双向循环链表](./assets/DSA-double-link2.png) + +将双向链表最后一个单元的 Next 指针指向链表的第一个单元,而第一个单元的 Previous 指针指向链表的最后一个单元,这样构成的链表称为`双向循环链表` + +## 流程控制基础 + +程序设计语言除了能表达各种各样的数据外,还必须提供一种手段来表达数据处理的过程,即`程序的控制过程`。程序的控制过程通过程序中的一系列语句来实现。 + +按照结构化程序设计的观点,任何程序都可以将程序模块通过三种基本的控制结构进行组合来实现。这三种基本的控制结构是`顺序`、`分支`、`循环`。 + +**《数据结构实现基础》 原文链接:[https://blog.maplemark.cn/2019/07/数据结构实现基础.html](https://blog.maplemark.cn/2019/07/数据结构实现基础.html)** \ No newline at end of file diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/03.\347\272\277\346\200\247\347\273\223\346\236\204.md" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/03.\347\272\277\346\200\247\347\273\223\346\236\204.md" new file mode 100644 index 0000000..2d8e6aa --- /dev/null +++ "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/03.\347\272\277\346\200\247\347\273\223\346\236\204.md" @@ -0,0 +1,156 @@ +# 线性结构 + +在数据的逻辑结构中,有种常见而且简单的结构是`线性结构`,即数据元素之间构成一个`有序`的序列。 + +## 线性表的定义与实现 + +### 线性表的定义 + +线性表(Linear List)是由同一类型的数据元素构成的有序序列的线性结构。线性表中的元素的个数称为线性表的长度;当线性表中没有元素时,称为空表;表的起始位置称为表头;表的结束位置称为表尾。 + +类型名称:线性表(List) + +操作集: +- 1.MakeEmpty:初始化新的空线性表 +- 2.FindKth:根据指定的位序,返回相应元素 +- 3.Find:查找特定元素 +- 4.Insert:指定位序,插入元素 +- 5.Delete:删除指定位序的元素 +- 6.Length:返回线性表的长度 + +### 线性表的顺序存储实现 + +线性表的顺序存储是指在内存中用地址连续的一块存储空间顺序存放线性表的各元素。考虑到线性表的运算有插入、删除等,即表的长度是动态可变的,因此,数组的容量需要设计得足够大。 + +![线性表的顺序存储](./assets/DSA-线性表的顺序存储示意.png) + +> MAXSIZE:根据实际问题定义的足够大的整数 + +> Last:记录当前线性表中最后一个元素在数组中的位置 + +#### 初始化 + +顺序表的初始化即构造一个空表。首先动态分配表结构所需要的存储空间,然后将表中的指针置为空,表示表中没有数据元素。 + +#### 查找 + +顺序存储的线性表中,查找主要是指在线性表中查找给定值相同的数据元素。由于线性表的元素都存储在数组中,所以这个查找过程实际上就是在数组的顺序查找。查找的平均时间复杂度为O(n)。 + +#### 插入 + +顺序表的插入是指在表的第 i 个位序上插入一个值为 X 的新元素。首先将位序之后的元素向后移动,为新元素让出位置;将新元素置入;修改表长度。 + +#### 删除 + +顺序表的删除是指将表中指定位序的元素从线性表中去掉,删除后并改变表长度。将指定位序的元素删除,并将位序之后的元素向前移动;修改表长度。 + +> 由于顺序表的存储特点是用物理上的相邻实现了逻辑上的相邻,它要求用连续的存储单元顺序存储线性表中的各元素,因此,对顺序表插入、删除时需要通过移动数据元素来实现,运行效率影响较大。 + +### 线性表的链式存储实现 + +使用链表结构可以克服数组表示线性表的缺陷。下图为单向链表的图示表示形式,它有 n 个数据单元,每个数据单元由数据域和链接域两部分组成。数据域用来存放数值,链接域是线性表数据单元的结构指针。 + +![线性表的链表表示](./assets/DSA-线性表的链表表示.png) + +```php +//结点的结构定义 +class node +{ + public $data; + public $next; + + /** + * @param $p1 结点数据 + * @param $p2 下一个结点 + */ + public function __construct($p1, $p2) + { + $this->data = $p1; + $this->next = $p2; + } +} +``` + +#### 求表长 + +从链表的第一个元素起,从头到尾遍历一遍。 + +#### 查找 + +线性表的查找有两种:按序号查找、按值查找 + +- 按序号查找 + +从链表的第一个元素起,判断结点序号是否相同,相同则返回结点值,不同则继续,没有则返回错误信息。 + +- 按值查找 + +从头到尾遍历,知道找到为止;从链表的第一个元素结点起,判断当前结点的值是否相等;若是,则返回结点位置,否则继续,知道表结束为止;找不到则返回错误信息。 + +#### 插入 + +线性表的插入是在指定位序前插入一个新元素。在插入位序为1是,代表插入到链表的头;当位序为表尾时,代表插入到链表最后。新增节点,节点值为新元素值,节点指向指针地址根据位序赋予,链表保持。 + +#### 删除 + +单向链表中删除指定位序元素,首先需要找到被删除结点的前一个元素,然后再删除结点并释放空间。 + +## 堆栈 + +`堆栈`(Stack)是具有一定约束的线性表,插入和删除操作都作用在一个称为栈顶(Top)的端点位置。 + +![堆栈](./assets/DSA-stack.png) + +### 表达式求值 + +表达式求值是程序设计语言编译中的一个基本问题,即编译程序要将源程序中描述的表达式转换为正确的机器指令序列或直接求出常量表达式的值。要实现表达式求值,首先需要理解一个表达式,主要是运算的先后顺序。 + +### 抽象数据类型 + +类型名称:堆栈(Stack) +数据对象集:一个有0个或多个元素的有穷线性表 +操作集: +- 1.CreateStack:生成空堆栈 +- 2.IsFull:判断堆栈是否已满 +- 3.Push:将元素压入堆栈 +- 4.IsEmpty:判断堆栈是否为空 +- 5.Pop:删除并返回栈顶元素 + +### 堆栈的实现 + +由于栈是线性表,因而栈的存储结构可采用顺序和链式两种形式。顺序存储的栈称为顺序栈,链式存储的栈称为链栈。 + +#### 栈的顺序存储实现 + +栈的顺序存储结构通常由一个一维数组和一个记录栈顶元素位置的变量组成,另外我们还可以用一个变量来存储堆栈的最大容量,这样方便判断什么时候堆栈是满的。 + +#### 栈的链式存储实现 + +栈的链式存储结构(链栈)与单链表类似,但其操作受限制,插入和删除操作只能在链栈的栈顶进行。栈顶指针就是链表的头指针。 + +### 堆栈应用:表达式求值 + +## 队列 + +`队列`(Queue)是一个有序线性表,队列的插入和删除操作分别在线性表的两个不同端点进行的。先进先出特点。 + +### 队列的定义 + +类型名称:队列(Queue) +数据对象集:一个有0个或多个元素的又穷线性表 +操作集: +- 1.CreateQueue:生成空队列 +- 2.IsFull:判断队列是否已满 +- 3.AddQ:将新元素压入队列 +- 4.IsEmpty:判断队列是否为空 +- 5.DeleteQ:删除并返回队列头元素 + +### 队列的实现 + +#### 队列的顺序存储实现 + +队列最简单的表示方法是用数组。用数组存储队列有许多种具体的方法。一般可以选择将队列头放数组下标小的位置,而将队列尾放在数组下标大的位置,并用两个变量分别指示队列的头和尾。 + +#### 队列的链式存储实现 + +队列与堆栈一样,也可以采用链式存储结构,但队列的头必须指向链表的头结点,队列的尾指向链表的尾结点 \ No newline at end of file diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/04.\346\240\221.md" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/04.\346\240\221.md" new file mode 100644 index 0000000..2a3ec10 --- /dev/null +++ "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/04.\346\240\221.md" @@ -0,0 +1,49 @@ +# 树 + +在计算机科学中,`树`(Tree)是一种抽象数据类型(ADT)或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。树是一种十分重要的`非线性数据结构`。 + +## 树的定义、表示和术语 + +树是 n(n≥0)个结点构成的有限集合。当 n=0 时,称为空树。对于任意非空树(n>0),具备如下特点。 + +- 每个节点都只有有限个子节点或无子节点 +- 没有父节点的节点称为根节点 +- 每一个非根节点有且只有一个父节点 +- 除了根节点外,每个子节点可以分为多个不相交的子树 +- 树里面没有环路(cycle) + +## 二叉树 + +### 二叉树的定义 + +在计算机科学中,`二叉树`(Binary tree)是每个节点最多只有两个分支的树结构。通常分支被称作"左子树"或"右子树"。二叉树的分支具有左右次序,不能随意颠倒。 + +![二叉树五种基本形态]() + +### 二叉树的性质 + +### 二叉树的存储结构 + +### 二叉树的操作 + +## 二叉搜索树 + +### 二叉搜索树的定义 + +### 二叉搜索树的动态查找 + +### 二叉搜索树的插入 + +### 二叉搜索树的删除 + +## 平衡二叉树 + +### 平衡二叉树的定义 + +### 平衡二叉树的调整 + +## 树的应用 + +### 堆及其操作 + +### 哈夫曼树 \ No newline at end of file diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/05.\346\225\243\345\210\227\346\237\245\346\211\276.md" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/05.\346\225\243\345\210\227\346\237\245\346\211\276.md" new file mode 100644 index 0000000..e69de29 diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/06.\345\233\276.md" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/06.\345\233\276.md" new file mode 100644 index 0000000..e69de29 diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/07.\346\216\222\345\272\217.md" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/07.\346\216\222\345\272\217.md" new file mode 100644 index 0000000..e69de29 diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/08.\347\256\227\346\263\225\350\241\245\345\205\205.md" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/08.\347\256\227\346\263\225\350\241\245\345\205\205.md" new file mode 100644 index 0000000..e69de29 diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/09.\347\273\217\345\205\270\347\256\227\346\263\225\351\242\230.md" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/09.\347\273\217\345\205\270\347\256\227\346\263\225\351\242\230.md" new file mode 100644 index 0000000..d75ea41 --- /dev/null +++ "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/09.\347\273\217\345\205\270\347\256\227\346\263\225\351\242\230.md" @@ -0,0 +1,3 @@ +# 经典算法题 + +## 1 \ No newline at end of file diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-double-link.png" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-double-link.png" new file mode 100644 index 0000000..38297d7 Binary files /dev/null and "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-double-link.png" differ diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-double-link2.png" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-double-link2.png" new file mode 100644 index 0000000..95e7480 Binary files /dev/null and "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-double-link2.png" differ diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-single-link.png" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-single-link.png" new file mode 100644 index 0000000..74e09c0 Binary files /dev/null and "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-single-link.png" differ diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-stack.png" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-stack.png" new file mode 100644 index 0000000..cb89c11 Binary files /dev/null and "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-stack.png" differ diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-\347\272\277\346\200\247\350\241\250\347\232\204\351\223\276\350\241\250\350\241\250\347\244\272.png" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-\347\272\277\346\200\247\350\241\250\347\232\204\351\223\276\350\241\250\350\241\250\347\244\272.png" new file mode 100644 index 0000000..2490f47 Binary files /dev/null and "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-\347\272\277\346\200\247\350\241\250\347\232\204\351\223\276\350\241\250\350\241\250\347\244\272.png" differ diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-\347\272\277\346\200\247\350\241\250\347\232\204\351\241\272\345\272\217\345\255\230\345\202\250\347\244\272\346\204\217.png" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-\347\272\277\346\200\247\350\241\250\347\232\204\351\241\272\345\272\217\345\255\230\345\202\250\347\244\272\346\204\217.png" new file mode 100644 index 0000000..cec5bb6 Binary files /dev/null and "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/DSA-\347\272\277\346\200\247\350\241\250\347\232\204\351\241\272\345\272\217\345\255\230\345\202\250\347\244\272\346\204\217.png" differ diff --git "a/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/\346\225\260\346\215\256\347\273\223\346\236\204.sketch" "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/\346\225\260\346\215\256\347\273\223\346\236\204.sketch" new file mode 100644 index 0000000..436eb1e Binary files /dev/null and "b/docs/02.\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/assets/\346\225\260\346\215\256\347\273\223\346\236\204.sketch" differ diff --git "a/docs/03.PHP/01.PHP\346\225\260\347\273\204.md" "b/docs/03.PHP/01.PHP\346\225\260\347\273\204.md" new file mode 100644 index 0000000..a3b88dd --- /dev/null +++ "b/docs/03.PHP/01.PHP\346\225\260\347\273\204.md" @@ -0,0 +1,258 @@ +# PHP 数组 + +## 简介 + +这些函数允许你通过不同的方式来使用和操作数组。数组是存储、管理和操作变量组的必不可少的工具。 + +PHP 支持简单数组和多维数组,数组可由用户自己创建也可以由其它函数创建。有很多特殊的数据库处理函数可以从数据库查询中返回数组以及一些返回数组的函数。 + +## 预定义常量 + +下列常量作为 PHP 核心的一部分总是可用的。 + +CASE_LOWER (integer) + +> CASE_LOWER 用在 array_change_key_case() 中将数组的键名转换成小写字母。这也是 array_change_key_case() 的默认值。 + +CASE_UPPER (integer) + +> CASE_UPPER 用在 array_change_key_case() 中将数组的键名转换成大写字母。 +排序顺序标识: + +SORT_ASC (integer) + +> SORT_ASC 用在 array_multisort() 函数中,使其升序排列。 + +SORT_DESC (integer) + +> SORT_DESC 用在 array_multisort() 函数中,使其降序排列。 + +- 排序类型标识:用于各种排序函数 + +SORT_REGULAR (integer) + +> SORT_REGULAR 用于对对象进行通常比较。 + +SORT_NUMERIC (integer) + +> SORT_NUMERIC 用于对对象进行数值比较。 + +SORT_STRING (integer) + +> SORT_STRING 用于对对象进行字符串比较。 + +SORT_LOCALE_STRING (integer) + +> SORT_LOCALE_STRING 基于当前区域来对对象进行字符串比较。PHP 4.4.0 和 5.0.2 新加。 + +COUNT_NORMAL (integer) + +COUNT_RECURSIVE (integer) + +EXTR_OVERWRITE (integer) + +EXTR_SKIP (integer) + +EXTR_PREFIX_SAME (integer) + +EXTR_PREFIX_ALL (integer) + +EXTR_PREFIX_INVALID (integer) + +EXTR_PREFIX_IF_EXISTS (integer) + +EXTR_IF_EXISTS (integer) + +EXTR_REFS (integer) + +## 对数组进行排序 + +PHP 有一些用来排序数组的函数 + +主要区别有: + +- 有些函数基于 array 的键来排序, 而其他的基于值来排序的:$array['key'] = 'value';。 +- 排序之后键和值之间的关联关系是否能够保持, 是指排序之后数组的键可能 会被重置为数字型的(0,1,2 ...)。 +- 排序的顺序有:字母表顺序, 由低到高(升序), 由高到低(降序),数字排序,自然排序,随机顺序或者用户自定义排序。 +- 注意:下列的所有排序函数都是直接作用于数组本身, 而不是返回一个新的有序的数组。 +- 以下函数对于数组中相等的元素,它们在排序后的顺序是未定义的。 (也即相等元素之间的顺序是不稳定的)。 + +|函数名称|排序依据|数组索引健保持|排序的顺序|相关函数| +|-|-|-|-|-| +|array_multisort()|值|键值关联的保持,数字类型的不保持|第一个数组或者由选项指定|array_walk()| +|asort()|值|是|由低到高|arsort()| +|arsort()|值|是|由高到低|asort()| +|krsort()|键|是|由高到低|ksort()| +|ksort()|键|是|由低到高|asort()| +|natcasesort()|值|是|自然排序,大小写不敏感|natsort()| +|natsort()|值|是|自然排序|natcasesort()| +|rsort()|值|否|由高到低|sort()| +|shuffle()|值|否|随机|array_rand()| +|sort()|值|否|由低到高|rsort()| +|uasort()|值|是|由用户定义|uksort()| +|uksort()|键|是|由用户定义|uasort()| +|usort()|值|否|由用户定义|uasort()| + +## 数组函数 + +array_change_key_case — 将数组中的所有键名修改为全大写或小写 + +array_chunk — 将一个数组分割成多个 + +array_column — 返回数组中指定的一列 + +array_combine — 创建一个数组,用一个数组的值作为其键名,另一个数组的值作为其值 + +array_count_values — 统计数组中所有的值 + +array_diff_assoc — 带索引检查计算数组的差集 + +array_diff_key — 使用键名比较计算数组的差集 + +array_diff_uassoc — 用用户提供的回调函数做索引检查来计算数组的差集 + +array_diff_ukey — 用回调函数对键名比较计算数组的差集 + +array_diff — 计算数组的差集 + +array_fill_keys — 使用指定的键和值填充数组 + +array_fill — 用给定的值填充数组 + +array_filter — 用回调函数过滤数组中的单元 + +array_flip — 交换数组中的键和值 + +array_intersect_assoc — 带索引检查计算数组的交集 + +array_intersect_key — 使用键名比较计算数组的交集 + +array_intersect_uassoc — 带索引检查计算数组的交集,用回调函数比较索引 + +array_intersect_ukey — 用回调函数比较键名来计算数组的交集 + +array_intersect — 计算数组的交集 + +array_key_exists — 检查数组里是否有指定的键名或索引 + +array_key_first — Gets the first key of an array + +array_key_last — Gets the last key of an array + +array_keys — 返回数组中部分的或所有的键名 + +array_map — 为数组的每个元素应用回调函数 + +array_merge_recursive — 递归地合并一个或多个数组 + +array_merge — 合并一个或多个数组 + +array_multisort — 对多个数组或多维数组进行排序 + +array_pad — 以指定长度将一个值填充进数组 + +array_pop — 弹出数组最后一个单元(出栈) + +array_product — 计算数组中所有值的乘积 + +array_push — 将一个或多个单元压入数组的末尾(入栈) + +array_rand — 从数组中随机取出一个或多个单元 + +array_reduce — 用回调函数迭代地将数组简化为单一的值 + +array_replace_recursive — 使用传递的数组递归替换第一个数组的元素 + +array_replace — 使用传递的数组替换第一个数组的元素 + +array_reverse — 返回单元顺序相反的数组 + +array_search — 在数组中搜索给定的值,如果成功则返回首个相应的键名 + +array_shift — 将数组开头的单元移出数组 + +array_slice — 从数组中取出一段 + +array_splice — 去掉数组中的某一部分并用其它值取代 + +array_sum — 对数组中所有值求和 + +array_udiff_assoc — 带索引检查计算数组的差集,用回调函数比较数据 + +array_udiff_uassoc — 带索引检查计算数组的差集,用回调函数比较数据和索引 + +array_udiff — 用回调函数比较数据来计算数组的差集 + +array_uintersect_assoc — 带索引检查计算数组的交集,用回调函数比较数据 + +array_uintersect_uassoc — 带索引检查计算数组的交集,用单独的回调函数比较数据和索引 + +array_uintersect — 计算数组的交集,用回调函数比较数据 + +array_unique — 移除数组中重复的值 + +array_unshift — 在数组开头插入一个或多个单元 + +array_values — 返回数组中所有的值 + +array_walk_recursive — 对数组中的每个成员递归地应用用户函数 + +array_walk — 使用用户自定义函数对数组中的每个元素做回调处理 + +array — 新建一个数组 + +arsort — 对数组进行逆向排序并保持索引关系 + +asort — 对数组进行排序并保持索引关系 + +compact — 建立一个数组,包括变量名和它们的值 + +count — 计算数组中的单元数目,或对象中的属性个数 + +current — 返回数组中的当前单元 + +each — 返回数组中当前的键/值对并将数组指针向前移动一步 + +end — 将数组的内部指针指向最后一个单元 + +extract — 从数组中将变量导入到当前的符号表 + +in_array — 检查数组中是否存在某个值 + +key_exists — 别名 array_key_exists + +key — 从关联数组中取得键名 + +krsort — 对数组按照键名逆向排序 + +ksort — 对数组按照键名排序 + +list — 把数组中的值赋给一组变量 + +natcasesort — 用“自然排序”算法对数组进行不区分大小写字母的排序 + +natsort — 用“自然排序”算法对数组排序 + +next — 将数组中的内部指针向前移动一位 + +pos — current 的别名 + +prev — 将数组的内部指针倒回一位 + +range — 根据范围创建数组,包含指定的元素 + +reset — 将数组的内部指针指向第一个单元 + +rsort — 对数组逆向排序 + +shuffle — 打乱数组 + +sizeof — count 的别名 + +sort — 对数组排序 + +uasort — 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联 + +uksort — 使用用户自定义的比较函数对数组中的键名进行排序 + +usort — 使用用户自定义的比较函数对数组中的值进行排序 diff --git "a/docs/03.PHP/02.PHP\345\217\215\345\260\204\350\257\246\350\247\243.md" "b/docs/03.PHP/02.PHP\345\217\215\345\260\204\350\257\246\350\247\243.md" new file mode 100644 index 0000000..6d3f2d1 --- /dev/null +++ "b/docs/03.PHP/02.PHP\345\217\215\345\260\204\350\257\246\350\247\243.md" @@ -0,0 +1,160 @@ +# PHP 反射详解 + +面向对象编程中对象被赋予了自省的能力,而这个自省的过程就是反射。 +反射,直观理解就是根据到达地找到出发地和来源。比如,一个光秃秃的对象,我们可以仅仅通过这个对象就能知道它所属的类、拥有哪些方法。 +反射是指在PHP运行状态中,扩展分析PHP程序,导出或提出关于类、方法、属性、参数等的详细信息,包括注释。这种动态获取信息以及动态调用对象方法的功能称为反射API。 +如何使用反射API + +## 如何使用反射 API + +```php +class person +{ + public $name; + public $gender; + public function say(){ + echo $this->name," \tis ",$this->gender,"\r\n"; + } + public function set($name, $value) + { + echo "Setting $name to $value \r\n"; + $this->$name= $value; + } + public function get($name) + { + if(!isset($this->$name)){ + echo '未设置';  + $this->$name="正在为你设置默认值"; + } + return $this->$name; + } +} +$student = new person(); +$student->name = 'Tom'; +$student->gender = 'male'; +$student->age = 24; +``` + +现在,要获取这个student对象的方法和属性列表该怎么做呢? + +```php +// 获取对象属性列表 +$reflect = new ReflectionObject($student); +$props = $reflect->getProperties(); +foreach ($props as $prop) { + print $prop->getName() ."\n"; +} +// 获取对象方法列表 +$m = $reflect->getMethods(); +foreach ($m as $prop) { + print $prop->getName() ."\n"; +} +``` + +也可以不用反射API,使用class函数,返回对象属性的关联数组以及更多的信息: + +```php +// 返回对象属性的关联数组 +var_dump(get_object_vars($student)); +// 类属性 +var_dump(get_class_vars(get_class($student))); +// 返回由类的方法名组成的数组 +var_dump(get_class_methods(get_class($student))); +``` + +假如这个对象是从其他页面传过来的,怎么知道它属于哪个类呢? + +```php +// 获取对象属性列表所属的类 +echo get_class($student); +``` + +反射API的功能显然更强大,甚至能还原这个类的原型,包括方法的访问权限等 + +```php +// 反射获取类的原型 +$obj = new ReflectionClass('person'); +$className = $obj->getName(); +$Methods = $Properties = array(); +foreach($obj->getProperties() as $v) +{ + $Properties[$v->getName()] = $v; +} +foreach($obj->getMethods() as $v)   +{ + $Methods[$v->getName()] = $v; +} +echo "class {$className}\n{\n"; +is_array($Properties) && ksort($Properties); +foreach($Properties as $k => $v) +{ + echo "\t"; + echo $v->isPublic() ? ' public' : '',$v->isPrivate() ? ' private' : '', + $v->isProtected() ? ' protected' : '', + $v->isStatic() ? ' static' : ''; + echo "\t{$k}\n"; +} +echo "\n"; +if(is_array($Methods)) ksort($Methods); +foreach($Methods as $k => $v) +{ + echo "\tfunction {$k}(){}\n"; +} +echo "}\n"; +``` + +输出如下 + +```php +class person +{ + public gender + public name + function get(){} + function set(){} + function say(){} +} +``` + +不仅如此,PHP手册中关于反射API更是有几十个,可以说,反射完整地描述了一个类或者对象的原型。反射不仅可以用于类和对象,还可以用于函数、扩展模块、异常等 + +## 反射有什么作用 + +反射可以用于文档生成。因此可以用它对文件里的类进行扫描,逐个生成描述文档。 +既然反射可以探知类的内部结构,那么是不是可以用它做hook实现插件功能呢?或者是做动态代理呢? + +```php +class mysql +{ + function connect($db) { + echo "连接到数据库${db[0]}\r\n"; + } +} +class sqlproxy +{ + private $target; + function construct($tar) { + $this->target[] = new $tar(); + } + function call($name, $args) { + foreach ($this->target as $obj) { + $r = new ReflectionClass($obj); + if ($method = $r->getMethod($name)) { + if ($method->isPublic() && !$method->isAbstract()) { + echo "方法前拦截记录LOG\r\n"; + $method->invoke($obj, $args); + echo "方法后拦截\r\n"; + } + } + } + } +} +$obj = new sqlproxy('mysql'); +$obj->connect('member'); +``` + +在平常开发中,用到反射的地方不多:一个是对对象进行调试,另一个是获取类的信息。在MVC和插件开发中,使用反射很常见,但是反射的消耗也很大,在可以找到替代方案的情况下,就不要滥用。 + +PHP有Token函数,可以通过这个机制实现一些反射功能。从简单灵活的角度讲,使用已经提供的反射API是可取的。 + +很多时候,善用反射能保持代码的优雅和简洁,但反射也会破坏类的封装性,因为反射可以使本不应该暴露的方法或属性被强制暴露了出来,这既是优点也是缺点。 \ No newline at end of file diff --git a/docs/03.PHP/QA.md b/docs/03.PHP/QA.md new file mode 100644 index 0000000..780114a --- /dev/null +++ b/docs/03.PHP/QA.md @@ -0,0 +1,580 @@ +# 问题与简答 + +## PHP 篇 + +### static、self、$this 的区别 + +static:static 可以用于静态或非静态方法中,也可以访问类的静态属性、静态方法、常量和非静态方法,但不能访问非静态属性 + +self:可以用于访问类的静态属性、静态方法和常量,但 self 指向的是当前定义所在的类,这是 self 的限制 + +$this:指向的是实际调用时的对象,也就是说,实际运行过程中,谁调用了类的属性或方法,$this 指向的就是哪个对象。但 $this 不能访问类的静态属性和常量,且 $this 不能存在于静态方法中 + +### include、require、include_once、require_once 的区别 + +require 和 include 几乎完全一样,除了处理失败的方式不同之外。require 在出错时产生 E_COMPILE_ERROR 级别的错误。换句话说将导致脚本中止而 include 只产生警告(E_WARNING),脚本会继续运行 + +include_once 语句在脚本执行期间包含并运行指定文件。此行为和 include 语句类似,唯一区别是如果该文件中已经被包含过,则不会再次包含。如同此语句名字暗示的那样,只会包含一次 + +### 常见数组函数 + +array_count_values — 统计数组中所有的值 + +array_flip — 交换数组中的键和值 + +array_merge — 合并一个或多个数组 + +array_multisort — 对多个数组或多维数组进行排序 + +array_pad — 以指定长度将一个值填充进数组 + +array_pop — 弹出数组最后一个单元(出栈) + +array_push — 将一个或多个单元压入数组的末尾(入栈) + +array_rand — 从数组中随机(伪随机)取出一个或多个单元 + +array_keys — 返回数组中部分的或所有的键名 + +array_values — 返回数组中所有的值 + +count — 计算数组中的单元数目,或对象中的属性个数 + +sort — 对数组排序 + +### Cookie 和 Session + +Cookie:PHP 透明的支持 HTTP cookie 。cookie 是一种远程浏览器端存储数据并以此来跟踪和识别用户的机制 + +Session:会话机制(Session)在 PHP 中用于保持用户连续访问Web应用时的相关数据 + +### 预定义变量 + +对于全部脚本而言,PHP 提供了大量的预定义变量 + +超全局变量 — 超全局变量是在全部作用域中始终可用的内置变量 + +```text +$GLOBALS — 引用全局作用域中可用的全部变量 +$_SERVER — 服务器和执行环境信息 +$_GET — HTTP GET 变量 +$_POST — HTTP POST 变量 +$_FILES — HTTP 文件上传变量 +$_REQUEST — HTTP Request 变量 +$_SESSION — Session 变量 +$_ENV — 环境变量 +$_COOKIE — HTTP Cookies +$php_errormsg — 前一个错误信息 +$HTTP_RAW_POST_DATA — 原生POST数据 +$http_response_header — HTTP 响应头 +$argc — 传递给脚本的参数数目 +$argv — 传递给脚本的参数数组 +``` + +- 超全局变量 + +PHP 中的许多预定义变量都是“超全局的”,这意味着它们在一个脚本的全部作用域中都可用。在函数或方法中无需执行 global $variable; 就可以访问它们 + +超全局变量:$GLOBALS、$\_SERVER、$\_GET、$\_POST、$\_FILES、$\_COOKIE、$\_SESSION、$\_REQUEST、$\_ENV + +### 传值和传引用的区别 + +传值导致对象生成了一个拷贝,传引用则可以用两个变量指向同一个内容 + +### 构造函数和析构函数 + +构造函数:PHP 5 允行开发者在一个类中定义一个方法作为构造函数。具有构造函数的类会在每次创建新对象时先调用此方法,所以非常适合在使用对象之前做一些初始化工作 + +析构函数:PHP 5 引入了析构函数的概念,这类似于其它面向对象的语言,如 C++。析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时执行 + +### 魔术方法 + +\_\_construct(), \_\_destruct(), \_\_call(), \_\_callStatic(), \_\_get(), \_\_set(), \_\_isset(), \_\_unset(), \_\_sleep(), \_\_wakeup(), \_\_toString(), \_\_invoke() 等方法在 PHP 中被称为"魔术方法"(Magic methods) + +### public、protected、private、final 区别 + +对属性或方法的访问控制,是通过在前面添加关键字 public(公有),protected(受保护)或 private(私有)来实现的。被定义为公有的类成员可以在任何地方被访问 + +PHP 5 新增了一个 final 关键字。如果父类中的方法被声明为 final,则子类无法覆盖该方法。如果一个类被声明为 final,则不能被继承 + +### 客户端/服务端 IP 获取,了解代理透传 实际IP 的概念 + +客户端IP: $\_SERVER['REMOTE_ADDR'] + +服务端IP: $\_SERVER['SERVER_ADDR'] + +客户端IP(代理透传): $\_SERVER['HTTP_X_FORWARDED_FOR'] + +### 类的静态调用和实例化调用 + +- 占用内存 + +静态方法在内存中只有一份,无论调用多少次,都是共用的 + +实例化不一样,每一个实例化是一个对象,在内存中是多个的 + +- 不同点 + +静态调用不需要实例化即可调用 + +静态方法不能调用非静态属性,因为非静态属性需要实例化后,存放在对象里 + +静态方法可以调用非静态方法,使用 self 关键字。php 里,一个方法被 `self::` 后,自动转变为静态方法 + +调用类的静态函数时不会自动调用类的构造函数 + +### 接口和抽象的区别 +抽象用于描述不同的事物,接口用于描述事物的行为。 + +### PHP 不实例化调用方法 + +静态调用、使用 PHP 反射方式 + +### php.ini 配置选项 + +- 配置选项 + +|名字|默认|备注| +|-|-|-| +|short_open_tag|"1"|是否开启缩写形式(``)| +|precision|"14"|浮点数中显示有效数字的位数| +|disable_functions|""|禁止某些函数| +|disable_classes|""|禁用某些类| +|expose_php|""|是否暴露 PHP 被安装在服务器上| +|max_execution_time|30|最大执行时间| +|memory_limit|128M|每个脚本执行的内存限制| +|error_reporting|NULL|设置错误报告的级别 `E_ALL` & ~`E_NOTICE` & ~`E_STRICT` & ~`E_DEPRECATED`| +|display_errors|"1"|显示错误| +|log_errors|"0"|设置是否将错误日志记录到 error_log 中| +|error_log|NULL|设置脚本错误将被记录到的文件| +|upload_max_filesize|"2M"|最大上传文件大小| +|post_max_size|"8M"|设置POST最大数据限制| + +```shell +php -ini | grep short_open_tag //查看 php.ini 配置 +``` + +- 动态设置 + +```php +ini_set(string $varname , string $newvalue); + +ini_set('date.timezone', 'Asia/Shanghai'); //设置时区 +ini_set('display_errors', '1'); //设置显示错误 +ini_set('memory_limit', '256M'); //设置最大内存限制 +``` + +### php-fpm.conf 配置 + +|名称|默认|备注| +|-|-|-| +|pid||PID文件的位置| +|error_log||错误日志的位置| +|log_level|notice|错误级别 alert:必须立即处理、error:错误情况、warning:警告情况、notice:一般重要信息、debug:调试信息| +|daemonize|yes|设置 FPM 在后台运行| +|listen|ip:port、port、/path/to/unix/socket|设置接受 FastCGI 请求的地址| +|pm|static、ondemand、dynamic|设置进程管理器如何管理子进程| +|request_slowlog_timeout|'0'|慢日志记录阀值| +|slowlog||慢请求的记录日志| + +### 502、504 错误产生原因及解决方式 + +#### 502 + +502 表示网关错误,当 PHP-CGI 得到一个无效响应,网关就会输出这个错误 + +- `php.ini` 的 memory_limit 过小 +- `php-fpm.conf` 中 max_children、max_requests 设置不合理 +- `php-fpm.conf` 中 request_terminate_timeout、max_execution_time 设置不合理 +- php-fpm 进程处理不过来,进程数不足、脚本存在性能问题 + +#### 504 + +504 表示网关超时,PHP-CGI 没有在指定时间响应请求,网关将输出这个错误 + +- Nginx+PHP 架构,可以调整 FastCGI 超时时间,fastcgi_connect_timeout、fastcgi_send_timeout、fastcgi_read_timeout + +#### 500 + +php 代码问题,文件权限问题,资源问题 + +#### 503 + +超载或者停机维护 + +### 如何返回一个301重定向 + +```php +header('HTTP/1.1 301 Moved Permanently'); +header('Location: https://blog.maplemark.cn'); +``` + +### PHP 与 MySQL 连接方式 + +#### MySQL + +```php +$conn = mysql_connect('127.0.0.1:3306', 'root', '123456'); +if (!$conn) { + die(mysql_error() . "\n"); +} +mysql_query("SET NAMES 'utf8'"); +$select_db = mysql_select_db('app'); +if (!$select_db) { + die(mysql_error() . "\n"); +} +$sql = "SELECT * FROM `user` LIMIT 1"; +$res = mysql_query($sql); +if (!$res) { + die(mysql_error() . "\n"); +} +while ($row = mysql_fetch_assoc($res)) { + var_dump($row); +} +mysql_close($conn); +``` + +#### MySQLi + +```php +$conn = @new mysqli('127.0.0.1:3306', 'root', '123456'); +if ($conn->connect_errno) { + die($conn->connect_error . "\n"); +} +$conn->query("set names 'utf8';"); +$select_db = $conn->select_db('user'); +if (!$select_db) { + die($conn->error . "\n"); +} +$sql = "SELECT * FROM `user` LIMIT 1"; +$res = $conn->query($sql); +if (!$res) { + die($conn->error . "\n"); +} +while ($row = $res->fetch_assoc()) { + var_dump($row); +} +$res->free(); +$conn->close(); +``` + +#### PDO + +```php +$pdo = new PDO('mysql:host=127.0.0.1:3306;dbname=user', 'root', '123456'); +$pdo->exec("set names 'utf8'"); +$sql = "SELECT * FROM `user` LIMIT 1"; +$stmt = $pdo->prepare($sql); +$stmt->bindValue(1, 1, PDO::PARAM_STR); +$rs = $stmt->execute(); +if ($rs) { + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + var_dump($row); + } +} +$pdo = null; +``` + +### MySQL、MySQLi、PDO 区别 + +#### MySQL + +- 允许 PHP 应用与 MySQL 数据库交互的早期扩展 +- 提供了一个面向过程的接口,不支持后期的一些特性 + +#### MySQLi + +- 面向对象接口 +- prepared 语句支持 +- 多语句执行支持 +- 事务支持 +- 增强的调试能力 + +#### PDO + +- PHP 应用中的一个数据库抽象层规范 +- PDO 提供一个统一的 API 接口,无须关心数据库类型 +- 使用标准的 PDO API,可以快速无缝切换数据库 + +### 数据库持久连接 + +把 PHP 用作多进程 web 服务器的一个模块,这种方法目前只适用于 Apache。 + +对于一个多进程的服务器,其典型特征是有一个父进程和一组子进程协调运行,其中实际生成 web 页面的是子进程。每当客户端向父进程提出请求时,该请求会被传递给还没有被其它的客户端请求占用的子进程。这也就是说当相同的客户端第二次向服务端提出请求时,它将有可能被一个不同的子进程来处理。在开启了一个持久连接后,所有请求 SQL 服务的后继页面都能够重用这个已经建立的 SQL Server 连接。 + +### 代码执行过程 + +PHP 代码 => 启动 php 及 zend 引擎,加载注册拓展模块 => 对代码进行词法/语法分析 => 编译成opcode(opcache) => 执行 opcode + +> PHP7 新增了抽象语法树(AST),在语法分析阶段生成 AST,然后再生成 opcode 数组 + +### base64 编码原理 + +![base64](./assets/php-base64.png) + +### ip2long 实现 + +![ip2long](./assets/php-ip2long.png) + + +``` +124.205.30.150=2093817494 + +list($p1,$p2,$p3,$p4) = explode('.','124.205.30.150'); + +$realNum = ($p1<<24)+($p2<<16)+($p3<<8)+$p4; +``` + +### MVC 的理解 + +MVC 包括三类对象。模型 Model 是应用对象,视图 View 是它在屏幕上的表示,控制器 Controller 定义用户界面对用户输入的响应方式。不使用 MVC,用户界面设计往往将这些对象混在一起,而 MVC 则将它们分离以提高灵活性和复用性 + +### 主流 PHP 框架特点 + +#### Laravel + +易于访问,功能强大,并提供大型,强大的应用程序所需的工具 + +- 简单快速的路由引擎 +- 强大的依赖注入容器 +- 富有表现力,直观的数据库 ORM +- 提供数据库迁移功能 +- 灵活的任务调度器 +- 实时事件广播 + +#### Symfony + +- Database engine-independent +- Simple to use, in most cases, but still flexible enough to adapt to complex cases +- Based on the premise of convention over configuration--the developer needs to configure only the unconventional +- Compliant with most web best practices and design patterns +- Enterprise-ready--adaptable to existing information technology (IT) policies and architectures, and stable enough for long-term projects +- Very readable code, with phpDocumentor comments, for easy maintenance +- Easy to extend, allowing for integration with other vendor libraries + +#### CodeIgniter + +- 基于模型-视图-控制器的系统 +- 框架比较轻量 +- 全功能数据库类,支持多个平台 +- Query Builder 数据库支持 +- 表单和数据验证 +- 安全性和 XSS 过滤 +- 全页面缓存 + +#### ThinkPHP + +- 采用容器统一管理对象 +- 支持 Facade +- 更易用的路由 +- 注解路由支持 +- 路由跨域请求支持 +- 验证类增强 +- 配置和路由目录独立 +- 取消系统常量 +- 类库别名机制 +- 模型和数据库增强 +- 依赖注入完善 +- 支持 PSR-3 日志规范 +- 中间件支持 +- 支持 Swoole/Workerman 运行 + +### 对象关系映射/ORM + +#### 优点 + +- 缩短编码时间、减少甚至免除对 model 的编码,降低数据库学习成本 +- 动态的数据表映射,在表结构发生改变时,减少代码修改 +- 可以很方便的引入附加功能(cache 层) + +#### 缺点 + +- 映射消耗性能、ORM 对象消耗内存 +- SQL 语句较为复杂时,ORM 语法可读性不高(使用原生 SQL) + +### 链式调用实现 + +类定义一个内置变量,让类中其他定义方法可访问到 + +### 异常处理 + +set_exception_handler — 设置用户自定义的异常处理函数 + +使用 try / catch 捕获 + +### 串行、并行、并发的区别 +串行:执行多个任务时,各个任务按顺序执行,完成一个之后才能进行下一个 +并行:多个任务在同一时刻执行 +并发:同一时刻需要执行多个任务 + +### 同步与异步的理解 +**同步和异步是一种消息通信机制**。其关注点在于 `被调用者返回` 和 `结果返回` 之间的关系,描述对象是被调用对象的行为。 + +### 阻塞与非阻塞的理解 +**阻塞和非阻塞是一种业务流程处理方式**。其关注点在于调用发生时 `调用者状态` 和 `被调用者返回结果` 之间的关系,描述对象是等待结果时候调用者的状态。 + +### 同步阻塞与非同步阻塞的理解 +同步阻塞:打电话问老板有没有某书(调用),老板说查一下,让你别挂电话(同步),你一直等待老板给你结果,什么事也不做(阻塞)。 + +同步非阻塞:打电话问老板有没有某书(调用),老板说查一下,让你别挂电话(同步),等电话的过程中你还一边嗑瓜子(非阻塞)。 + +### 异步阻塞与异步非阻塞的理解 +异步阻塞:打电话问老板有没有某书(调用),老板说你先挂电话,有了结果通知你(异步),你挂了电话后(结束调用), 除了等老板电话通知结果,什么事情也不做(阻塞)。 + +异步非阻塞:打电话问老板有没有某书(调用),老板说你先挂电话,有了结果通知你(异步),你挂电话后(结束调用),一遍等电话,一遍嗑瓜子。(非阻塞) + +### 如何实现异步调用 + +```php +$fp = fsockopen("blog.maplemark.cn", 80, $errno, $errstr, 30); +if (!$fp) { + echo "$errstr ($errno)
\n"; +} else { + $out = "GET /backend.php / HTTP/1.1\r\n"; + $out .= "Host: blog.maplemark.cn\r\n"; + $out .= "Connection: Close\r\n\r\n"; + fwrite($fp, $out); + /*忽略执行结果 + while (!feof($fp)) { + echo fgets($fp, 128); + }*/ + fclose($fp); +} +``` + +### 多进程同时写一个文件 + +加锁、队列 + +### PHP 进程模型,进程通讯方式,进程线程区别 + +消息队列、socket、信号量、共享内存、信号、管道 + +### PHP 支持回调的函数,实现一个 + +array_map、array_filter、array_walk、usort + +is_callable + callbacks + 匿名函数实现 + +### 发起 HTTP 请求有哪几种方式,它们有何区别 + +cURL、file_get_contents、fopen、fsockopen + +### php for while foreach 迭代数组时候,哪个效率最高 + +### 弱类型变量如何实现 + +PHP 中声明的变量,在 zend 引擎中都是用结构体 zval 来保存,通过共同体实现弱类型变量声明 + +### PHP 拓展初始化 + +- 初始化拓展 + +```shell +$ php /php-src/ext/ext_skel.php --ext +``` + +- 定义拓展函数 + +zend_module_entry 定义 Extension name 编写 PHP_FUNCTION 函数 + +- 编译安装 + +```shell +$ phpize $ ./configure $ make && make install +``` + +### 如何获取扩展安装路径 + +### 垃圾回收机制 + +引用计数器 + +### Trait + +自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait + +### yield 是什么,说个使用场景 yield、yield 核心原理是什么 + +一个生成器函数看起来像一个普通的函数,不同的是普通函数返回一个值,而一个生成器可以yield生成许多它所需要的值。 + +yield核心原理: PHP在使用生成器的时候,会返回一个Generator类的对象。每一次迭代,PHP会通过Generator实例计算出下一次需要迭代的值。简述即yield用于生成值。 + +yield使用场景:读取大文件、大量计算。 + +yield好处:节省内存、优化性能 + +### traits 与 interfaces 区别 及 traits 解决了什么痛点 + +### 如何 foreach 迭代对象、如何数组化操作对象 $obj[key]、如何函数化对象 $obj(123); + +### Swoole 适用场景,协程实现方式 + +Swoole 是一个使用 C++ 语言编写的基于异步事件驱动和协程的并行网络通信引擎,为 PHP 提供协程、高性能网络编程支持。提供了多种通信协议的网络服务器和客户端模块,可以方便快速的实现 TCP/UDP服务、高性能Web、WebSocket服务、物联网、实时通讯、游戏、微服务等,使 PHP 不再局限于传统的 Web 领域。 + + +协程可以简单理解为线程,只不过这个线程是用户态的,不需要操作系统参与,创建销毁和切换的成本非常低,和线程不同的是协程没法利用多核 cpu 的,想利用多核 cpu 需要依赖 Swoole 的多进程模型。 +在底层实现上是单线程的,因此同一时间只有一个协程在工作,协程的执行是串行的。 +采用 CSP 编程模型,即不要以共享内存的方式来通信,相反,要通过通信来共享内存。 +swoole4.0采用双栈方式,通过栈桢切换来实现协程;即遇到IO等待就切换到。 + +#### swoole的进程模型 + +同一台主机上两个进程间通信 (简称 IPC) 的方式有很多种,在 Swoole 中使用了 2 种方式 Unix Socket 和 sysvmsg。 + +swoole启动后会生成master进程、reactor线程、worker进程、task进程以及manager进程 + +master进程是一个多线程进程,会生成多个reactor线程 +reactor线程负载网络监听、数据收发 +work进程处理reactor线程投递的请求数据 +task进程处理work进程投递的任务 +manager进程用于管理work进程和task进程 + +### PHP 数组底层实现 (HashTable + Linked list) + +PHP 数组底层依赖的散列表数据结构,定义如下(位于 Zend/zend_types.h)。 + +数据存储在一个散列表中,通过中间层来保存索引与实际存储在散列表中位置的映射。 + +由于哈希函数会存在哈希冲突的可能,因此对冲突的值采用链表来保存。 + +哈希表的查询效率是o(1),链表查询效率是o(n);因此PHP数据索引速度很快;但是相对比较占用空间。 + +### Copy on write 原理,何时 GC + +### 如何解决 PHP 内存溢出问题 + +### ZVAL + +### HashTable + +### PHP7 新特性 + +标量类型声明、返回值类型声明、通过 define() 定义常量数组、匿名类、相同命名空间类一次性导入 + +### PHP7 底层优化 + +ZVAL 结构体优化,占用由24字节降低为16字节 + +内部类型 zend_string,结构体成员变量采用 char 数组,不是用 char* + +PHP 数组实现由 hashtable 变为 zend array + +函数调用机制,改进函数调用机制,通过优化参数传递环节,减少了一些指令 + +### PSR 介绍,PSR-1, 2, 4, 7 + +### Xhprof 、Xdebug 性能调试工具使用 + +### 字符串、数字比较大小的原理,注意 0 开头的8进制、0x 开头16进制 + +### BOM 头是什么,怎么除去 + +WINDOWS自带的记事本,在保存一个以 UTF-8 编码的文件时,会在文件开始的地方插入三个不可见的字符(0xEF 0xBB 0xBF,即BOM);它是一串隐藏的字符,用于让记事本等编辑器识别这个文件是否以UTF-8编码。 +去除方法:$result = trim($result, "\xEF\xBB\xBF"); + +### 模板引擎是什么,解决什么问题、实现原理(Smarty、Twig、Blade) + + +### 写一个函数,尽可能高效的从一个标准 URL 中取出文件的扩展名 +parse_str,explode diff --git a/assets/base64.png b/docs/03.PHP/assets/php-base64.png similarity index 100% rename from assets/base64.png rename to docs/03.PHP/assets/php-base64.png diff --git a/docs/03.PHP/assets/php-ip2long.png b/docs/03.PHP/assets/php-ip2long.png new file mode 100644 index 0000000..981b6f3 Binary files /dev/null and b/docs/03.PHP/assets/php-ip2long.png differ diff --git "a/docs/04.Web/01.\345\210\235\346\216\242SEO.md" "b/docs/04.Web/01.\345\210\235\346\216\242SEO.md" new file mode 100644 index 0000000..d5b7722 --- /dev/null +++ "b/docs/04.Web/01.\345\210\235\346\216\242SEO.md" @@ -0,0 +1,327 @@ +# 初探 SEO + +## 初探 SEO + +### SEO 的基本概念 + +> 搜索引擎优化(英语:search engine optimization,缩写为 SEO),是一种透过了解搜索引擎的运作规则来调整网站,以及提高目的网站在有关搜索引擎内排名的方式 + +> 一般的可以理解为,通过了解谷歌/百度搜索引擎规则,从而提高网站在某些关键字下的排名,提高网站访问量 + +### SEO 优缺点 + +> 成本较低。从某个角度上看,SEO 是一种"免费"的搜索引擎营销方式。对个人网站来说,只要站长掌握一定的搜索引擎优化技术即可。而对于企业来说,成本主要来自于从引擎优化员工的薪酬或雇用专业搜索引擎优化公司所花的费用 + +> 持久性。一般情况下,采用正规方法优化的网站,排名效果会比较稳定。除非搜索算法发生重大的改变或强大的竞争对手后来居上,否则不会有太大的变化 + +### SEO 应用领域 + +- 企业网站 + +> 企业网站通过优化后,大大增加了向目标客户展示产品或者服务的机会,从而增强企业的影响力,提升品牌知名度 + +- 电子商务型网站 + +> 电子商务类网站经过优化后可以通过搜索引擎向更多的潜在消费者推销自身的产品,从而节省巨额的广告费用,提高产品销量 + +- 内容型网站 + +> 资讯内容类网站经过优化后,可以大大提高网站的流量,从而进一步蚕食强大的竞争对手的市场,最终后来居上,成为行业领先者 + +### SEO 主要工作 + +- SEO 的主要工作可以分为内部优化与外部优化 + +> 内部优化:从网站内部出发,对网站的基本要素(如网站结构、页面结构、关键字分布等)进行适当的调整,如果经过调整后,网站在搜索引擎中的表现达到了我们预期的效果,则内部优化工作就基本完成;否则,需要反复地对网站进行调整,直至达到预期效果为止 + +> 外部优化:主要围绕增强外部链接关系而展开的,此项工作必须贯彻优化的全过程。常用于增加外部链接的方法包括交换友情链接、登录分类目录、发布链接诱饵等 + +![网站优化流程](./assets/seo-网站优化流程.png) + +### SEO 宗旨 + +> SEO 工作应该以用户为中心,围绕提高用户体验、完善网站功能而进行,不能为了优化而优化 + +## 搜索引擎工作原理 + +> 搜索引擎优化的主要任务之一就是提高网站的搜索引擎友好性,因此,搜索引擎优化的每个环节都与搜索引擎存在必然的联系,研究搜索引擎优化实际上是对搜索引擎工作过程的逆向推理。因此,学习搜索引擎优化应该从了解搜索引擎的工作原理开始 + +> 搜索引擎的主要工作包括:页面收录、页面分析、页面排序及关键字查询 + +### 页面收录 + +> 页面收录指搜索引擎通过蜘蛛程序在互联网上抓取页面并进行存储的过程,它为搜索引擎开展各项工作提供了数据支持 + +- 页面收录流程 + +> 在互联网中,URL 是每个页面的入口地址,搜索引擎蜘蛛程序就是通过 URL 抓取到页面的。搜索引擎蜘蛛程序从 URL 列表出发,通过 URL 抓取并存储原始页面;同时,提取原始页面中的 URL 资源并加入到 URL 列表中。如此不断地循环,就可以从互联网获取足够多的页面 + +> 利用搜索引擎提供的网站登录入口,想搜索引擎提交网站域名 + +> 通过与外部网站建立链接关系,使搜索引擎可以通过外部网站发现我们的网站,从而实现对网站的收录 + +![搜索引擎抓取页面简单流程](./assets/seo-搜索引擎抓取页面简单流程.png) + +- 页面收录原理 + +> 如果把网站页面组成的集合看作是一个有向图,从指定的页面出发,沿着页面中的链接,按照某种特定的策略对网站中的页面进行遍历。不停地从 URL 列表中移出已经访问过的 URL,并存储原始页面,同时提取原始页面中的 URL 信息;再将 URL 分为域名及内部 URL 两大类,同时判断 URL 是否被访问过,将未访问的 URL 加入 URL 列表中。递归地扫描 URL 列表,直至耗尽所有 URL 资源为止。经过这些工作,搜索引擎就可以建立庞大的域名列表、页面 URL 列表并存储足够多的原始页面 + +- 页面收录方式 + +> 在互联网数以亿计的页面中,搜索引擎怎样才能从中抓取到相对重要的页面呢?这就涉及搜索引擎的页面收录方式 + +> 页面收录方式是指搜索引擎抓取页面时所使用的策略,目的是为了能在互联网中筛选出相对重要的信息 + +> 搜索引擎收录页面的方式主要有广度优先、深度优先及用户提交三种 + +> 广度优先:如果把整个网站看作是一棵树,首页就是根,每个页面就是叶子。广度优先是一种横向的页面抓取方式,先从树的较浅层开始抓取页面,直至抓取完同层次的所有页面后才进入下一层 + +> 深度优先:与广度优先抓取方式相反,深度优先首先跟踪浅层页面中的某一链接逐步抓取深层页面,直至抓取完最深的页面后才返回浅层页面再跟踪其另一链接,继续向深层页面抓取,这是一种纵向的页面抓取方式 + +> 用户提交:为了抓取更多的网页,搜索引擎还允许网站管理员主动提交页面。网站管理员只需要把网站中页面 URL 按照指定的格式制作成文件,提交给搜索引擎,搜索引擎即可通过该文件对网站中的页面进行抓取及更新 + +- 如何避免重复性收录 + +> 在互联网中,信息的重复是在所难免的。搜索引擎怎样识别重复信息呢?怎样判断哪些网页的信息是原创的,哪些是“复制”的?又会认为哪些重复的信息是有价值的,哪些又是可以舍弃的? + +> 在网站中,重复信息主要包括转载内容及镜像内容两大类 + +> 转载页面:通过将网页正文内容划分,进行比对,判断相识度,确定互为转载关系后,再结合搜索引擎的抓取存储时间、页面权重等因素判断页面是否是原创页面还是转载页面 + +> 镜像页面:与转载页面判别类似 + +> 镜像网站:指两个内容完全相同的网站,形成镜像网站主要有两种情况,第一种是多个域名或 IP 指向同一服务器的同一个物理目录;另一个整个网站内容被复制到使用不同域名或者IP服务器上 + +> 转载页面、镜像页面、镜像网站,综合多项因素(网站权重值、建立时间)等因素,可能只收录极少页面,甚至不收录页面 + +### 页面分析 + +> 页面分析首先是对原始页面建立索引,实现对页面的快速定位;然后,提取页面的正文信息,并对正文信息进行切词以及为这些词(即关键字)建立索引,从而得到页面与关键字的对应关系;最后,对关键字进行重组,并建立关键字与网页相对于的反向索引列表,从而能够根据关键字快速定位至相应网页 + +![网页分析处理流程](./assets/seo-网页分析处理流程.png) + +- 页面索引 + +> 为了提高页面检索的效率,搜索引擎需要对抓取回来的原始页面建立索引,由于 URL 就是页面的入口地址,为了原始页面建立索引实际上就是为页面的 URL 建立索引,这样就可以实现根据 URL 快速定位到对应页面 + +- 网页分析 + +> 页面分析是整个网页处理中最重要的环节,包括了网页正文信息的提取、切词、建立关键字索引列表及关键字重组几个重要的步骤。结果形成了一个关键字对应多个原始页面的关系,即形成了与用户习惯相符合的信息雏形 + +### 页面排序 + +> 搜索引擎结合页面的内外部因素计算出页面与某个关键字的相关程度,从而得到与该关键字相关的页面排序列表 + +- 页面相关性 + +> 页面相关性是指页面内容与用户所查询的关键字的接近程度,主要由关键字匹配度、关键字密度、关键字分布及关键字的权重标签决定 + +- 链接权重 + +> 链接主要分内部链接及外部链接,是网页制作或者编辑者在对页面内容进行规划或者编辑时加入到页面中的,加入的理由可能是该链接所指向的页面非常重要,或者它是大部分用户所需要的。因此,某一页面得到的链接越多,从一定程度上反映了该页面越重要,链接权重值就越高 + +- 用户行为 + +> 搜索引擎在完成页面基本权重计算后,就可以向用户展示初步的排序结果。但这个排序结果不一定能让大部分用户满意,因此还要结合其他因素对该排序结果进行改进。列如,统计每条搜索结果的点击次数来推测用户对搜索结果的偏好 + +### 关键字查询 + +> 搜索引擎接收来自用户的查询请求,并对查询信息进行切词及匹配后,再向用户返回相应的页面排序列表 + +- 查询流程 + +![查询处理流程](./assets/seo-查询处理流程.png) + +> 先对用户提供的查询条件进行切词,并删除查询条件中没有的字或词,例如的、得等停用词 + +> 再以切词结果作为条件在关键字反向索引列表中进行匹配 + +> 如果存在匹配结果,则把所有与关键字相匹配的页面组成一个列表 + +> 最后,把匹配的页面按照权重值从高到低进行排序,并返回给用户 + +- 用户行为 + +> 用户在搜索中的行为主要包括搜索及点击。搜索是用户获取信息的过程,点击是用户得到需要信息后的表现 + +> 用户的搜索及点击行为中蕴含着非常丰富和重要的信息。例如,在用户搜索行为中包含了“提交的关键字”、“提交时间”、“用户IP地址”等信息,而在点击行为中则包含了“每个结果的点击次数”等信息 + +> 搜索引擎通过对用户行为的分析可以进一步发掘用户的需求,提高搜索结果的精准度。例如,从用户的搜索行为中,搜索引擎还可以发现新词汇;而从用户对搜索结果的点击行为中,可以分析出用户对每个搜索结果的偏好等 + +- 缓存机制 + +> 为了能在极短的时间内响应用户的查询请求,搜索引擎除了在用户提交查询信息前就生成关键字的页面排序列表外,还需要为那些查询最频繁的关键字对应的页面排序列表建立缓存机制 + +### 搜索引擎介绍 + +- Google + +> Google 十分重视链接关系,对于链接的质量、数量及相关性方面的分析技术在业界更是遥遥领先。尽管百度、雅虎也非常重视链接关系,但对链接的质量及相关性方面的分析则远不如 Google + +> 在切词算法上,Google 与其他中文搜索引擎也存在一定的区别 + +> 在对待新网站方面,Google 非常严格,新网站只有同时满足多个条件时,才能正常参与排名竞争,这就是所谓的“沙盒效应”现象。这样做可以有效避免垃圾网站,但同时也给了一些新的优秀网站诸多制肘,很难通过 Google 向用户展示其极具价值的信息 + +> 在对垃圾信息处理方面,尽管 Google 目前还是以人工为主,但与其他搜索引擎相比,Google Spam 检测算法已经比较成熟。对于一些常见的作弊手段,例如伪装(cloaking)、门页(doorway page)、堆砌关键字、隐藏文字、垃圾链接等,Google 可以轻易识别 + +- 百度 + +> 如果把决定页面权重的因素分内部因素与外部因素两大类。在百度中,内部因素与外部因素在影响页面权重方面的差距比较小 + +> 百度对新网站比较宽松,这就造成了搜索结果中充斥着大量的垃圾信息,严重影响了用户体验 + +> 百度也非常重视链接关系,对于被高质量页面链接的页面会赋予极高的权重,但却忽悠了链接关系中网站间的主题相关性 + +> 百度对搜索结果的人工干预非常强 + +## 关键字 + +> 要对网站进行优化,首先需要为网站中的页面选择合适的关键词;然后,围绕突出关键字、提高页面相关性开展一系列的工作,以提高页面在相关关键字搜索结果中的排名 + +### 关键字介绍 + +> 关键字(keyword)在不同的领域有不同的含义。在搜索引擎中,关键字是指用户在寻找相关信息时所使用的内容,是搜索应用的基础,也是搜索引擎优化的基础。搜索引擎优化的目的之一就是提高页面与某个关键字间的相关性,要了解关键字与页面相关性的关系,我们要从认识关键字词频及关键字密度开始 + +### 关键字词频 + +> 关键字词频是指某个关键字的页面中出现的概率,也即关键字在页面中出现的次数,从一定程度上反映了页面与该关键字的相关性 + +> 在搜索引擎发展初期,搜索结果中页面的排序基本是由关键字词频决定的。也就是说,页面中某个关键字出现的次数越多,说明该页面与此关键字间的相关性就越高,在此关键字的搜索结果中排名就越靠前。这种单纯以词频决定排序的方式,极容易被恶意操纵,从而影响搜索引擎的用户体验。因此,搜索引擎逐渐引进了诸如“关键字密度”、“关键字分布”及“外部链接”等因素进行制约 + +### 关键字密度 + +> 在实际中,常用关键字密度来衡量页面中关键字的词频是否合理。关键字密度主要是由“关键字词频”及“网页总词汇量”两个因素决定,这三者关系如下: + +> 关键字密度=关键字词频/网页总词汇量 + +> 式中,总词汇量是指页面程序标签(如 HTML 标签及 ASP、JSP、PHP 等)以外的所有词汇的数量 + +### 关键字分布及表现形式 + +> 搜索引擎分析网页时,在 HTML 源代码中是自上而下地进行的;而从页面布局的角度上看,则是自上而下、自左而右地进行(这也符合用户浏览网页的习惯)。因此,搜索引擎会更加重视网页中首先出现的内容,故我们在规划页面时也应该把相对重要的内容安排在页面的顶部,搜索引擎对页面的重视程度沿箭头方向逐渐减低 + +> 关键字表现形式是指关键字在页面中的显示样式,常见的关键字表现形式包括字体的字号、颜色、样式等;而字体样式又包括加粗、下划线、斜体、段落标题等 + +> 关键字描述指在页面中通过多种方式表达主辅关键字,以达到合理增加主辅关键字的词频及控制主关键字密度的目的 + +### 关键字策略 + +> 制定符合自己网站特点的科学的关键字策略,可以避免与强大的竞争对手直接硬碰硬的竞争,从而占据属于自己的一席之地,为以后的生存打下坚实的基础。制定关键字策略,首先要寻找与页面主题相关的关键字,然后再根据实际情况从中筛选出一部分合适的关键字 + +> 利用搜索引擎的搜索功能,我们可轻松地找到与页面主题相关的关键字 + +> 用户搜索习惯是指用户在搜索引擎中寻找相关内容时所使用的关键字形式。对于不同类别的产品,用户的搜索习惯会存在一定的差别,而我们应该优先选择那些符合大部分用户搜索习惯的关键字形式 + +> 一个关键字是否具有优化可行性,需要首先对该关键字进行综合的评估,包括关键字的“搜索量”、“商业价值”及“竞争程度”;再从中筛选出高搜索量、高相关性、低竞争的关键字 + +> 关键字选择技巧:次关键字法、长尾理论法 + +## URL 优化 + +### URL 优化简介 + +> URL 优化就是指通过对 URL 各组成部分进行适当的调整,以提高 URL 的搜索引擎友好性。它包括三大部分:第一,对域名、目录、文件的命名;第二,分隔符的使用;第三,URL 长度及关键字词频的控制。这三部分之间相互制约、相互影响。在优化的过程中,我们必须掌握着三部分的内在联系,对各部分进行充分的协调,才能达到最佳的优化效果,否则就功败垂成 + +### URL 命名技巧 + +> URL 命名是针对 URL 各组成部分而进行的,是 URL 优化最重要的环节。URL 命名的关键在于使用合适的关键字为 URL 各组成部分进行命名,即该关键字所表达的意义必须与 URL 所指向的页面的主题是相关的,这样有利于提高页面的相关性,突出页面的主题 + +### 分隔符的使用 + +> 为了让搜索引擎能够正确识别以英文形式命名的 URL 中的关键字,我们需要使用相应的符号对单词进行分隔,常见的分隔符包括:空格“ ”、横杠“-”、下划线“\_”、逗号“,”及加号“+”等 + +### URL 长度 + +> 搜索引擎在抓取页面时,对页面的 URL 长度存在一定的限制。对于超过限定长度的 URL 所指向的页面,搜索引擎就有可能放弃收录。决定 URL 长度的主要因素包括域名长度、路径长度及文件名长度 + +### 关键字词频 + +> 在一定完整的 URL 中,主关键字只出现一次,不管对于普通用户还是搜索引擎都是较为友好的。一方面涉及 URL 长度的问题;另一方面,如果同一关键字在 URL 中多次重复出现,还会涉嫌关键字堆砌,严重者可能遭到搜索引擎的惩罚 + +### 关键字结合 + +> 在 URL 中,还可以利用分隔符对 URL 各组成的名称进行组合,从而产生新的词组(或短语)来扩展 URL 的意义 + +### URL 重定向 + +> 重定向是指把对一个域名、目录或者文件的访问请求转发至另一个域名、目录或其他服务器空间上,当用户发出相应的访问请求时将自动跳转到指定的位置 + +> 常见的重定向欧301(永久重定向)及302(暂时重定向)两种 + +### URL 静态化 + +> 不管是普通用户还是搜索引擎,都更钟情于静态页面 + +## 代码优化 + +### 代码优化简介 + +> 代码优化就是对网页中的 HTML 源代码进行必要的调整,以提高页面的友好性。页面经过代码优化后,一方面可以有效精简页面中的冗余代码,加快页面的显示速度,同时也降低页面占用搜索引擎服务器的存储空间,从而提高页面的用户体验及搜索引擎友好性;另一方面,还可以有效地突出页面的主题,提高页面的相关性。代码优化的主要工作包括精简代码、头部优化、权重标签使用及图片优化。在这四个环节中,精简代码是最基础、最根本的 + +### 精简代码 + +> 精简代码是指清除或者简化页面中的代码,从而达到降低页面体积、提高页面的用户体验及搜索引擎友好性的目的。页面代码的精简包括五大环节,它们是清理垃圾代码、 HTML 标签转换、 CSS 优化、 Js 优化及表格优化。其中,清理垃圾代码又是精简代码中最重要、最基础的 + +### 页面头部优化 + +> 摘要信息的生成在不同的搜索引擎中会存在比较大的差别,即使是同一个搜索引擎也会由于页面的实际情况而有所不同。一般情况下,搜索引擎会提取页面标题标签中的内容作为摘要信息的标题,而描述则常来自页面描述标签的内容或直接从页面正文中截取 + +> 标签也称为标题标签,标题标签内容是对网页主题的概括,相当于一篇文章的题目。标题标签的优化是网站优化中最重要的内容之一,对页面相关性产生决定性的影响。大多数搜索引擎都是提取网页标题中的全部或部分内容作为搜索结果中摘要信息的标题向用户展示。因此,在拟写标题内容时,要做到主题突出、内容简洁 + +> 描述标签,即标签,其内容是对页面内容的概括,相当于页面的简介。由于描述标签常被一些不法分子用于堆砌关键字,导致其对页面相关性的影响力日渐下降。但是,Google 仍然相当重视描述标签的内容。在 Google 的搜索结果中,大部分页面的描述都是来自页面描述标签里的内容 + +> 关键字标签即标签,是头部三大标签之一。相对于标题标签及描述标签,关键字标签所起的作用微乎其微,甚至己经被忽略。但无论如何,为了能在残酷的竞争中脱颖而出,我们还是要把握好每一个可能会影响页面相关性的细节。 + +### 权重标签使用 + +> 标签又称为标题标签,是所有权重标签中最重要的。它一共有六种样式,从 + +> 字体标签()包括字体颜色、字号大小等属性,其中最重要的就是字号及颜色属性 + +### 图片优化 + +> 在制作网页的时候,为了增强网页的视觉效果,在网页中或多或少会使用图片或者多媒体元素。然而,相对于文本来说,图片的体积庞大(少则二三十千字节,多则上百千字节),这就造成页面显示缓慢等现象。此外,搜索引擎也不能识别图片叭的文本内容。所以,对于页面中的重要内容,例如主关键字、辅关键字、频道名称、栏目名称等,我们不能以图片形式进行展示 + +> 为了减轻图片带来的负面影响,我们需要对图片进行相应的处理(即图片优化),以满足搜索引擎索引信息的要求。图片优化主要包括“图片描述”及“图片压缩”两大块,前者主要是为了向搜索引擎表达图片蕴含的信息,而后者主要是为了降低图片体积,加快页面显示速度 + +## 网页结构 + +### 网页结构简介 + +> 网页结构即网页内容的布局,创建网页结构,实际上就是对网页内容的布局进行规划。网页结构的创建是页面优化的重要环节之一,直接影响页面的用户体验及相关性。而且,还在一定程度上影响网站的整体结构及页面被收录的数量 + +### 网页组成元素 + +> 从页面结构的角度上看,网页主要由"导航栏"、"栏目"及"正文内容"这三大元素组成。网页结构的创建,网页内容的规划实际也是围绕这三大组成元素展开的 + +> 导航栏是构成网页的重要元素之一,是网站频道入口的集合区域,相当于网站的菜单 + +> 栏目是指页面中存放相同性质(或特征)内容的区域。在对页面内容进行内容布局时,把性质(或特征)相同的内容安排在页面上的同一区域,可以帮助用户快速获取所需的信息,对网站内容起到非常好的导航作用 + +> 正文内容是指页面中的主体内容。例如一个文章类页面,正文内容就是文章本身;而对于展示产品的网站,正文内容就是产品信息 + +### 页面重要区域分布规律 + +> 左上 > 右上 > 左 > 右 > 左下 > 右下 + +> 搜索引擎分析网页时,在 HTML 源代码中是自上而下地进行的。因此,搜索引擎更加重视接近页面顶部的代码 + +### 网页结构类型 + +> 创建网页结构实际就是对导航栏、栏目及正文内容这三大页面基本组成元素进行组织布局。 + +## 搜索引擎优化工具 + +### 关键字查询工具 + +> 关键字查询工具是指可以提供指定关键字的搜索量及相关信息的工具。这些工具可以帮助我们寻找与网站相关的关键字,常见的关键字查询工具包括 overtrue 关键字选择器、百度火爆地带、百度指数、 Google 趋势及 Google Adwords 关键字选择工具 + +### Google 管理员工具 + +> Google 管理员工具是 Google 为了加强网站管理员间的互动及提高搜索质量而退出的一款网站管理员工具。通过 Google 管理员工具,网站管理员可以了解 Google 查看网站的方式,并与 Google 协调工作,进一步提高网站的友好性 + +### Sitemap 生成器 + +> 可以利用相关工具生成 Sitemap,并将入口置于站点首页底部,便于爬虫快速爬取整站 + +**《初探SEO》原文链接:[https://blog.maplemark.cn/2019/03/初探-seo.html](https://blog.maplemark.cn/2019/03/初探-seo.html)** \ No newline at end of file diff --git "a/docs/04.Web/02.CSS\351\200\211\346\213\251\345\231\250\347\232\204\345\210\206\347\261\273.md" "b/docs/04.Web/02.CSS\351\200\211\346\213\251\345\231\250\347\232\204\345\210\206\347\261\273.md" new file mode 100644 index 0000000..b055108 --- /dev/null +++ "b/docs/04.Web/02.CSS\351\200\211\346\213\251\345\231\250\347\232\204\345\210\206\347\261\273.md" @@ -0,0 +1,268 @@ +# CSS 选择器的分类 + +## 基本规则 + +> 通过 CSS 可以向文档中的一组元素类型应用某些规则 + +> 利用 CSS,可以创建易于修改和编辑的规则,且能很容易地将其应用到定义的所有文本元素 + +### 规则结构 + +> 每个规则都有两个基本部分:选择器和声明块;声明块由一个或多个声明组成;每个声明则是一个属性-值对 + +> 每个样式表由一系列规则组成 + +![规则的结构](./assets/css-规则的结构.png) + +> 如规则左边所示,选择器定义了将影响文档中的哪些部分 + +> 规则右边包含声明块,它由一个或多个声明组成。每个声明是一个 CSS 属性和该属性的值的组合 + +### 元素选择器 + +> 最常见的选择器往往是 HTML 元素。文档的元素就是最基本的选择器 + +### 声明和关键字 + +> 声明块包含一个或多个声明。声明总有如下格式:一个属性后面跟一个冒号,再后面是一个值,然后是一个分号。冒号和分号后面可以有0个或多个空格 + +> 如果一个属性的值可以取多个关键字,在这种情况下,关键字通常由空格分隔。并不是所偶属性都能接受多个关键字,不过确实有许多属性是这样 + +```css +p {font: medium Helvetica;} +``` + +## 选择器 + +### 通配选择器 + +> CSS2引入了一种新的简单选择器,称为通配选择器(universal selector),显示为一个星号(\*)。这个选择器可以与任何元素匹配,就像是一个通配符 + +```css +* {color: red;} +``` + +### 类选择器 + +> 要应用样式而不考虑具体涉及的元素,最常用的方法就是使用类选择器。在使用类选择器之前,需要修改具体的文档标记,以便选择器正常工作 + +> 为了将一个类选择器的样式与元素关联,必须将 class 属性指定为一个适当的值 + +```css +*.warning {font-weight: bold;} +p.warning {font-weight: bold;} +.warning {font-weight: bold;} +``` + +#### 多类选择器 + +```css +.warning {font-weight: bold;} +.urgent {font-style: italic;} +.warning.urgent {background: silver;} +``` + +### ID 选择器 + +> ID 选择器前面有一个 # 号 + +> ID 选择器不引用 class 属性的值 + +> 在一个 HTML 文档中,ID 选择器会使用一次,且仅一次 + +```css +*#first-para {font-weight: bold;} +#first-para {font-weight: bold;} +``` + +### 属性选择器 + +> 在某些标记语言中,不能使用类和 ID 选择器。为了解决这个问题,CSS2引入了属性选择器(attribute selector),它可以根据元素的属性及属性值来选择元素 + +#### 简单属性选择 + +> 如果希望选择某个属性的元素,而不讨论该属性的值是什么,可以使用一个简单属性选择器 + +```html +

Hello

+

Serenity

+

Fooling

+``` + +```css +h1[class] {color: silver;} +``` + +```css +img[alt] {border: 3px solid red;} /*对所有带有 alt 属性的图像应用样式*/ +*[title] {font-weight: bold;} /*包含标题(title)信息的所有元素变为粗体显示*/ +``` + +#### 根据具体属性值选择 + +> 除了选择有某些属性的元素,还可以进一步缩小选择范围,只选择有特定属性值的元素 + +```css +/*将指向 Web 服务器上某个特定超链接变成粗体*/ +a[href="https://blog.maplemark.cn/"] {font-weight: bold;} +``` + +```html +Venus +Earth +Mars +``` +```css +/*将第二个元素文本变成粗体*/ +planet[moons="1"] { + font-weight: bold; +} +``` + +#### 根据部分属性值选择 + +> 如果属性能接受词列表(词之间用空格分隔),可以根据其中的任意一个词进行选择 + +```css +img[title~="Figure"] {border: 1px solid gray;} +``` + +- 子串匹配属性选择器 + +|类型|描述| +|-|-| +|[foo^="bar"]|选择 foo 属性值以"bar"开头的所有元素| +|[foo$="bar"]|选择 foo 属性值以"bar"结尾的所有元素| +|[foo*="bar"]|选择 foo 属性值中包含子串"bar"的所有元素| + +#### 特定属性选择类型 + +```html +

Hello!

+

Greetings!

+
G'day!
+

Bonjour!

+

Jrooana!

+``` + +```css +*[lang|="en"] {color: white;} +``` + +> 这种属性选择器最常见的用途是匹配语言值 + +### 后代选择器 + +![文档树结构](./assets/css-文档树结构.png) + +> 通过文档树结构,可以很形象的理解什么是后代选择器(descendant selector),也称为包含选择器/上下文选择器。定义后代选择器就是来创建一些规则,它们仅在某些结构中起作用,而在另外一些结构中不起作用 + +```html +

文字一

+

文字一后代

+
文字二
+

文字三

+``` + +```css +.row p{color: red;} +``` + +#### 选择子元素 + +> 在某些情况下,可能并不想选择一个任意的后代元素;而是希望缩小范围,只选择另一个元素的子元素 + +```css +.row > p{color: red;} +``` + +#### 选择相邻兄弟元素 + +```html +
    +
  1. List item 1
  2. +
  3. List item 1
  4. +
  5. List item 1
  6. +
  7. List item 1
  8. +
+
    +
  • A list item
  • +
  • Another list item
  • +
  • Yet Another list item
  • +
  • Lat list item
  • +
+``` + +```css +ol + ul {font-weight: bold;} /*将命中 ul*/ +``` + +> ul 必须紧跟在 ol 后面 + +### 伪类选择器 + +> 锚类型称为伪类 + +#### 链接伪类 + +CSS2.1定义了两个只应用于超链接的伪类 + +|伪类名|描述| +|-|-| +|:link|指示作为超链接并指向一个未访问地址的所有锚| +|:visited|指示作为已访问超链接的所有锚| + +```css +a {color: black;} +a:link {color: bule;} +a:visited {color: red;} +``` + +#### 动态伪类 + +> CSS2.1定义了3个动态伪类,它们可以根据用户行为改变文档的外观 + +|伪类名|描述| +|-|-| +|:focus|指示当前拥有输入焦点的元素| +|:hover|指示鼠标指针停留在哪个元素上| +|:active|指示被用户输入激活的元素| + +> 伪类顺序:link-visited-focus-hover-active + +#### 选择第一个子元素 + +> 可以使用静态伪类:first-child 来选择元素的第一个子元素 + +```css +p:first-child {font-weight: bold;} +``` + +### 伪元素选择器 + +> 就像伪类为锚指定幻像类一样,伪元素能够在文档中插入假象的元素,从而得到某种效果。CSS2.1中定义了4个伪元素:设置首字母样式、设置第一行样式、设置之前和之后元素的样式 + +#### 设置首字母样式 + +```css +p:first-letter {color: red;} +``` + +#### 设置第一行样式 + +```css +p:first-line {color: purple;} +``` + +#### :first-letter 和 :first-line 的限制 + +> 在 CSS2 中,:first-letter 和:first-line 伪元素只能应用于标记或段落之类的块级元素,而不能应用于超链接等的行内元素 + +#### 设置之前和之后元素的样式 + +```css +p:before {color: black;} +p:after {color: red;} +``` + +**《CSS选择器的分类》原文链接:[https://blog.maplemark.cn/2019/04/css选择器的分类.html](https://blog.maplemark.cn/2019/04/css选择器的分类.html)** \ No newline at end of file diff --git "a/docs/04.Web/03.CSS-Sprite\347\232\204\345\272\224\347\224\250.md" "b/docs/04.Web/03.CSS-Sprite\347\232\204\345\272\224\347\224\250.md" new file mode 100644 index 0000000..ee4b576 --- /dev/null +++ "b/docs/04.Web/03.CSS-Sprite\347\232\204\345\272\224\347\224\250.md" @@ -0,0 +1,172 @@ +# 雪碧图CSS Sprite的应用 + +> CSS雪碧,即CSS Sprite,也有人叫它CSS精灵,是一种CSS图像合并技术,该方法是将小图标和背景图像合并到一张图片上,然后利用css的背景定位来显示需要显示的图片部分。例如常见的商品分类导航其实所有商品的背景图用的都是一个所有小图标拼凑成的大图,只是在不同类别显示对应类别的图标时,通过li背景定位到大图的对应图标的位置。背景图位置可以使用一些雪碧图生成工具的时候,生成对应的CSS样式文件里面会有对应的位置信息。 + +## 好处优点 + +> 有效减少网站的http请求数量,加速图片的显示。 + +## 条件 + +> 静态图片,图片不随用户信息的变化而变化。 + +> 小图片,容量比较小的(2-3KB) + +> 图片加载量比较大的。 + +> 注意:大图片不建议用雪碧图咯,图片那么大,拼完之后岂不是拆机无敌大咯,加载就慢了,得不偿失!!! + +## 原理 + +> 利用 css3的 background-position控制一个层可显示区域范围大小,通过一个窗口,对背景图进行滑动。 +简单来说,就是利用这个属性,设置背景图需要显示的起始位置,在通过标签来控制背景图显示的范围。 + +## background-position属性 + +![CSS sprite 1](./assets/web-css-雪碧图1.jpg) + +> 根据图所知: + +> 以左上角为(0,0)坐标 + +> x,y都是负值 + +> 综上所诉,所以background-position的取值就是背景图显示的起始坐标,形式就是background-position:0, 0; + +## 拼图 + +> 拼图可以用ps,或者网上很多在线雪碧图生成工具,可以利用这些去生成雪碧图。 + +> 其实最好的拼图就是每个图标边距是多少和图标周围的留白留多少都控制好,对css的background-position的坐标写起来有规律的话,好些很多(然而这个是我自己手动拖拉的,位置很没有规律,所以下面的坐标值都是调试过才取值的) + +![CSS sprite 2](./assets/web-css-雪碧图2.png) + +> 讲完这些了,可以来正题了,html和css代码了,下面代码是模仿生成一个菜单~~~ + +## 代码 + +```html +
+
    +
  • + +

    女装/男装/内衣

    +
  • +
  • + +

    鞋靴/箱包/配件

    +
  • +
  • + +

    童装玩具/孕产/用品

    +
  • +
  • + +

    家电/数码/手机

    +
  • +
  • + +

    美妆/洗护/保健品

    +
  • +
  • + +

    珠宝/眼镜/手表

    +
  • +
  • + +

    运动/户外/乐器

    +
  • +
  • + +

    游戏/动漫/影视

    +
  • +
  • + +

    美食/生鲜/零食

    +
  • +
  • + +

    鲜花/宠物/农资

    +
  • +
  • + +

    房产/装修/建材

    +
  • +
+
+``` + +```css +#content{ + width: 180px; + background: #f8f8f8; + border: 1px solid #bbb; +} +h3{ + margin: 0; + padding: 0; +} +ul{ + list-style: none; + padding: 0; +} +li h3{ + font-size: 14px; + font-weight: 400; +} +li{ + margin: 3px 10px 0 0; + display: block; + height: 31px; + line-height: 31px; + overflow: hidden; + border-bottom: 1px solid #dedede; + +} +li i{ + background: url(http://github.com/sprite.png); + display: inline; + width: 40px; + height: 28px; + float: left; +} +.cat-1 i{ + background-position: -7px -5px; +} +.cat-2 i{ + background-position: -2px -35px; +} +.cat-3 i{ + background-position: -7px -65px; +} +.cat-4 i{ + background-position: -7px -105px; +} +.cat-5 i{ + background-position: -7px -129px; +} +.cat-6 i{ + background-position: -7px -151px; +} +.cat-7 i{ + background-position:-60px -4px; +} +.cat-8 i{ + background-position:-56px -33px; +} +.cat-9 i{ + background-position: -56px -66px; +} +.cat-10 i{ + background-position:-60px -103px; +} +.cat-11 i{ + background-position: -51px -128px; +} +``` + +## 效果图如下 + +![CSS sprite 3](./assets/web-css-雪碧图3.jpg) + +**本文转载自 忆桐之家的博客,《雪碧图CSS Sprite的应用》** \ No newline at end of file diff --git "a/docs/04.Web/04.CSS\347\233\222\346\250\241\345\236\213.md" "b/docs/04.Web/04.CSS\347\233\222\346\250\241\345\236\213.md" new file mode 100644 index 0000000..9a4cc36 --- /dev/null +++ "b/docs/04.Web/04.CSS\347\233\222\346\250\241\345\236\213.md" @@ -0,0 +1,111 @@ +# CSS 盒模型 + +## 盒模型 + +> 网页设计中常听的属性名:内容(content)、填充(padding)、边框(border)、边界(margin), CSS盒子模式都具备这些属性。 + +> 这些属性我们可以把它转移到我们日常生活中的盒子(箱子)上来理解,日常生活中所见的盒子也就是能装东西的一种箱子,也具有这些属性,所以叫它盒子模式。 + +> CSS盒子模型就是在网页设计中经常用到的CSS技术所使用的一种思维模型。 + +## 盒模型中的边框 + +> 盒子模型的边框就是围绕着内容及补白的线,这条线你可以设置它的粗细、样式和颜色(边框三个属性)。 + +> 如下面代码为 div 来设置边框粗细为 2px、样式为实心的、颜色为红色的边框: + +```css +div{ border:2px solid red;} +``` + +> 上面是 border 代码的缩写形式,可以分开写: + +```css +div{ + border-width:2px; + border-style:solid; + border-color:red; +} +``` + +- 注意 + +> border-style(边框样式)常见样式有: + +> dashed(虚线)| dotted(点线)| solid(实线)。 + +> border-color(边框颜色)中的颜色可设置为十六进制颜色,如: + +> border-color:#888;//前面的井号不要忘掉。 + +> border-width(边框宽度)中的宽度也可以设置为: + +> thin | medium | thick(但不是很常用),最常还是用象素(px)。 + +> 现在有一个问题,如果有想为 p 标签单独设置下边框,而其它三边都不设置边框样式怎么办呢?css 样式中允许只为一个方向的边框设置样式: + +``` +div{border-bottom:1px solid red;} +``` + +> 同样可以使用下面代码实现其它三边(上、右、左)边框的设置: + +```css +border-top:1px solid red; +border-right:1px solid red; +border-left:1px solid red; +``` + +## 宽度和高度 + +> 盒模型宽度和高度和我们平常所说的物体的宽度和高度理解是不一样的,css内定义的宽(width)和高(height),指的是填充以里的内容范围。 + +> 因此一个元素实际宽度(盒子的宽度)=左边界+左边框+左填充+内容宽度+右填充+右边框+右边界。 + +## 填充 + +> 元素内容与边框之间是可以设置距离的,称之为“填充”。填充也可分为上、右、下、左(顺时针)。如下代码: + +```css +div{padding:20px 10px 15px 30px;} +``` + +> 顺序一定不要搞混。也可以分开写上面代码。 + +> 如果上、右、下、左的填充都为10px;可以这么写 + +```css +div{padding:10px;} +``` + +> 如果上下填充一样为10px,左右一样为20px,可以这么写: + +```css +div{padding:10px 20px;} +``` + +## 边界 + +> 元素与其它元素之间的距离可以使用边界(margin)来设置。边界也是可分为上、右、下、左。如下代码: + +```css +div{margin:20px 10px 15px 30px;} +``` + +> 也也可以分开写。 + +> 如果上右下左的边界都为10px;可以这么写: + +```css +div{ margin:10px;} +``` + +> 如果上下边界一样为10px,左右一样为20px,可以这么写: + +```css +div{ margin:10px 20px;} +``` + +> 总结一下:padding和margin的区别,padding在边框里,margin在边框外。 + +**本文转载自 陈浩的个人博客,《CSS 盒模型》** \ No newline at end of file diff --git "a/docs/04.Web/05.CSS\346\260\264\345\271\263\345\261\205\344\270\255\350\256\276\347\275\256.md" "b/docs/04.Web/05.CSS\346\260\264\345\271\263\345\261\205\344\270\255\350\256\276\347\275\256.md" new file mode 100644 index 0000000..56171f8 --- /dev/null +++ "b/docs/04.Web/05.CSS\346\260\264\345\271\263\345\261\205\344\270\255\350\256\276\347\275\256.md" @@ -0,0 +1,136 @@ +# CSS 水平居中设置 + +## 行内元素水平居中 + +> 如果被设置元素为文本、图片等行内元素时,水平居中是通过给父元素设置 text-align:center 来实现的。如下代码: + +```html + +
我是文本,哈哈,我想要在父容器中水平居中显示。
+ +``` + +```css +div.txtCenter{ + text-align:center; +} +``` + +## 定宽块状元素水平居中 + +> 当被设置元素为块状元素时用 text-align:center 就不起作用了,这时也分两种情况:定宽块状元素和不定宽块状元素。下面来讲讲定宽块状元素。 + +> 满足定宽和块状两个条件的元素是可以通过设置 “左右margin” 值为 “auto” 来实现居中的。我们来看个例子就是设置 div 这个块状元素水平居中 + +```html + +
我是定宽块状元素,哈哈,我要水平居中显示。
+ +``` + +```css +div{ + border:1px solid red; /*为了显示居中效果明显为 div 设置了边框*/ + width:500px; /*定宽*/ + margin:20px auto; /* margin-left 与 margin-right 设置为 auto */ +} +``` + +> 也可以写成 + +```css +margin-left:auto; +margin-right:auto; +``` + +> 注意:元素的“上下 margin” 是可以随意设置的 + +## 不定宽块状元素水平居中 + +> 在实际工作中我们会遇到需要为“不定宽度的块状元素”设置居中,比如网页上的分页导航,因为分页的数量是不确定的,所以我们不能通过设置宽度来限制它的弹性。 + +> 不定宽度的块状元素有三种方法居中(这三种方法目前使用的都比多): + +- 加入 table 标签 +- 设置 display;inline 方法 +- 设置 position:relative 和 left:50%; + +> 加入 table 标签 + +> 第一步:为需要设置的居中的元素外面加入一个 table 标签 ( 包括 、、 )。 + +> 第二步:为这个 table 设置 “左右 margin 居中”(这个和定宽块状元素的方法一样)。 + +```html +
+ + + + +
+
    +
  • 1
  • +
  • 2
  • +
  • 3
  • +
+
+
+``` + +```css +.container{ + text-align:center; +} +.container ul{ + list-style:none; + margin:0; + padding:0; + display:inline; +} +.container li{ + margin-right:8px; + display:inline; +} +``` + +> 这种方法相比第一种方法的优势是不用增加无语义标签,简化了标签的嵌套深度,但也存在着一些问题:它将块状元素的 display 类型改为 inline,变成了行内元素,所以少了一些功能,比如设定长度值。 + +> 设置 position:relative 和 left:50% + +> 通过给父元素设置 float,然后给父元素设置 position:relative 和 left:50%,子元素设置 position:relative 和 left:-50% 来实现水平居中。举例如下: + +```html + +
+
    +
  • 1
  • +
  • 2
  • +
  • 3
  • +
+
+ +``` + +```css +.container{ + float:left; + position:relative; + left:50% +} + +.container ul{ + list-style:none; + margin:0; + padding:0; + + position:relative; + left:-50%; +} +.container li{float:left;display:inline;margin-right:8px;} +``` + +> 这种方法可以保留块状元素仍以 display:block 的形式显示,优点不添加无语议表标签,不增加嵌套深度,但它的缺点是设置了 position:relative,带来了一定的副作用。 + +> 这三种方法使用得都非常广泛,各有优缺点,具体选用哪种方法,可以视具体情况而定。 + +**本文转载自 陈浩的个人博客,《CSS 水平居中设置》** \ No newline at end of file diff --git "a/docs/04.Web/06.CSS\345\236\202\347\233\264\345\261\205\344\270\255\350\256\276\347\275\256.md" "b/docs/04.Web/06.CSS\345\236\202\347\233\264\345\261\205\344\270\255\350\256\276\347\275\256.md" new file mode 100644 index 0000000..3ee9276 --- /dev/null +++ "b/docs/04.Web/06.CSS\345\236\202\347\233\264\345\261\205\344\270\255\350\256\276\347\275\256.md" @@ -0,0 +1,77 @@ +# CSS 垂直居中设置 + +## 父元素高度确定的单行文本 + +> 父元素高度确定的单行文本的竖直居中的方法是通过设置父元素的 height 和 line-height 高度一致来实现的。如下代码 + +```html +
+ hi,imooc! +
+``` + +```css +.container{ + height:100px; + line-height:100px; + background:#999; +} +``` + +## 父元素高度确定的多行文本 + +> 父元素高度确定的多行文本、图片、块状元素的竖直居中的方法有两种 + +### 方法一 + +> 使用插入 table (包括tbody、tr、td)标签,同时设置 vertical-align:middle + +> 说到竖直居中,css 中有一个用于竖直居中的属性 vertical-align,但这个样式只有在父元素为 td 或 th 时,才会生效。所以又要插入 table 标签了。下面看一下例子: + +```html + +
+
+

看我是否可以居中。

+

看我是否可以居中。

+

看我是否可以居中。

+

看我是否可以居中。

+
+
+ +``` + +```css +table td{height:500px;background:#ccc} +``` + +> 因为 td 标签默认情况下就默认设置了 vertical-align 为 middle,所以我们不需要显式地设置了 + +### 方法二 + +> 在 chrome、firefox 及 IE8 以上的浏览器下可以设置块级元素的 display 为 table-cell,激活 vertical-align 属性,但注意 IE6、7 并不支持这个样式 + +```html +
+
+

看我是否可以居中。

+

看我是否可以居中。

+

看我是否可以居中。

+

看我是否可以居中。

+

看我是否可以居中。

+
+
+``` + +```css +.container{ + height:300px; + background:#ccc; + display:table-cell;/*IE8以上及Chrome、Firefox*/ + vertical-align:middle;/*IE8以上及Chrome、Firefox*/ +} +``` + +> 这种方法的好处是不用添加多余的无意义的标签,但缺点也很明显,它的兼容性不是很好,不兼容 IE6、7 + +**本文转载自 陈浩的个人博客,《CSS 垂直居中设置》** \ No newline at end of file diff --git "a/docs/04.Web/07.flex\345\270\203\345\261\200\347\232\204\345\237\272\346\234\254\346\246\202\345\277\265.md" "b/docs/04.Web/07.flex\345\270\203\345\261\200\347\232\204\345\237\272\346\234\254\346\246\202\345\277\265.md" new file mode 100644 index 0000000..1163012 --- /dev/null +++ "b/docs/04.Web/07.flex\345\270\203\345\261\200\347\232\204\345\237\272\346\234\254\346\246\202\345\277\265.md" @@ -0,0 +1,326 @@ +# flex 布局的基本概念 + +> Flexible Box 模型,通常被称为 flexbox,是一种一维的布局模型。它给 flexbox 的子元素之间提供了强大的空间分布和对齐能力。本文给出了 flexbox 的主要特性,更多的细节将在别的文档中探索。 + +> 我们说 flexbox 是一种一维的布局,是因为一个 flexbox 一次只能处理一个维度上的元素布局,一行或者一列。作为对比的是另外一个二维布局 CSS Grid Layout,可以同时处理行和列上的布局。 + +## flexbox 的两根轴线 + +> 当使用 flex 布局时,首先想到的是两根轴线 — 主轴和交叉轴。主轴由 flex-direction 定义,另一根轴垂直于它。我们使用 flexbox 的所有属性都跟这两根轴线有关, 所以有必要在一开始首先理解它。 + +### 主轴 + +> 主轴由 flex-direction 定义,可以取4个值: + +- row +- row-reverse +- column +- column-reverse + +> 如果你选择了 row 或者 row-reverse,你的主轴将沿着 inline 方向延伸。 + +![flex-图1](./assets/web-flex-01.png) + +> 选择 column 或者 column-reverse 时,你的主轴会沿着上下方向延伸 — 也就是 block 排列的方向。 + +![flex-图2](./assets/web-flex-02.png) + +### 交叉轴 + +> 交叉轴垂直于主轴,所以如果你的flex-direction (主轴) 设成了 row 或者 row-reverse 的话,交叉轴的方向就是沿着列向下的。 + +![flex-图3](./assets/web-flex-03.png) + +> 如果主轴方向设成了 column 或者 column-reverse,交叉轴就是水平方向。 + +![flex-图4](./assets/web-flex-04.png) + +> 理解主轴和交叉轴的概念对于对齐 flexbox 里面的元素是很重要的;flexbox 的特性是沿着主轴或者交叉轴对齐之中的元素。 + +## 起始线和终止线 + +> 另外一个需要理解的重点是 flexbox 不会对文档的书写模式提供假设。过去,CSS的书写模式主要被认为是水平的,从左到右的。现代的布局方式涵盖了书写模式的范围,所以我们不再假设一行文字是从文档的左上角开始向右书写, 新的行也不是必须出现在另一行的下面。 + +> 你可以在接下来的文章中学到更多 flexbox 和书写模式关系的详细说明。下面的描述是来帮助我们理解为什么不用上下左右来描述 flexbox 元素的方向。 + +> 如果 flex-direction 是 row ,并且我是在书写英文,那么主轴的起始线是左边,终止线是右边。 + +![flex-图5](./assets/web-flex-05.png) + +> 如果我在书写阿拉伯文,那么主轴的起始线是右边,终止线是左边。 + +![flex-图6](./assets/web-flex-06.png) + +> 在这两种情况下,交叉轴的起始线是flex容器的顶部,终止线是底部,因为两种语言都是水平书写模式。 + +> 之后,你会觉得用起始和终止来描述比左右更合适,这会对你理解其他相同模式的布局方法(例如:CSS Grid Layout)起到帮助的作用。 + +## Flex 容器 + +> 文档中采用了 flexbox 的区域就叫做 flex 容器。为了创建 flex 容器, 我们把一个容器的 display 属性值改为 flex 或者 inline-flex。 完成这一步之后,容器中的直系子元素就会变为 flex 元素。所有CSS属性都会有一个初始值,所以 flex 容器中的所有 flex 元素都会有下列行为: + +- 元素排列为一行 (flex-direction 属性的初始值是 row)。 +- 元素从主轴的起始线开始。 +- 元素不会在主维度方向拉伸,但是可以缩小。 +- 元素被拉伸来填充交叉轴大小。 +- flex-basis 属性为 auto。 +- flex-wrap 属性为 nowrap。 + +> 这会让你的元素呈线形排列,并且把自己的大小作为主轴上的大小。如果有太多元素超出容器,它们会溢出而不会换行。如果一些元素比其他元素高,那么元素会沿交叉轴被拉伸来填满它的大小。 + +```html +
+
One
+
Two
+
Three +
has +
extra +
text +
+
+``` + +```css +.box { + display: flex; +} +``` + +### 更改flex方向 flex-direction + +> 在 flex 容器中添加 flex-direction 属性可以让我们更改 flex 元素的排列方向。设置 flex-direction: row-reverse 可以让元素沿着行的方向显示,但是起始线和终止线位置会交换。 + +> 把 flex 容器的属性 flex-direction 改为 column ,主轴和交叉轴交换,元素沿着列的方向排列显示。改为 column-reverse ,起始线和终止线交换。 + +> 下面的例子中,flex-direction 值为 row-reverse。尝试使用其他的值 row ,column,column-reverse,看看内容会发生什么改变。 + +```html +
+
One
+
Two
+
Three
+
+``` + +```css +.box { + display: flex; + flex-direction: row-reverse; +} +``` + +## 用flex-wrap实现多行Flex容器 + +> 虽然flexbox是一维模型,但可以使我们的flex项目应用到多行中。 在这样做的时候,您应该把每一行看作一个新的flex容器。 任何空间分布都将在该行上发生,而不影响该空间分布的其他行。 + +> 为了实现多行效果,请为属性flex-wrap添加一个属性值wrap。 现在,如果您的项目太大而无法全部显示在一行中,则会换行显示。 下面的实时例子包含已给出宽度的项目,对于flex容器,项目的子元素总宽度大于容器最大宽度。 由于flex-wrap的值设置为wrap,所以项目的子元素换行显示。若将其设置为nowrap,这也是初始值,它们将会缩小以适应容器,因为它们使用的是允许缩小的初始Flexbox值。 如果项目的子元素无法缩小,使用nowrap会导致溢出,或者缩小程度还不够小。 + +```html +
+
One
+
Two
+
Three
+
+``` + +```css +.box { + display: flex; + flex-wrap: wrap; +} +``` + +## 简写属性 flex-flow + +> 你可以将两个属性 flex-direction 和 flex-wrap 组合为简写属性 flex-flow。第一个指定的值为 flex-direction ,第二个指定的值为 flex-wrap. + +> 在下面的例子中,尝试将第一个值修改为 flex-direction 的允许取值之一,即 row, row-reverse, column 或 column-reverse, 并尝试将第二个指定值修改为 wrap 或 nowrap。 + + +```html +
+
One
+
Two
+
Three
+
+``` + +```css +.box { + display: flex; + flex-flow: row wrap; +} +``` + +## flex 元素上的属性 + +> 为了更好地控制 flex 元素,有三个属性可以作用于它们: + +- flex-grow +- flex-shrink +- flex-basis + +> 在这里,我们只会大概介绍一下它们的用法,更详细的细节请参阅其它的文章。 + +> 在考虑这几个属性的作用之前,需要先了解一下 布局空白 available space 这个概念。这几个 flex 属性的作用其实就是改变了 flex 容器中的布局空白的行为。同时,布局空白对于 flex 元素的对齐行为也是很重要的。 + +> 假设在 1 个 500px 的容器中,我们有 3 个 100px 宽的元素,那么这 3 个元素需要占 300px 的宽,剩下 200px 的布局空白。在默认情况下, flexbox 的行为会把这 200px 的空白留在最后一个元素的后面。 + +![flex-图7](./assets/web-flex-07.png) + +> 如果期望这些元素能自动地扩展去填充满剩下的空白,那么我们需要去控制布局空白在这几个元素间如何分配,这就是元素上的那些 flex 属性要做的事。 + +### Flex 元素属性:flex-basis + +> flex-basis 定义了该元素的布局空白(available space)的基准值。 该属性的默认值是 auto 。此时,浏览器会检测这个元素是否具有确定的尺寸。 在上面的例子中, 所有元素都设定了宽度(width)为100px,所以 flex-basis 的值为100px。 + +> 如果没有给元素设定尺寸,flex-basis 的值采用元素内容的尺寸。这就解释了:我们给只要给Flex元素的父元素声明 display: flex ,所有子元素就会排成一行,且自动分配小大以充分展示元素的内容。 + +### Flex 元素属性:flex-grow + +> flex-grow 若被赋值为一个正整数, flex 元素会以 flex-basis 为基础,沿主轴方向增长尺寸。这会使该元素延展,并占据此方向轴上的布局空白(available space)。如果有其他元素也被允许延展,那么他们会各自占据布局空白的一部分。 + +> 如果我们给上例中的所有元素设定 flex-grow 值为1, 容器中的布局空白会被这些元素平分。它们会延展以填满容器主轴方向上的空间。 + +> flex-grow 属性可以按比例分配空间。如果第一个元素 flex-grow 值为2, 其他元素值为1,则第一个元素将占有2/4(上例中,即为 200px 中的 100px), 另外两个元素各占有1/4(各50px)。 + +### Flex 元素属性: flex-shrink + +> flex-grow属性是处理flex元素在主轴上增加空间的问题,相反flex-shrink属性是处理flex元素收缩的问题。如果我们的容器中没有足够排列flex元素的空间,那么可以把flex元素flex-shrink属性设置为正整数来缩小它所占空间到flex-basis以下。与flex-grow属性一样,可以赋予不同的值来控制flex元素收缩的程度 —— 给flex-shrink属性赋予更大的数值可以比赋予小数值的同级元素收缩程度更大。 + +### Flex属性的简写 + +> 你可能很少看到 flex-grow,flex-shrink,和 flex-basis 属性单独使用,而是混合着写在 flex 简写形式中。 Flex 简写形式允许你把三个数值按这个顺序书写 — flex-grow,flex-shrink,flex-basis。 + +> 你可以在下面的实例中尝试把flex简写形式中的数值更改为不同数值,但要记得第一个数值是 flex-grow。赋值为正数的话是让元素增加所占空间。第二个数值是flex-shrink — 正数可以让它缩小所占空间,但是只有在flex元素总和超出主轴才会生效。最后一个数值是 flex-basis;flex元素是在这个基准值的基础上缩放的。 + +```html +
+
One
+
Two
+
Three
+
+``` + +```css +.box { + display: flex; +} +.one { + flex: 1 1 auto; +} +.two { + flex: 1 1 auto; +} +.three { + flex: 1 1 auto; +} +``` + +> 大多数情况下可以用预定义的简写形式。 在这个教程中你可能经常会看到这种写法,许多情况下你都可以这么使用。下面是几种预定义的值: + +- flex: initial +- flex: auto +- flex: none +- flex: + +> flex: initial 是把flex元素重置为Flexbox的初始值,它相当于 flex: 0 1 auto。在这里 flex-grow 的值为0,所以flex元素不会超过它们 flex-basis 的尺寸。flex-shrink 的值为1, 所以可以缩小flex元素来防止它们溢出。flex-basis 的值为 auto. Flex元素尺寸可以是在主维度上设置的,也可以是根据内容自动得到的。 + +> flex: auto 等同于 flex: 1 1 auto;和上面的 flex:initial 基本相同,但是这种情况下,flex元素在需要的时候既可以拉伸也可以收缩。 + +> flex: none 可以把flex元素设置为不可伸缩。它和设置为 flex: 0 0 auto 是一样的。元素既不能拉伸或者收缩,但是元素会按具有 flex-basis: auto 属性的flexbox进行布局。 + +> 你在教程中常看到的 flex: 1 或者 flex: 2 等等。它相当于flex: 1 1 0。元素可以在flex-basis为0的基础上伸缩。 + +> 尝试在下面的实例中应用这些简写值。 + +```html +
+
One
+
Two
+
Three
+
+``` + +```css +.box { + display: flex; +} +.one { + flex: 1; +} +.two { + flex: 1; +} +.three { + flex: 1; +} +``` + +## 元素间的对齐和空间分配 + +> Flexbox的一个关键特性是能够设置flex元素沿主轴方向和交叉轴方向的对齐方式,以及它们之间的空间分配。 + +### align-items + +> align-items 属性可以使元素在交叉轴方向对齐。 + +> 这个属性的初始值为stretch,这就是为什么flex元素会默认被拉伸到最高元素的高度。实际上,它们被拉伸来填满flex容器 —— 最高的元素定义了容器的高度。 + +> 你也可以设置align-items的值为flex-start,使flex元素按flex容器的顶部对齐, flex-end 使它们按flex容器的下部对齐, 或者center使它们居中对齐. 在实例中尝试——我给出了flex容器的高度,以便你可以看到元素在容器中移动。看看如果更改 align-items的值为下列值会发生什么: + +- stretch +- flex-start +- flex-end +- center + +```html +
+
One
+
Two
+
Three +
has +
extra +
text +
+
+``` + +```css +.box { + display: flex; + align-items: flex-start; +} +``` + +### justify-content + +> justify-content属性用来使元素在主轴方向上对齐,主轴方向是通过 flex-direction 设置的方向。初始值是flex-start,元素从容器的起始线排列。但是你也可以把值设置为flex-end,从终止线开始排列,或者center,在中间排列. + +> 你也可以把值设置为space-between,把元素排列好之后的剩余空间拿出来,平均分配到元素之间,所以元素之间间隔相等。或者使用space-around,使每个元素的左右空间相等。 + +> 在实例中尝试下列justify-content属性的值: + +- stretch +- flex-start +- flex-end +- center +- space-around +- space-between + +```html +
+
One
+
Two
+
Three
+
+``` + +```css +.box { + display: flex; + justify-content: flex-start; +} +``` + +**本文转载自 MDN web docs,《flex 布局的基本概念》** \ No newline at end of file diff --git "a/docs/04.Web/08.CSS-Position\345\255\246\344\271\240.md" "b/docs/04.Web/08.CSS-Position\345\255\246\344\271\240.md" new file mode 100644 index 0000000..3f5e0c2 --- /dev/null +++ "b/docs/04.Web/08.CSS-Position\345\255\246\344\271\240.md" @@ -0,0 +1,61 @@ +# CSS Position学习 + +## CSS Position有四个属性: + +- relative +- absolute +- fixed +- static(默认) + +## 样例 + +```html +
+
sub1
+
sub2
+
+``` + +sub1和sub2是同级关系,parent是它们的父级元素。 + +## relative(相对定位) + +相对定位指的是相对于这个元素原位置的定位,且会占住原来的位置。 + +所谓原位置指不设置relative属性时它的位置(既static属性时的位置) + +relative偏移相对的是margin的左上侧。 + +例如对sub1设置relative属性后,会根据top,right,bottom,left属性偏移,而sub2的位置不变(sub1会占住原来的位置) + +![css position 图1](./assets/web-css-position-01.png) + +再对sub2设置relative属性,它也会相对其原来的位置偏移(sub2位置还会被占着) + +![css position 图2](./assets/web-css-position-02.png) + +## absolute(绝对定位) + +绝对定位是根据其最近进行定位的父对象的 padding 的左上角进行定位,基本分为以下两种情况: + +- 例如对sub1设置absolute,如果sub1的父级元素(parent或者其父级元素)设置了absolute或relative,那么sub1就会相对这个父元素定位。 + +- 如果父级元素都没有设置absolute或relative,那sub1相对body定位。 + +这时由于sub1的位置“腾出来了”,sub2就会跑到sub1的位置(也可以理解sub1浮起来了,dreamweaver中叫做层),它的文档流就会基于parent。 + +![css position 图3](./assets/web-css-position-03.png) + +如果再对sub2设置absolute,那其也是相对parent的。 + +![css position 图4](./assets/web-css-position-04.png) + +## fixed + +fixed是特殊的absolute,即fixed总是以body为定位对象的,按照浏览器的窗口进行定位。 + +## static(默认) + +position的默认值,一般不设置position属性时,会按照正常的文档流进行排列。 + +**本文转载自 小磊的博客,《CSS Position学习》** \ No newline at end of file diff --git "a/docs/04.Web/09.JavaScript\347\232\204\346\200\247\350\203\275\344\274\230\345\214\226-\345\212\240\350\275\275\345\222\214\346\211\247\350\241\214.md" "b/docs/04.Web/09.JavaScript\347\232\204\346\200\247\350\203\275\344\274\230\345\214\226-\345\212\240\350\275\275\345\222\214\346\211\247\350\241\214.md" new file mode 100644 index 0000000..2c06119 --- /dev/null +++ "b/docs/04.Web/09.JavaScript\347\232\204\346\200\247\350\203\275\344\274\230\345\214\226-\345\212\240\350\275\275\345\222\214\346\211\247\350\241\214.md" @@ -0,0 +1,288 @@ +# JavaScript 的性能优化:加载和执行 + +## 概述 + +无论当前 JavaScript 代码是内嵌还是在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成。JavaScript 执行过程耗时越久,浏览器等待响应用户输入的时间就越长。浏览器在下载和执行脚本时出现阻塞的原因在于,脚本可能会改变页面或 JavaScript 的命名空间,它们对后面页面内容造成影响。一个典型的例子就是在页面中使用document.write()。例如清单 1 + +清单 1 JavaScript 代码内嵌示例 +```html + + + Source Example + + +

+ +

+ + +``` + +当浏览器遇到` + + + + + +

Hello world!

+ + +``` + +然而这种常规的做法却隐藏着严重的性能问题。在清单 2 的示例中,当浏览器解析到 ` + + + + +``` + +这段代码展示了在 HTML 文档中放置` +``` + +带有 defer 属性的` + + + + +``` + +这段代码在页面处理过程中弹出三次对话框。不支持 defer 属性的浏览器的弹出顺序是:“defer”、“script”、“load”。而在支持 defer 属性的浏览器上,弹出的顺序则是:“script”、“defer”、“load”。请注意,带有 defer 属性的` +``` + +运行结果为: + +![JavaScript DOM 图3](./assets/web-javascript-dom-03.png) + +### 4.4 createDocumentFragment + +DocumentFragments 是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素所代替。 +因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(reflow)(对元素位置和几何上的计算)。因此,使用文档片段document fragments 通常会起到优化性能的作用。 + +语法 + +```javascript +let fragment = document.createDocumentFragment(); +``` + +例子: +```html + +
    + + +``` +运行结果为: +![JavaScript DOM 图4](./assets/web-javascript-dom-04.png) + +### 4.5 节点创建型API总结 + +节点创建型API主要包括createElement,createTextNode,cloneNode和createDocumentFragment四个方法,需要注意下面几点: +(1)它们创建的节点只是一个孤立的节点,要通过appendChild添加到文档中 +(2)cloneNode要注意如果被复制的节点是否包含子节点以及事件绑定等问题 +(3)使用createDocumentFragment来解决添加大量节点时的性能问题 + +## 5 页面修改型API + +前面我们提到节点创建型API,它们只是创建节点,并没有真正修改到页面内容,而是要调用·appendChild·来将其添加到文档树中。我在这里将这类会修改到页面内容归为一类。 +修改页面内容的api主要包括:appendChild,insertBefore,removeChild,replaceChild。 + +### 5.1 appendChild + +appendChild我们在前面已经用到多次,就是将指定的节点添加到调用该方法的节点的子元素的末尾。 + +语法 + +```javascript +parent.appendChild(child); +``` + +child节点将会作为parent节点的最后一个子节点。 +appendChild这个方法很简单,但是还有有一点需要注意:如果被添加的节点是一个页面中存在的节点,则执行后这个节点将会添加到指定位置,其原本所在的位置将移除该节点,也就是说不会同时存在两个该节点在页面上,相当于把这个节点移动到另一个地方。 +如果child绑定了事件,被移动时,它依然绑定着该事件。 + +例子: +```html + +
    + 要被添加的节点 +
    +
    +
    +
    +
    + 要移动的位置 +
    + + + +``` +运行结果: +![JavaScript DOM 图5](./assets/web-javascript-dom-05.gif) + +### 5.2 insertBefore + +insertBefore用来添加一个节点到一个参照节点之前 + +语法 + +```javascript +parentNode.insertBefore(newNode,refNode); +``` + +parentNode表示新节点被添加后的父节点 +newNode表示要添加的节点 +refNode表示参照节点,新节点会添加到这个节点之前 + +例子: +```html + +
    + 父节点 +
    + 子元素 +
    +
    + + + +``` +运行结果: +![JavaScript DOM 图6](./assets/web-javascript-dom-06.gif) + + +关于第二个参数参照节点还有几个注意的地方: +(1)refNode是必传的,如果不传该参数会报错 +(2)如果refNode是undefined或null,则insertBefore会将节点添加到子元素的末尾 + +### 5.3 removeChild + +删除指定的子节点并返回 + +语法 + +```javascript +var deletedChild = parent.removeChild(node); +``` + +deletedChild指向被删除节点的引用,它等于node,被删除的节点仍然存在于内存中,可以对其进行下一步操作。 +注意:如果被删除的节点不是其子节点,则程序将会报错。我们可以通过下面的方式来确保可以删除: + +```javascript +if(node.parentNode){ +​ node.parentNode.removeChild(node); +} +``` + +运行结果: +![JavaScript DOM 图7](./assets/web-javascript-dom-07.gif) + +通过节点自己获取节点的父节点,然后将自身删除 + +### 5.4 replaceChild + +replaceChild用于使用一个节点替换另一个节点 + +语法 + +``` +parent.replaceChild(newChild,oldChild); +``` + +newChild是替换的节点,可以是新的节点,也可以是页面上的节点,如果是页面上的节点,则其将被转移到新的位置 +oldChild是被替换的节点 + +例子: +```html + +
    + 父节点 +
    + 子元素 +
    +
    + + + + ``` +运行结果: +![JavaScript DOM 图8](./assets/web-javascript-dom-08.gif) + +### 5.5 页面修改型API总结 + +页面修改型API主要是这四个接口,要注意几个特点: +(1)不管是新增还是替换节点,如果新增或替换的节点是原本存在页面上的,则其原来位置的节点将被移除,也就是说同一个节点不能存在于页面的多个位置 +(2)节点本身绑定的事件会不会消失,会一直保留着。 + +## 6 节点查询型API + +### 6.1 document.getElementById + +这个接口很简单,根据元素id返回元素,返回值是Element类型,如果不存在该元素,则返回null + +语法 + +```javascript + var element = document.getElementById(id); +``` + +使用这个接口有几点要注意: +(1)元素的Id是大小写敏感的,一定要写对元素的id +(2)HTML文档中可能存在多个id相同的元素,则返回第一个元素 +(3)只从文档中进行搜索元素,如果创建了一个元素并指定id,但并没有添加到文档中,则这个元素是不会被查找到的 + +例子: +```html + +

    Some text here

    + + + + +``` +运行结果: +![JavaScript DOM 图9](./assets/web-javascript-dom-09.gif) + +### 6.2 document.getElementsByTagName + +返回一个包括所有给定标签名称的元素的HTML集合HTMLCollection。 整个文件结构都会被搜索,包括根节点。返回的 HTML集合是动态的, 意味着它可以自动更新自己来保持和 DOM 树的同步而不用再次调用document.getElementsByTagName() + +语法 + +```javascript + var elements = document.getElementsByTagName(name); +``` + +(1)如果要对HTMLCollection集合进行循环操作,最好将其长度缓存起来,因为每次循环都会去计算长度,暂时缓存起来可以提高效率 +(2)如果没有存在指定的标签,该接口返回的不是null,而是一个空的HTMLCollection +(3)name是一个代表元素的名称的字符串。特殊字符 `"*"` 代表了所有元素。 + +例子: +```html + +
    div1
    +
    div2
    + + + + +``` +这段代码中有两个按钮,一个按钮是显示HTMLCollection元素的个数,另一个按钮可以新增一个div标签到文档中。前面提到HTMLCollcetion元素是即时的表示该集合是随时变化的,也就是是文档中有几个div,它会随时进行变化,当我们新增一个div后,再访问HTMLCollection时,就会包含这个新增的div。 +运行结果: + + +### 6.3 document.getElementsByName +getElementsByName主要是通过指定的name属性来获取元素,它返回一个即时的NodeList对象 + +语法 + +```javascript +var elements = document.getElementsByName(name) +``` + +使用这个接口主要要注意几点: +(1)返回对象是一个即时的NodeList,它是随时变化的 +(2)在HTML元素中,并不是所有元素都有name属性,比如div是没有name属性的,但是如果强制设置div的name属性,它也是可以被查找到的 +(3)在IE中,如果id设置成某个值,然后传入getElementsByName的参数值和id值一样,则这个元素是会被找到的,所以最好不好设置同样的值给id和name + +例子: + +```html + + +
    +
    +
    +
    + + +``` +运行结果: +![JavaScript DOM 图10](./assets/web-javascript-dom-10.gif) + +### 6.4 document.getElementsByClassName +这个API是根据元素的class返回一个即时的HTMLCollection + +语法 +```javascript +var elements = document.getElementsByClassName(names); // or: +var elements = rootElement.getElementsByClassName(names); +``` + +elements是一个实时集合,包含了找到的所有元素 +names是一个字符串,表示要匹配的类名列表;类名通过空格分隔 +getElementsByClassName可以在任何元素上调用,不仅仅是document。调用这个方法的元素将作为本次查找的根元素 +这个接口有下面几点要注意: +(1)返回结果是一个即时的HTMLCollection,会随时根据文档结构变化 +(2)IE9以下浏览器不支持 +(3)如果要获取2个以上classname,可传入多个classname,每个用空格相隔,例如 + +```javascript +var elements = document.getElementsByClassName("test1 test2"); +``` + +例子: + +获取所有class为 'test' 的元素 + +```javascript +var elements = document.getElementsByClassName('test'); +``` + +获取所有class同时包括 'red' 和 'test' 的元素 + +```javascript +var elements = document.getElementsByClassName('red test'); +``` + +在id为'main'的元素的子节点中,获取所有class为'test'的元素 + +```javascript +var elements = document.getElementById('main').getElementsByClassName('test'); +``` + +我们还可以对任意的HTMLCollection 使用Array.prototype的方法,调用时传递HTMLCollection 作为方法的参数。这里我们将查找到所有class为'test'的div元素: + +```javascript +var testElements = document.getElementsByClassName('test'); +var testDivs = Array.prototype.filter.call(testElements, function(testElement){ +​ return testElement.nodeName === 'DIV';; +}); +``` + +### 6.5 document.querySelector和document.querySelectorAll + +这两个API很相似,通过css选择器来查找元素,注意选择器要符合CSS选择器的规则 + +#### 6.5.1 document.querySelector + +document.querySelector返回第一个匹配的元素,如果没有匹配的元素,则返回null + +语法 + +```javascript +var element = document.querySelector(selectors); +``` + +注意,由于返回的是第一个匹配的元素,这个api使用的深度优先搜索来获取元素。 + +例子: +```html + +
    +
    + 第三级的span +
    +
    +
    + 同级的第二个div +
    + + + +``` +两个class都包含“test”的元素,一个在文档树的前面,但是它在第三级,另一个在文档树的后面,但它在第一级,通过querySelector获取元素时,它通过深度优先搜索,拿到文档树前面的第三级的元素。 +运行结果: +![JavaScript DOM 图11](./assets/web-javascript-dom-11.gif) + +#### 6.5.2 document.querySelectorAll + +返回的是所有匹配的元素,而且可以匹配多个选择符 +语法 + +```javascript +var elementList = document.querySelectorAll(selectors); +``` + +elementList是一个静态的NodeList类型的对象 +selectors是一个由逗号连接的包含一个或多个CSS选择器的字符串 +如果selectors参数中包含CSS伪元素,则返回一个空的elementList +例子: + +```javascript +var matches = document.querySelectorAll("div.note, div.alert"); +``` + +返回一个文档中所有的class为"note"或者"alert"的div元素 +```html + +
    + class为test +
    +
    + id为test +
    + + + +``` + +这段代码通过querySelectorAll,使用id选择器和class选择器选择了两个元素,并依次输出其内容。要注意两点: +(1)querySelectorAll也是通过深度优先搜索,搜索的元素顺序和选择器的顺序无关 +(2)返回的是一个非即时的NodeList,也就是说结果不会随着文档树的变化而变化 +兼容性问题:querySelector和querySelectorAll在ie8以下的浏览器不支持。 + +运行结果: +![JavaScript DOM 图12](./assets/web-javascript-dom-12.gif) + + +## 7 节点关系型API + +在html文档中的每个节点之间的关系都可以看成是家谱关系,包含父子关系,兄弟关系等等 + +### 7.1 父关系型API + +#### 7.1.1 parentNode + +每个节点都有一个parentNode属性,它表示元素的父节点。Element的父节点可能是Element,Document或DocumentFragment + +#### 7.1.2 parentElement + +返回元素的父元素节点,与parentNode的区别在于,其父节点必须是一个Element,如果不是,则返回null + +### 7.2 子关系型APPI + +#### 7.2.1 childNodes + +返回一个即时的NodeList,表示元素的子节点列表,子节点可能会包含文本节点,注释节点等 + +#### 7.2.2 children: + +一个即时的HTMLCollection,子节点都是Element,IE9以下浏览器不支持 +children属性为只读属性,对象类型为HTMLCollection,你可以使用elementNodeReference.children[1].nodeName来获取某个子元素的标签名称 + +#### 7.2.3 firstChild + +只读属性返回树中节点的第一个子节点,如果节点是无子节点,则返回 null + +#### 7.2.4 lastChild + +返回当前节点的最后一个子节点。如果父节点为一个元素节点,则子节点通常为一个元素节点,或一个文本节点,或一个注释节点。如果没有子节点,则返回null + +#### 7.2.5 hasChildNodes + +返回一个布尔值,表明当前节点是否包含有子节点. + +### 7.3 兄弟关系型API + +#### 7.3.1 previousSibling + +返回当前节点的前一个兄弟节点,没有则返回null +Gecko内核的浏览器会在源代码中标签内部有空白符的地方插入一个文本结点到文档中.因此,使用诸如Node.firstChild和Node.previousSibling之类的方法可能会引用到一个空白符文本节点, 而不是使用者所预期得到的节点 + +#### 7.3.2 previousElementSibling + +previousElementSibling返回当前元素在其父元素的子元素节点中的前一个元素节点,如果该元素已经是第一个元素节点,则返回null,该属性是只读的。注意IE9以下浏览器不支持 + +#### 7.3.3 nextSibling + +Node.nextSibling是一个只读属性,返回其父节点的childNodes列表中紧跟在其后面的节点,如果指定的节点为最后一个节点,则返回null +Gecko内核的浏览器会在源代码中标签内部有空白符的地方插入一个文本结点到文档中.因此,使用诸如Node.firstChild和Node.previousSibling之类的方法可能会引用到一个空白符文本节点, 而不是使用者所预期得到的节点 + +#### 7.3.4 nextElementSibling + +nextElementSibling返回当前元素在其父元素的子元素节点中的后一个元素节点,如果该元素已经是最后一个元素节点,则返回null,该属性是只读的。注意IE9以下浏览器不支持 + +## 8 元素属性型API + +### 8.1 setAttribute + +设置指定元素上的一个属性值。如果属性已经存在,则更新该值; 否则将添加一个新的属性用指定的名称和值 + +语法 + +```javascript +element.setAttribute(name, value); +``` + +其中name是特性名,value是特性值。如果元素不包含该特性,则会创建该特性并赋值。 + +例子: +```html + +
    ABC
    + + +``` +运行结果: +![JavaScript DOM 图13](./assets/web-javascript-dom-13.png) + + +如果元素本身包含指定的特性名为属性,则可以世界访问属性进行赋值,比如下面两条代码是等价的: + +```javascript +element.setAttribute("id","test"); +element.id = "test"; +``` + +### 8.2 getAttribute + +getAttribute()返回元素上一个指定的属性值。如果指定的属性不存在,则返回null或""(空字符串) + +语法 + +```javascript +let attribute = element.getAttribute(attributeName); +``` + +attribute是一个包含attributeName属性值的字符串。attributeName是你想要获取的属性值的属性名称 + +例子: +```html + +
    ABC
    + + +``` +运行结果: +![JavaScript DOM 图14](./assets/web-javascript-dom-14.png) + +### 8.3 removeAttribute + +removeAttribute()从指定的元素中删除一个属性 + +语法 + +```javascript +element.removeAttribute(attrName) +``` + +attrName是一个字符串,将要从元素中删除的属性名 + +例子: +```html + +
    ABC +
    + + +``` +在运行之前div有个style="color:red"的属性,在运行之后这个属性就被删除了 + +运行结果: +![JavaScript DOM 图15](./assets/web-javascript-dom-15.png) + + +## 9 元素样式型API + +### 9.1 window.getComputedStyle + +Window.getComputedStyle()方法给出应用活动样式表后的元素的所有CSS属性的值,并解析这些值可能包含的任何基本计算 +假设某个元素并未设置高度而是通过其内容将其高度撑开,这时候要获取它的高度就要用到getComputedStyle + +语法 + +```javascript +var style = window.getComputedStyle(element[, pseudoElt]); +``` + +element是要获取的元素,pseudoElt指定一个伪元素进行匹配。 +返回的style是一个CSSStyleDeclaration对象。 +通过style可以访问到元素计算后的样式 + +### 9.2 getBoundingClientRect + +getBoundingClientRect用来返回元素的大小以及相对于浏览器可视窗口的位置 + +语法 + +```javascript +var clientRect = element.getBoundingClientRect(); +``` + +clientRect是一个DOMRect对象,包含left,top,right,bottom,它是相对于可视窗口的距离,滚动位置发生改变时,它们的值是会发生变化的。除了IE9以下浏览器,还包含元素的height和width等数据 + +### 9.3 直接修改元素的样式 + +例子: + +```javascript +elem.style.color = 'red'; +elem.style.setProperty('font-size', '16px'); +elem.style.removeProperty('color'); +``` + +### 9.4 动态添加样式规则 + +例子: + +```javascript +var style = document.createElement('style'); +style.innerHTML = 'body{color:red} #top:hover{background-color: red;color: white;}'; +document.head.appendChild(style);); +``` + +## 10 总结 + +JavaScript中的API太多了,将这些API记住并熟练使用对JavaScript的学习是有很大的提高 + +**本文转载自 YyzclYang/notes,《JavaScript操作DOM常用的API》** \ No newline at end of file diff --git "a/docs/04.Web/12.\346\265\217\350\247\210\345\231\250\347\232\204\345\220\214\346\272\220\347\255\226\347\225\245.md" "b/docs/04.Web/12.\346\265\217\350\247\210\345\231\250\347\232\204\345\220\214\346\272\220\347\255\226\347\225\245.md" new file mode 100644 index 0000000..ffb050d --- /dev/null +++ "b/docs/04.Web/12.\346\265\217\350\247\210\345\231\250\347\232\204\345\220\214\346\272\220\347\255\226\347\225\245.md" @@ -0,0 +1,135 @@ +# 浏览器的同源策略 + +同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。 + +## 同源的定义 + +如果两个页面的协议,端口(如果有指定)和主机都相同,则两个页面具有相同的源。我们也可以把它称为“协议/主机/端口 tuple”,或简单地叫做“tuple". ("tuple" ,“元”,是指一些事物组合在一起形成一个整体,比如(1,2)叫二元,(1,2,3)叫三元) + +下表给出了相对 `http://store.company.com/dir/page.html` 同源检测的示例: + +|URL|结果|原因| +|-|-|-| +|http://store.company.com/dir2/other.html |成功|只有路径不同| +|http://store.company.com/dir/inner/another.html |成功|只有路径不同| +|https://store.company.com/secure.html |失败|不同协议 ( https和http )| +|http://store.company.com:81/dir/etc.html |失败|不同端口 ( http:// 80是默认的)| +|http://news.company.com/dir/other.html |失败|不同域名 ( news和store )| + +### 源的继承 + +在页面中用 about:blank 或 javascript: URL 执行的脚本会继承打开该 URL 的文档的源,因为这些类型的 URLs 没有明确包含有关原始服务器的信息。 + +例如,about:blank 通常作为父脚本写入内容的新的空白弹出窗口的 URL(例如,通过 Window.open() 机制)。 如果此弹出窗口也包含代码,则该代码将继承与创建它的脚本相同的源。 + +> 注意:在Gecko 6.0之前,如果用户在位置栏中输入 data URLs,data URLs 将继承当前浏览器窗口中网页的安全上下文。 + +> data:URLs 获得一个新的,空的安全上下文。 + +### IE 例外 + +当涉及到同源策略时,Internet Explorer 有两个主要的不同点 + +- 授信范围(Trust Zones):两个相互之间高度互信的域名,如公司域名(corporate domains),不遵守同源策略的限制。 +- 端口:IE 未将端口号加入到同源策略的组成部分之中,因此 http://company.com:81/index.html 和 http://company.com/index.html 属于同源并且不受任何限制。 + +这些例外是非标准的,其它浏览器也未做出支持,但会助于开发基于window RT IE的应用程序。 + +## 源的更改 + +页面可能会因某些限制而改变他的源。脚本可以将 `document.domain` 的值设置为其当前域或其当前域的父域。如果将其设置为其当前域的父域,则这个较短的父域将用于后续源检查。假设 http://store.company.com/dir/other.html 文档中的一个脚本执行以下语句: + +```javascript +document.domain = "company.com"; +``` + +这条语句执行之后,页面将会成功地通过对 http://company.com/dir/page.html 的同源检测(假设http://company.com/dir/page.html 将其 document.domain 设置为“company.com”,以表明它希望允许这样做 - 更多有关信息,请参阅 document.domain )。然而,company.com 不能设置 document.domain 为 othercompany.com,因为它不是 company.com 的父域。 + +端口号是由浏览器另行检查的。任何对document.domain的赋值操作,包括 document.domain = document.domain 都会导致端口号被重写为 null 。因此 company.com:8080 不能仅通过设置 document.domain = "company.com" 来与company.com 通信。必须在他们双方中都进行赋值,以确保端口号都为 null 。 + +> 注意:使用 document.domain 来允许子域安全访问其父域时,您需要在父域和子域中设置 document.domain 为相同的值。这是必要的,即使这样做只是将父域设置回其原始值。不这样做可能会导致权限错误 + +## 跨源网络访问 + +同源策略控制了不同源之间的交互,例如在使用`XMLHttpRequest` 或 `` 标签时则会受到同源策略的约束。这些交互通常分为三类: + +- 通常允许跨域写操作(Cross-origin writes)。例如链接(links),重定向以及表单提交。特定少数的HTTP请求需要添加 preflight。 +- 通常允许跨域资源嵌入(Cross-origin embedding)。之后下面会举例说明。 +- 通常不允许跨域读操作(Cross-origin reads)。但常可以通过内嵌资源来巧妙的进行读取访问。例如可以读取嵌入图片的高度和宽度,调用内嵌脚本的方法,或availability of an embedded resource. + +以下是可能嵌入跨源的资源的一些示例: + +- `` 标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。 +- `` 标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域需要一个设置正确的Content-Type 消息头。不同浏览器有不同的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera。 +- ``嵌入图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,... +- `