diff --git a/README.md b/README.md
index 3f56082..714b187 100644
--- a/README.md
+++ b/README.md
@@ -2,11 +2,11 @@
# 如何通过饿了么 Node.js 面试
-Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过饿了么大前端的面试, 职位是 2~3 年经验的 Node.js 服务端程序员, 如果你对这个职位感兴趣或者学习 Node.js 一些进阶的内容, 那么欢迎阅读.
+Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过饿了么大前端的面试, 职位是 2~3 年经验的 Node.js 服务端程序员 (并不是全栈), 如果你对这个职位感兴趣或者学习 Node.js 一些进阶的内容, 那么欢迎围观.
需要注意的是, 本文针对的并不是零基础的同学, 你需要有一定的 JavaScript/Node.js 基础, 并且有一定的工作经验. 另外本教程的重点更准确的说是服务端基础中 Node.js 程序员需要了解的部分.
-如果你觉得大多不了解, 就不用投简历了 (这样两边都节约了时间), 如果你觉得大都有了解或者***光看大纲都都觉得很简单那么欢迎投递简历至 ElemeFe (fe.job@ele.me)***.
+如果你觉得大多不了解, 就不用投简历了 (这样两边都节约了时间), 如果你觉得大都有了解或者**光看大纲都都觉得很简单那么欢迎投递简历至 ElemeFe (fe.job@ele.me)**.
### 导读
@@ -18,6 +18,8 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
## [Js 基础问题](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md)
+> 与前端 Js 不同, 后端是直面服务器的, 更加偏向内存方面.
+
### 覆盖点
* [`[Basic]` 类型判断](https://github.com/ElemeFE/node-interview/blob/master/sections/js-basic.md#类型判断)
@@ -37,8 +39,6 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
## [模块](https://github.com/ElemeFE/node-interview/blob/master/sections/module.md)
-> 与前端 Js 不同, 后端是直面服务器的, 更加偏向内存方面, 对于一些更基础的问题也会更加关注.
-
* [`[Basic]` 模块机制](https://github.com/ElemeFE/node-interview/blob/master/sections/module.md#模块机制)
* [`[Basic]` 热更新](https://github.com/ElemeFE/node-interview/blob/master/sections/module.md#热更新)
* [`[Basic]` 上下文](https://github.com/ElemeFE/node-interview/blob/master/sections/module.md#上下文)
@@ -106,22 +106,29 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
* Stream 的 pipe 的作用是? 在 pipe 的过程中数据是引用传递还是拷贝传递? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#pipe)
* 什么是文件描述符? 输入流/输出流/错误流是什么? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#file)
* console.log 是同步还是异步? 如何实现一个 console.log? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#console)
-* 如何同步的获取用户的输入? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#如何同步的获取用户的输入)
+* 如何* `[Doc]` HTTP同步的获取用户的输入? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#如何同步的获取用户的输入)
* Readline 是如何实现的? (有思路即可) [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md#readline)
[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/io.md)
## Network
+* [`[Doc]` Net (网络)](https://github.com/ElemeFE/node-interview/blob/master/sections/network.md#net)
+* [`[Doc]` UDP/Datagram](https://github.com/ElemeFE/node-interview/blob/master/sections/network.md#udp)
+* [`[Doc]` HTTP](https://github.com/ElemeFE/node-interview/blob/master/sections/network.md#http)
+* [`[Doc]` DNS (域名服务器)](https://github.com/ElemeFE/node-interview/blob/master/sections/network.md#dns)
+* [`[Doc]` ZLIB (压缩)](https://github.com/ElemeFE/node-interview/blob/master/sections/network.md#zlib)
+* [`[Point]` RPC](https://github.com/ElemeFE/node-interview/blob/master/sections/network.md#rpc)
+
### 常见问题
-* HTTP 协议中的 POST 和 PUT 有什么区别?
-* `TCP/UDP` 的区别? `TCP` 粘包是怎么回事,如何处理? `UDP` 有粘包吗?
-* `time_wait` 是什么情况?出现过多的 `close_wait` 可能是什么原因?
+* HTTP 协议中的 POST 和 PUT 有什么区别? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/network.md#q-post-put)
+* TCP/UDP 的区别? TCP 粘包是怎么回事,如何处理? UDP 有粘包吗? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/network.md#q-tcp-udp)
+* `TIME_WAIT` 是什么情况? 出现过多的 `TIME_WAIT` 可能是什么原因? [[more]](https://github.com/ElemeFE/node-interview/blob/master/sections/network.md#q-time-wait)
* socket hang up 是什么意思? 一般什么情况下出现?
* 列举几个提高网络传输速度的办法?
-`更多整理中`
+[阅读更多](https://github.com/ElemeFE/node-interview/blob/master/sections/network.md)
## OS
@@ -134,6 +141,7 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
### 常见问题
+
* 服务器负载是什么概念? 如何计算负载?
* ulimit 是用来干什么的?
@@ -205,6 +213,8 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
## 安全
+* `[Doc]` HTTPS
+* `[Doc]` TLS/SSL
* `[Point]` XSS
* `[Point]` CSRF
* `[Point]` 中间人攻击
@@ -218,3 +228,9 @@ Hi, 欢迎来到 ElemeFE, 如标题所示本教程的目的是教你如何通过
* 如何避免中间人攻击?
`更多整理中`
+
+## 最后
+
+目前 repo 处于施工现场的情况,如果发现问题欢迎在 [issues](https://github.com/ElemeFE/node-interview/issues) 中指出。如果有比较好的问题/知识点/指正,也欢迎提 PR。
+
+另外关于 Js 基础 是个比较大的话题,在本教程不会很细致深入的讨论,更多的是列出一些重要或者更服务端更相关的地方,所以如果你拿着《Javascript 权威指南》给教程提 PR 可能不会采纳。本教程的重点更准确的说是服务端基础中 Node.js 程序员需要了解的部分。
diff --git a/assets/socket-backlog.png b/assets/socket-backlog.png
new file mode 100644
index 0000000..6a87848
Binary files /dev/null and b/assets/socket-backlog.png differ
diff --git a/assets/tcpfsm.png b/assets/tcpfsm.png
new file mode 100644
index 0000000..2488072
Binary files /dev/null and b/assets/tcpfsm.png differ
diff --git a/sections/js-basic.md b/sections/js-basic.md
index b759d26..62e9f95 100644
--- a/sections/js-basic.md
+++ b/sections/js-basic.md
@@ -17,12 +17,12 @@
## 类型判断
-Javascript 的类型判断其实是个挺折磨人的话题, 不然也不会有 Typescript 出现了.
-
-能清晰的说出变量类型以及各种 `typeof` 情况的,要么是好好看过书的,要么就是平常代码写得不少的.
+Javascript 的类型判断其实是个挺折磨人的话题, 不然也不会有 Typescript 出现了. 在类型判断的问题上, 基础上 推荐阅读 [lodash](https://github.com/lodash/lodash) 的源代码.
这类问题一般只是简单的开场, 不会因为说你不知道 `undefined == null` 的结果是 `true` 就一票否决一个人. 只是根据个人经验看来,这个问题答不清楚的有不小的概率属于基础较差. 如果你对这种问题没有任何概念, 也许要反思一下是不是该找本书过一下 Js 的基础了.
+另外在这个问题上, 对使用 TypeScript 以及 flow 同学会有一定的加分.
+
## 作用域
在面试时, 作用域并不是一个很好问的知识点, 一般会问的是 `es6 中 let 与 var 的区别`, 或者列举代码, 然后通过对代码的解读来看你对作用域的掌握比较方便.
diff --git a/sections/module.md b/sections/module.md
index b46232b..4701081 100644
--- a/sections/module.md
+++ b/sections/module.md
@@ -1,15 +1,15 @@
# 模块
-* [`[Basic]` 模块机制](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#模块机制)
-* [`[Basic]` 热更新](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#热更新)
-* [`[Basic]` 上下文](https://github.com/ElemeFE/node-interview/blob/master/sections/node-basic.md#上下文)
+* [`[Basic]` 模块机制](https://github.com/ElemeFE/node-interview/blob/master/sections/module.md#模块机制)
+* [`[Basic]` 热更新](https://github.com/ElemeFE/node-interview/blob/master/sections/module.md#热更新)
+* [`[Basic]` 上下文](https://github.com/ElemeFE/node-interview/blob/master/sections/module.md#上下文)
## 常见问题
> 如何在不重启 node 进程的情况下热更新一个 js/json 文件? 这个问题本身是否有问题?
-可以清除掉 `require` 的缓存重新 `require`, 是具体情况还可以用 VM 模块重新执行.
+可以清除掉 `require.cache` 的缓存重新 `require(xxx)`, 视具体情况还可以用 VM 模块重新执行.
当然这个问题可能是典型的 [`X-Y Problem`](http://coolshell.cn/articles/10804.html), 使用 js 实现热更新很容易碰到 v8 优化之后各地拿到缓存的引用导致热更新 js 没意义. 当然热更新 json 还是可以简单一点比如用读取文件的方式来热更新, 但是这样也不如从 redis 之类的数据库中读取比较合理.
@@ -70,13 +70,13 @@ function require(...) {
### 热更新
-从面试官的角度看, `热更新` 是很多程序常见的问题, 问的过程中可以一定程度的暴露应聘程序员的水平.
+从面试官的角度看, `热更新` 是很多程序常见的问题. 对客户端而言, 热更新意味着不用换包, 当然也包含着 md5 校验/差异更新等复杂问题; 对服务端而言, 热更新意味着服务不用重启, 这样可用性较高同时也优雅和有逼格. 问的过程中可以一定程度的暴露应聘程序员的水平.
-因为热更新意味着服务不用重启, 对于服务来说这属于高可用性的特性了. 从 PHP 转 node 的同学可能会有些想法, 比如 PHP 的代码直接刷上去就好了, 并没有什么所谓的重启. 而 node 重启看起来动作还挺大. 当然这里面的区别, 主要是与同时有 PHP 与 node 开发经验的同学可以讨论, 也是很暴露水平的.
+从 PHP 转 node 的同学可能会有些想法, 比如 PHP 的代码直接刷上去就好了, 并没有所谓的重启. 而 node 重启看起来动作还挺大. 当然这里面的区别, 主要是与同时有 PHP 与 node 开发经验的同学可以讨论, 也是很好的切入点.
在 Node.js 中做热更新代码, 牵扯到的知识点可能主要是 `require` 会有一个 `cache`, 有这个 `cache` 在, 即使你更新了 `.js` 文件, 在代码中再次 `require` 还是会拿到之前的编译好缓存在 v8 内存 (code space) 中的的旧代码. 但是如果只是单纯的清除掉 `require` 中的 `cache`, 再次 `require` 确实能拿到新的代码, 但是这时候很容易碰到各地维持旧的引用依旧跑的旧的代码的问题. 如果还要继续推行这种热更新代码的话, 可能要推翻当前的架构, 从头开始从新设计一下目前的框架.
-不过热更新 json 之类的配置文件的话, 还是可以简单的实现的, 更新 `require` 的 `cache` 可以实现, 不会有持有旧引用的问题, 可以参见我 2 年前写着玩的[例子](https://www.npmjs.com/package/auto-reload), 但是这样写并没什么用, 你要热更新配置的话, 为什么不存数据库? 或者用 `zookeeper` 之类的运维工具? 通过更新文件还要再发布一次, 但是存数据库直接写个接口配个界面多爽你说是不是?
+不过热更新 json 之类的配置文件的话, 还是可以简单的实现的, 更新 `require` 的 `cache` 可以实现, 不会有持有旧引用的问题, 可以参见我 2 年前写着玩的[例子](https://www.npmjs.com/package/auto-reload), 但是如果旧的引用一直被持有很容易出现内存泄漏, 而要热更新配置的话, 为什么不存数据库? 或者用 `zookeeper` 之类的服务? 通过更新文件还要再发布一次, 但是存数据库直接写个接口配个界面多爽你说是不是?
所以这个问题其实本身其实是值得商榷的, 可能是典型的 [`X-Y Problem`](http://coolshell.cn/articles/10804.html), 不过聊起来确实是可以暴露水平.
diff --git a/sections/network.md b/sections/network.md
new file mode 100644
index 0000000..8578671
--- /dev/null
+++ b/sections/network.md
@@ -0,0 +1,224 @@
+# Network
+
+* `[Doc]` Net (网络)
+* `[Doc]` UDP/Datagram
+* `[Doc]` HTTP
+* `[Doc]` HTTPS
+* `[Doc]` SSL/TLS
+* `[Doc]` DNS (域名服务器)
+* `[Doc]` ZLIB (压缩)
+* `[Point]` RPC
+
+
+## Net
+
+目前互联化的核心是建立在 TCP/IP 协议的基础上的, 这些协议将数据分割成小的数据包进行传输, 并且解决传输过程中各种各样复杂的问题. 关于协议的具体细节推荐阅读 W.Richard Stevens 的[《TCP/IP 详解 卷1:协议》](https://www.amazon.cn/TCP-IP%E8%AF%A6%E8%A7%A3%E5%8D%B71-%E5%8D%8F%E8%AE%AE-W-Richard-Stevens/dp/B00116OTVS/), 本文不做赘述, 只是列举一些常见的知识点, 新人推荐看[《图解TCP/IP》](https://www.amazon.cn/%E5%9B%BE%E4%B9%A6/dp/B00DMS9990/), 抓包工具推荐看[《Wireshark网络分析就这么简单》](https://www.amazon.cn/%E5%9B%BE%E4%B9%A6/dp/B00PB5QQ84/).
+
+### 粘包
+
+默认情况下, TCP 连接会启用延迟传送算法 (Nagle 算法), 在数据发送之前缓存他们. 如果短时间有多个数据发送, 会缓冲到一起作一次发送 (缓冲大小见 `socket.bufferSize`), 这样可以减少 IO 消耗提高性能.
+
+如果是传输文件的话, 那么根本不用处理粘包的问题, 来一个包拼一个包就好了. 但是如果是多条消息, 或者是别的用途的数据那么久需要处理粘包.
+
+可以参见网上流传比较广的一个例子, 连续调用两次 send 分别发送两段数据 data1 和 data2, 在接收端有以下几种常见的情况:
+
+* A. 先接收到 data1, 然后接收到 data2 .
+* B. 先接收到 data1 的部分数据, 然后接收到 data1 余下的部分以及 data2 的全部.
+* C. 先接收到了 data1 的全部数据和 data2 的部分数据, 然后接收到了 data2 的余下的数据.
+* D. 一次性接收到了 data1 和 data2 的全部数据.
+
+其中的 BCD 就是我们常见的粘包的情况. 而对于处理粘包的问题, 常见的解决方案有:
+
+* 1. 多次发送之前间隔一个等待时间
+* 2. 关闭 Nagle 算法
+* 3. 进行封包/拆包
+
+***方案1***
+
+只需要等上一段时间再进行下一次 send 就好, 适用于交互频率特别低的场景. 缺点也很明显, 对于比较频繁的场景而言传输效率实在太低. 不过几乎用做什么处理.
+
+***方案2***
+
+关闭 Nagle 算法, 在 Node.js 中你可以通过 [`socket.setNoDelay()`](https://nodejs.org/dist/latest-v6.x/docs/api/net.html#net_socket_setnodelay_nodelay) 方法来关闭 Nagle 算法, 让每一次 send 都不缓冲直接发送.
+
+该方法比较适用于每次发送的数据都比较大 (但不是文件那么大), 并且频率不是特别高的场景. 如果是每次发送的数据量比较小, 并且频率特别高的, 关闭 Nagle 纯属自废武功.
+
+另外, 该方法不适用于网络较差的情况, 因为 Nagle 算法是在服务端进行的包合并情况, 但是如果短时间内客户端的网络情况不好, 或者应用层由于某些原因不能及时将 TCP 的数据 recv, 就会造成多个包在客户端缓冲从而粘包的情况. (如果是在稳定的机房内部通信那么这个概率是比较小可以选择忽略的)
+
+***方案3***
+
+封包/拆包是目前业内常见的解决方案了. 即给每个数据包在发送之前, 于其前/后放一些有特征的数据, 然后收到数据的时候根据特征数据分割出来各个数据包.
+
+### 可靠传输
+
+为每一个发送的数据包分配一个序列号(SYN, Synchronise packet), 每一个包在对方收到后要返回一个对应的应答数据包(ACK, Acknowledgedgement),. 发送方如果发现某个包没有被对方 ACK, 则会选择重发. 接收方通过 SYN 序号来保证数据的不会乱序(reordering), 发送方通过 ACK 来保证数据不缺漏, 以此参考决定是否重传. 关于具体的序号计算, 丢包时的重传机制等可以参见阅读陈皓的 [《TCP的那些事儿(上)》](http://coolshell.cn/articles/11564.html) 此处不做赘述.
+
+### window
+
+TCP 头里有一个 Window 字段, 是接收端告诉发送端自己还有多少缓冲区可以接收数据的. 发送端就可以根据接收端的处理能力来发送数据, 从而避免接收端处理不过来. 详细参见陈皓的 [《TCP的那些事儿(下)》](http://coolshell.cn/articles/11609.html)
+
+> window 是否设置的越大越好?
+
+类似木桶理论, 一个木桶能装多少水, 是由最短的那块木板决定的. 一个 TCP 连接的 window 是由该连接中间一连串设备中
+
+### backlog
+
+
+
+关于该 backlog 的定义参见 [man](https://linux.die.net/man/2/listen) 手册:
+
+> The behavior of the backlog argument on TCP sockets changed with Linux 2.2. Now it specifies the queue length for completely established sockets waiting to be accepted, instead of the number of incomplete connection requests.
+
+backlog 用于设置客户端与服务端 `ESTABLISHED` 之后等待 accept 的队列长图 (如上图中的 accept queue). 如果 backlog 过小, 在并发连接大的情况下容易导致 accept queue 装满之后断开连接. 但是如果将这个队列设置的特别大, 那么假定连接数并发量是 65525, 以 php-fpm 的 qps 5000 为例, 处理完约耗时 13s, 而这段时间中连接可能早已被 nginx 或者客户端断开, 那么我们去 accept 这个 socket 时只会拿到一个 broken pipe (该例子出处见 [PHP 源码 Set FPM_BACKLOG_DEFAULT to 511](https://github.com/php/php-src/commit/ebf4ffc9354f316f19c839a114b26a564033708a)). 经过我也不懂的计算 backlog 的长度默认是 511.
+
+另外提一句, 这个 backlog 是通过系统指定时是通过 `somaxconn` 参数来指定 accept queue 的. 而 `tcp_max_syn_backlog` 参数指定的是 SYN queue 的长度.
+
+### 状态机
+
+
+
+关于网络连接的建立以及断开, 存在着一个复杂的状态转换机制, 完整的状态表参见 [《The TCP/IP Guide》](http://www.tcpipguide.com/free/t_TCPOperationalOverviewandtheTCPFiniteStateMachineF-2.htm)
+
+state|简述
+-----|---
+CLOSED|连接关闭, 所有连接的初始状态
+LISTEN|监听状态, 等待客户端发送 SYN
+SYN-SENT|客户端发送了 SYN, 等待服务端回复
+SYN-RECEIVED|双方都收到了 SYN, 等待 ACK
+ESTABLISHED| SYN-RECEIVED 收到 ACK 之后, 状态切换为连接已建立.
+CLOSE-WAIT|被动方收到了关闭请求(FIN)后, 发送 ACK, 如果有数据要发送, 则发送数据, 无数据发送则回复 FIN. 状态切换到 LAST-ACK
+LAST-ACK|等待对方 ACK 当前设备的 CLOSE-WAIT 时发送的 FIN, 等到则切换 CLOSED
+FIN-WAIT-1|主动方发送 FIN, 等待 ACK
+FIN-WAIT-2|主动方收到被动方的 ACK, 等待 FIN
+CLOSING|主动方收到了FIN, 却没收到 FIN-WAIT-1 时发的 ACK, 此时等待那个 ACK
+TIME-WAIT|主动方收到 FIN, 返回收到对方 FIN 的 ACK, 等待对方是否真的收到了 ACK, 如果过一会又来一个 FIN, 表示对方没收到, 这时要再 ACK 一次
+
+> `TIME_WAIT` 是什么情况? 出现过多的 `TIME_WAIT` 可能是什么原因?
+
+`TIME_WAIT` 是连接的某一方 (可能是服务端也可能是客户端) 主动断开连接时, 四次挥手等待被断开的一方是否收到最后一次挥手 (ACK) 的状态. 如果在等待时间中, 再次收到第三次挥手 (FIN) 表示对方没收到最后一次挥手, 这时要再 ACK 一次. 这个等待的作用是避免出现连接混用的情况 (`prevent potential overlap with new connections` see [TCP Connection Termination](http://www.tcpipguide.com/free/t_TCPConnectionTermination.htm) for more).
+
+出现大量的 `TIME_WAIT` 比较常见的情况是, 并发量大, 服务器在短时间断开了大量连接. 对应 HTTP server 的情况可能是没开启 keepAlive. 如果有开 keepAlive, 一般是等待客户端自己主动断开, 那么`TIME_WAIT` 就只存在客户端, 而服务端则是 `CLOSE_WAIT` 的状态, 如果服务端出现大量 `CLOSE_WAIT`, 意味着当前服务端建立的链接大面积的被断开, 可能是目标服务集群重启或者拔网线/断电了之类.
+
+
+## UDP
+
+> TCP/UDP 的区别? UDP 有粘包吗?
+
+协议|连接性|双工性|可靠性|有序性|有界性|拥塞控制|传输速度|量级|头部大小
+---|---|---|---|---|---|---|---|---|---
+TCP|面向连接
(Connection oriented)|全双工(1:1)|可靠
(重传机制)|有序
(通过SYN排序)|无, 有[粘包情况](#粘包)|有|慢|低|20~60字节
+UDP|无连接
(Connection less)|n:m|不可靠
(丢包后数据丢失)|无序|有消息边界, **无粘包**|无|快|高|8字节
+
+UDP socket 支持 n 对 m 的连接状态, 在[官方文档](https://nodejs.org/dist/latest-v6.x/docs/api/dgram.html)中有写到在 `dgram.createSocket(options[, callback])` 中的 option 可以指定 `reuseAddr` 即 `SO_REUSEADDR`标志. 通过 `SO_REUSEADDR` 可以简单的实现 n 对 m 的多播特性 (不过仅在支持多播的系统上才有).
+
+
+### 常见的应用场景
+
+
| 传输层协议 | 应用 | 应用层协议 |
|---|---|---|
| TCP | 电子邮件 | SMTP |
| 终端连接 | TELNET | |
| 终端连接 | SSH | |
| 万维网 | HTTP | |
| 文件传输 | FTP | |
| UDP | 域名解析 | DNS |
| 简单文件传输 | TFTP | |
| 网络时间校对 | NTP | |
| 网络文件系统 | NFS | |
| 路由选择 | RIP | |
| IP电话 | - | |
| 流式多媒体通信 | - |