TCP 三次握手与四次挥手详解
我们知道网络层,可以实现两个主机之间的通信。但是这并不具体,因为,真正进行通信的实体是在主机中的进程,是一个主机中的一个进程与另外一个主机中的一个进程在交换数据。IP 协议虽然能把数据报文送到目的主机,但是并没有交付给主机的具体应用进程。而端到端的通信才应该是应用进程之间的通信。
UDP,在传送数据前不需要先建立连接,远地的主机在收到 UDP 报文后也不需要给出任何确认。虽然 UDP 不提供可靠交付,但是正是因为这样,省去和很多的开销,使得它的速度比较快,比如一些对实时性要求较高的服务,就常常使用的是 UDP。对应的应用层的协议主要有 DNS,TFTP,DHCP,SNMP,NFS 等。
TCP,提供面向连接的服务,在传送数据之前必须先建立连接,数据传送完成后要释放连接。因此 TCP 是一种可靠的的运输服务,但是正因为这样,不可避免的增加了许多的开销,比如确认,流量控制等。对应的应用层的协议主要有 SMTP,TELNET,HTTP,FTP 等。
常用的熟知端口号
应用程序
FTP
TFTP
TELNET
SMTP
DNS
HTTP
SSH
MYSQL
熟知端口
21,20 ...
实现 JavaScript 中的 Promise
Promise 类似于一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
“回调地狱” 的解决方案Promise 是处理异步编码的一个解决方案,在 Promise 出现以前,异步代码的编写都是通过回调函数来处理的,虽然单层回调代码相当直观,但多次回调就显得比较复杂,被称为 回调地狱 。
12345678const fs = require('fs');fs.readFile('1.txt', (err,data) => { fs.readFile('2.txt', (err,data) => { fs.readFile('3.txt', (err,data) => { // 可能还有后续代码 }); });}); ...
JavaScript 的变量提升
JavaScript 的设计不同于传统编程语言,它自成一派,有很多的奇妙特性。
今天就来聊聊变量提升。
变量提升是什么ES5 之前,JavaScript 实际上并不存在块级作用域。JavaScript 引擎对 JavaScript 中 var 类型变量的声明的处理方式是:
如果变量在函数外部声明,那么该变量会被提到全局作用域顶端
如果变量在函数内部声明,那么该变量会被提到函数作用域顶端
函数外部作用域123console.log(shape); // OUTPUT : undefinedvar shape = "square";console.log(shape); // OUTPUT : square
如果按照 C 语言的逻辑,第 1 行的 console.log 引用了未声明的变量,应该发生错误。但实际上却输出 undefined,也就是说实际上这里 shape 已经被声明了,只是没有初始化赋值而已。
这就是变量提升。实际上上面的代码会被 JS 引擎识别为这样
1234var shape;console.log(shape); // OUTPUT : un ...
JavaScript 中的 async
异步编程一直是 JavaScript 中的麻烦事,但在浏览器环境中,对异步的支持是必须的。JavaScript 一直都有异步的解决方案。最早是回调函数,但由于连环回调会带来严重的嵌套问题,所以后来换做扁平化的 Promise 解决方案。
回调函数过程调用一个函数 f -> 函数 f 执行完毕 -> f 调用回调函数 c
使用场合
资源加载:动态加载 js 文件后执行回调,加载 iframe 后执行回调,ajax 操作回调,图片加载完成执行回调,AJAX 等等。
DOM 事件及 Node.js 事件基于回调机制 (Node.js 回调可能会出现多层回调嵌套的问题)。setTimeout 的延迟时间为 0,这个 hack 经常被用到,settimeout 调用的函数其实就是一个 callback 的体现
链式调用:链式调用的时候,在赋值器 (setter) 方法中 (或者本身没有返回值的方法中) 很容易实现链式调用,而取值器 (getter) 相对来说不好实现链式调用,因为你需要取值器返回你需要的数据而不是 this 指针,如果要实现链式方法,可以用回调函数来实现
setT ...
JavaScript 的原型继承
JavaScript 继承一般的面向对象的语言对继承的处理方式都是 class-based,即基于类的继承。一般是说子类继承了父类,继承的主体是类。而类的对象之间的继承体现于类的继承中。
以 Java 为例
12public class A{} // 父类 Apublic class B extends A {} // 子类 B 继承了父类 A
在 JavaScript 的世界中则并非如此。JS 通过原型链的方式来实现继承,是基于对象的继承。下例中 b 对象继承了 a 对象的所有属性和方法。
12345678910function People(sex) { this.sex = sex; this.say = function () { console.log(this.sex); }}let a = new People("man"); // 实例化 a 对象 let b = Object.create(a); //b 对象继承 a 对象,继承了 ...
分布式锁:Redis vs. Zookeeper
在 Java 中 synchronized 关键字和 ReentrantLock 可重入锁在我们的代码中是经常见的,一般我们用其在多线程环境中控制对资源的并发访问,但是随着分布式的快速发展,本地的加锁往往不能满足我们的需要,在我们的分布式环境中上面加锁的方法就会失去作用。基于此,业界提出了分布式锁的概念。
Why 分布式锁?在讨论这个问题之前,我们先来看一个业务场景:
系统 A 是一个电商系统,目前是一台机器部署,系统中有一个用户下订单的接口,但是用户下订单之前一定要去检查一下库存,确保库存足够了才会给用户下单。
由于系统有一定的并发,所以会预先将商品的库存保存在 redis 中,用户下单的时候会更新 redis 的库存。
此时系统架构如下:
但是这样一来会 产生一个问题 :假如某个时刻,redis 里面的某个商品库存为 1,此时两个请求同时到来,其中一个请求执行到上图的第 3 步,更新数据库的库存为 0,但是第 4 步还没有执行。
而另外一个请求执行到了第 2 步,发现库存还是 1,就继续执行第 3 步。
这样的结果,是导致卖出了 2 个商品,然而其实库存只有 1 个。
很明显 ...
数字签名和数字证书
我们已经知道,非对称加密可以保证通信安全,但无法确认通信对象的身份。为了解决这个问题,就需要数字签名和数字证书技术了。
身份认证问题非对称加密中,双方需要交换密钥,然后各自用对方的密钥加密发送的信息,对方则用自己的私钥去解密信息,这样保证了通信过程的安全,但没法确保通信对象的身份问题,第三方可以伪造身份,把自己的密钥给监听对象。
所以问题的核心在于身份认证。现实生活中我们是用身份证等证件进行身份认证,它的有效性由权威性国家机构担保。类似的,在互联网中,也有权威性机构作为担保,它就是 CA,又称为证书授权(Certificate Authority)中心。它通过数字证书来认证互联网用户身份。假如把公钥比作互联网用户的电话号码的话,那么 CA 就是电话局,通过证书来确认电话号码对应互联网用户。
数字证书是什么一句话概括,数字签名就是用户信息 / 公钥的密文,数字证书就是数字签名文件。
举个例子,A 想要认证 A 的公钥,那么 A 需要去 CA(Certificate Authority)申请证书,CA 就会用 CA 的思钥来加密 A 的个人信息和 A 的公钥信息为密文,这个密文被称为数字 ...
了解 tcp 拥塞控制
什么是拥塞我们都知道计算机网络中的资源是有限的。某段时间内网络中对资源的需求超过了网络中的可用部分,而导致网络性能下降的情况就是 拥塞 。
通俗点说就是发送的数据包太多网络中的设备处理不过来,而导致网络性能下降的情况。
TCP 为什么要进行拥塞控制网络中的路由器会有一个数据包处理队列,当路由器接收到的数据包太多而一下子处理不过来时,就会导致数据包处理队列过长。此时,路由器就会无条件的丢弃新接收到的数据封包。
这就会导致上层的 TCP 协议以为数据包在网络中丢失,进而重传这些数据包,而路由器又会丢弃这些重传的数据包,如此以往,就会导致网络性能急剧下降,引起网络瘫痪。
因此,TCP 需要控制数据包发送的数量来避免网络性能的下降。
拥塞控制与流量控制的区别引用书上的答案:
拥塞控制 就是防止过多的数据注入到网络中,这样可以防止网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。
流量控制 往往指点对点通信量的控制,是个端到端的问题。流量控制所 ...
如何比较 Redis 与 Memcached
引Redis 与 Memcached 的比较相当多,首先我们比较一下他们的介绍。
Memcached:一款完全开源、高性能的、分布式的内存系统;
Redis:一个开源的、Key-Value 型、基于内存运行并支持持久化的 NoSQL 数据库;
可以发现,Memcached 更侧重于高性能内存 / 缓存系统,而 Redis 则支持持久化,主打数据库功能,兼可作缓存系统(性能也很高)。
下面有一个更加详细的比较表
对比参数
Redis
Memcached
类型
1. 支持内存 2. 非关系型数据库
1. 支持内存 2. key-value 形式 < br />3. 缓存系统
数据存储类型
String、List、Set、Hash、Sort Set
文本型、二进制型
查询操作
1. 批量操作 < br />2. 支持事务 < br />3. 每个类型 CRUD 不同
CRUD 和少量其他命令
附加功能
1. 发布 / 订阅模式 < br />2. 主从分区 < br />3. 序列化支持 < ...
编程模型之事件循环
UI 编程中,我们需要对鼠标点击,键盘按键等进行响应。这些用户操作在程序中被称为事件 (消息)。我们如何获得事件呢?
用一个线程轮询检测事件相对于整个运行阶段其实是非常时间稀疏的,但这样一个线程一直轮询相当于在整个运行期间大部分是做无用功,造成大量的 CPU 资源浪费。
理想状况时,每个事件都有一个回调函数,这个函数就是操作的响应。由事件去通知主线程调用回调函数。有点类似于协程的原理。我们通过事件驱动模型来实现。
事件驱动模型事件驱动模型有几大要素:
一个事件 (消息) 队列
每当有一个事件发生,就往队列中增加一个事件 (消息)
一个事件循环,它不断从队列中取出事件,根据事件来调用相应回调函数。例如鼠标点击对应的 onclick (),键盘敲击对应的 onKeyDown () 等。
每个事件 (消息) 都保存自己的回调函数指针 ,这样每个消息都有独立的处理函数。
单线程、多线程与事件循环我们不难发现,不同于单线程的固定程序执行流和多线程的并行 / 并发执行流, 事件驱动编程范式 的程序执行流取决于外部事件。由事件循环来控制程序执行流。 它是典型的异步编程 。
单线程程序编写最简 ...