坦白讲 PMG 模型就是
本质是 任务分配到携程任务本地队列,携程放到携程处理器,携程处理器再放到物理线程中执行
1.加了 fallthrough 后,会直接运行【紧跟的后一个】case 或 default 语句,不论条件是否满足都会执行
2.加了 fallthrough 语句后,【紧跟的后一个】case 条件不能定义常量和变量
3.执行完 fallthrough 后直接跳到下一个条件语句,本条件执行语句后面的语句不执行
当我们想要在 Go 语言中初始化一个结构时,其实会使用到两个完全不同的关键字,也就是 make 和 new,同时出现两个用于初始化的关键字对于初学者来说可能会感到非常困惑,不过它们两者却有着完全不同的作用。
在 Go 语言中,make 关键字的主要作用是初始化内置的数据结构,也就是我们在前面提到的数组、切片和 Channel,而当我们想要获取指向某个类型的指针时可以使用 new 关键字,只是知道如何使用 new 的人真的比较少,下面我们就来介绍一下 make 和 new 它们的区别以及实现原理。
虽然 make 和 new 都是能够用于初始化数据结构,但是它们两者能够初始化的结构类型却有着较大的不同,make 在 Go 语言中只能用于初始化语言中的基本类型:
1 | slice := make([]int, 0, 100) |
这些基本类型都是语言为我们提供的,我们在前面已经介绍过了它们初始化的过程以及原理,但是在这里还是需要提醒大家注意的是,这三者返回了不同类型的数据结构:
slice 是一个包含 data、cap 和 len 的结构体;
hash 是一个指向 hmap 结构体的指针;
ch 是一个指向 hchan 结构体的指针。
而另一个用于初始化数据结构的关键字 new 的作用其实就非常简单了,它只是接收一个类型作为参数然后返回一个指向这个类型的指针:
1 | i := new(int) |
上述代码片段中的两种不同初始化方法其实是等价的,它们都会创建一个指向 int 零值的指针。
到了这里我们对 Go 语言中这两种不同关键字的使用也有了一定的了解:make 用于创建切片、哈希表和管道等内置数据结构,new 用于分配并创建一个指向对应类型的指针。
实现原理
接下来我们将分别介绍 make 和 new 在初始化不同数据结构时的具体过程,我们会从编译期间和运行时两个不同的阶段理解这两个关键字的原理。
make
我们已经了解了 make 在创建数组和切片、哈希表和 Channel 的具体过程,所以在这里我们也只是会简单提及 make 相关的数据结构初始化原理。
在编译期间的类型检查阶段,Go 语言其实就将代表 make 关键字的 OMAKE 节点根据参数类型的不同转换成了 OMAKESLICE、OMAKEMAP 和 OMAKECHAN 三种不同类型的节点,这些节点最终也会调用不同的运行时函数来初始化数据结构。
new
内置函数 new 会在编译期间的 SSA 代码生成阶段经过 callnew 函数的处理,如果请求创建的类型大小是 0,那么就会返回一个表示空指针的 zerobase 变量,在遇到其他情况时会将关键字转换成 newobject:
1 | func callnew(t *types.Type) *Node { |
需要提到的是,哪怕当前变量是使用 var 进行初始化,在这一阶段也可能会被转换成 newobject 的函数调用并在堆上申请内存:
1 | func walkstmt(n *Node) *Node { |
当然这也不是绝对的,如果当前声明的变量或者参数不需要在当前作用域外生存,那么其实就不会被初始化在堆上,而是会初始化在当前函数的栈中并随着函数调用的结束而被销毁。
newobject 函数的工作就是获取传入类型的大小并调用 mallocgc 在堆上申请一片大小合适的内存空间并返回指向这片内存空间的指针:
1 | func newobject(typ *_type) unsafe.Pointer { |
总结
最后,简单总结一下 Go 语言中 make 和 new 关键字的实现原理,make 关键字的主要作用是创建切片、哈希表和 Channel 等内置的数据结构,而 new 的主要作用是为类型申请一片内存空间,并返回指向这片内存的指针。
OSI 体系 | TCP/IP | 五层体系 | |
---|---|---|---|
应用层 | 应用层(各种应用层协议,TELNET,FTP,SMTP) | 应用层 | HTTP |
HTTP2
FTP |
| 表示层 | | | |
| 会话层 | | | |
| 运输层 | 运输层(TCP、UDP) | 运输层 | TCP、UDP |
| 网络层 | 网际层 IP | 网络层 | IP |
| 数据链路层 | 网络接口 | 数据链路层 | 接口 |
| 物理层 | | 物理层 | |
传输数据的基本单位:比特流 0 和 1
数据传输系统:源系统(源点、发送器) –> 传输系统 –> 目的系统(接收器、终点)
单向通道(单工通道):只有一个方向通信,没有反方向交互,如广播
双向交替通信(半双工通信):通信双方都可发消息,但不能同时发送或接收
双向同时通信(全双工通信):通信双方可以同时发送和接收信息
频分复用(FDM,Frequency Division Multiplexing):不同用户在不同频带,所用用户在同样时间占用不同带宽资源
时分复用(TDM,Time Division Multiplexing):不同用户在同一时间段的不同时间片,所有用户在不同时间占用同样的频带宽度
波分复用(WDM,Wavelength Division Multiplexing):光的频分复用
码分复用(CDM,Code Division Multiplexing):不同用户使用不同的码,可以在同样时间使用同样频带通信
主要信道:
点对点信道
广播信道
点对点信道:
数据单元:帧
三个基本问题:
封装成帧:把网络层的 IP 数据报封装成帧,SOH - 数据部分 - EOT
透明传输:不管数据部分什么字符,都能传输出去;可以通过字节填充方法解决(冲突字符前加转义字符)
差错检测:降低误码率(BER,Bit Error Rate),广泛使用循环冗余检测(CRC,Cyclic Redundancy Check)
点对点协议(Point-to-Point Protocol):用户计算机和 ISP 通信时所使用的协议
广播信道:
硬件地址(物理地址、MAC 地址)
单播(unicast)帧(一对一):收到的帧的 MAC 地址与本站的硬件地址相同
广播(broadcast)帧(一对全体):发送给本局域网上所有站点的帧
多播(multicast)帧(一对多):发送给本局域网上一部分站点的帧
IP(Internet Protocol,网际协议)是为计算机网络相互连接进行通信而设计的协议。
ARP(Address Resolution Protocol,地址解析协议)
ICMP(Internet Control Message Protocol,网际控制报文协议)
IGMP(Internet Group Management Protocol,网际组管理协议)
IP 地址**({<网络号>,<主机号>})**分类:
ICMP 报文格式:
应用:
PING(Packet InterNet Groper,分组网间探测)测试两个主机之间的连通性
TTL(Time To Live,生存时间)该字段指定 IP 包被路由器丢弃之前允许通过的最大网段数量
RIP(Routing Information Protocol,路由信息协议)
OSPF(Open Sortest Path First,开放最短路径优先)
BGP(Border Gateway Protocol,边界网关协议)
IGMP(Internet Group Management Protocol,网际组管理协议)
多播路由选择协议
VPN(Virtual Private Network,虚拟专用网)
NAT(Network Address Translation,网络地址转换)
网络 ID(Network ID, Network number):就是目标地址的网络 ID。
子网掩码(subnet mask):用来判断 IP 所属哪个子网络
下一跳地址/接口(Next hop / interface):就是数据在发送到目标地址的旅途中下一站的地址。其中 interface 指向 next hop(即为下一个 route)。一个自治系统(AS, Autonomous system)中的 route 应该包含区域内所有的子网络,而默认网关(Network id: 0.0.0.0, Netmask: 0.0.0.0)指向自治系统的出口。
根据应用和执行的不同,路由表可能含有如下附加信息:
花费(Cost):就是数据发送过程中通过路径所需要的花费
路由的服务质量
路由中需要过滤的出/入连接列表
支持的协议:
TCP(Transmission Control Protocol,传输控制协议)
UDP(User Datagram Protocol,用户数据报协议)
端口号:
TCP 是一种面向连接的、可靠的、基于字节流的传输层通信协议,其传输的单位是报文段。
特征:
面向连接
只能点对点(一对一)通信
可靠交互
全双工通信
面向字节流
TCP 如何保证可靠传输?
确认和超时重传
数据合理分片和排序
流量控制
拥塞控制
数据校验
TCP 报文结构
TCP 首部
TCP:状态控制码(Code,Control Flag),占 6 比特,含义如下:
URG:紧急比特(urgent),当 URG = 1 时,表明紧急指针字段有效,代表该封包为紧急封包。它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据), 且上图中的 Urgent Pointer 字段也会被启用。
ACK:确认比特(Acknowledge)。只有当 ACK = 1 时确认号字段才有效,代表这个封包为确认封包。当 ACK = 0 时,确认号无效。
PSH:(Push function)若为 1 时,代表要求对方立即传送缓冲区内的其他对应封包,而无需等缓冲满了才送。
RST:复位比特(Reset),当 RST = 1 时,表明 TCP 连接中出现严重差错(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立运输连接。
SYN:同步比特(Synchronous),SYN 置为 1,就表示这是一个连接请求或连接接受报文,通常带有 SYN 标志的封包表示『主动』要连接到对方的意思。
FIN:终止比特(Final),用来释放一个连接。当 FIN = 1 时,表明此报文段的发送端的数据已发送完毕,并要求释放传输连接。 6. 2 UDP(User Datagram Protocol,用户数据报协议)
UDP 是 OSI(Open System Interconnection 开放式系统互联) 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,其传输的单位是用户数据报。
特征:
无连接
尽最大努力交付
面向报文
没有拥塞控制
支持一对一、一对多、多对多的交互通信
首部开销小
UDP 报文结构
UDP 首部
TCP 面向连接、UDP 是无连接的;
TCP 提供可靠的服务、也就是说,通过 TCP 连接传输的数据是无差错、不丢失、不重复且按序到达;UDP 尽最大努力交付,即不保证可靠交付
TCP 的逻辑通信信息是全双工的可靠信息;UDP 则是不可靠信息
每一条 TCP 连接只能是点对点的;UDP 支持一对多、多对一、多对多的交互通信
TCP 面向字节流(可能会出现黏包问题),实际上是 TCP 白数据看成一连串无结构的字节流;UDP 是面向报文的(不会出现黏包问题)
UDP 没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如 IP 电话,实时视频会议等)
TCP 首部开销 20 字节;UDP 的首部开销小,只有 8 字节
出现黏包问题的原因:TCP 是一个基于字节流的传输服务(UDP 基于报文的),“流” 意味着 TCP 所传输的数据是没有边界的,所以可能会出现两个数据包黏在一起的情况。
解决方法:
发送定长包。如果每个消息的大小都是一样的,那么在接收对等方只要累计接收数据,直到数据等于一个定长的数值就将它作为一个消息。
包头加上包体长度。包头是定长的 4 个字节,说明了包体的长度。接收对等方先接收包头长度,依据包头长度来接收包体。
在数据包之间设置边界,如添加特殊符号 \r\n 标记。FTP 协议正是这么做的。但问题在于如果数据正文中也含有 \r\n,则会误判为消息的边界。
使用更加复杂的应用层协议
概念:流量控制(flow control)就是让发送方的发送速率不要太快,要让接收方来得及接收。
方法:利用可变窗口进行流量控制
概念:拥塞控制就是防止过多的数据注入到网络中,可以使网络中的路由器或链路不致过载。
解决方法:
慢启动( slow-start )
拥塞避免( congestion avoidance )
快重传( fast retransmit )
快恢复( fast recovery )
一.TCP 建立连接:三次握手
TCP 建立连接全过程解释: 1.客户端发生 SYN 给服务器,表示客户端向服务器请求建立连接; 2.服务端收到客户端的 SYN,并回复 SYN+ACK 给客户端(同意建立连接); 3.客户端收到来自服务器的 SYN+ACK 后,回复 ACK 给服务端(表示客户端收到了服务端发的同意报文); 4.服务端收到客户端的 ACK,连接已建立,可以进行数据传输。
建立连接的详细过程:
a.B 的 TCP 服务器进程首先创建传输控制块 TCB,准备接受客户进程的连接请求。然后服务器进程就处于 LISTEN(收听)状态,等待客户的连接请求。如有,就做出响应。
b.A 的 TCP 客户进程也是首先创建传输控制模块 TCB,然后向 B 发出连接请求报文段,这时首部中的同步位 SYN=1,同时选择一个初始序号 seq=x。TCP 规定,SYN 报文段(即 SYN=1 的报文段)不能携带数据,但是要消耗一个序号。这时,TCP 客户进程进入 SYN-SENT(同步已发送)状态。
c.B 收到连接请求报文段后,如同意建立连接,则向 A 发送确认。在确认报文段中应把 SYN 和 ACK 位都置 1,确认号是 ack=x+1,同时也为自己选择一个初始序号 seq=y。注意:这个报文段也不能携带数据,但同样要消耗一个序号。这时 TCP 服务器进程进入 SYN-RECV(同步收到)状态。
d.TCP 客户进程收到 B 的确认后,还要向 B 给出确认。确认报文段的 ACK 置 1,确认号 ack=y+1,而自己的序号 seq=x+1。TCP 标准规定,ACK 报文段可以携带数据。但如果不携带数据则不消耗序号。在这种情况下,下一个数据报文段的序号仍然是 seq=x+1。这时,TCP 的连接已经建立,A 进入 ESTABLISHED(已建立连接)状态。当 B 接收到 A 的确认后,B 也进入 ESTABLISHED(已建立连接)状态。
Q1:TCP 为什么要进行三次握手?
因为信道不可靠,而 TCP 想在不可靠信道上建立可靠地传输,那么三次通信是理论上的最小值。(而 UDP 则不需建立可靠传输,因此 UDP 不需要三次握手)
因为双方都需要确认对方收到了自己发送的序列号,确认过程最少要进行三次通信
为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误
二.TCP 断开连接:四次挥手
TCP 断开连接全过程解释: 1.客户端发送 FIN 给服务器,说明客户端不必发送数据给服务器了(请求释放从客户端到服务器的连接); 2.服务器接收到客户端发的 FIN,并回复 ACK 给客户端(同意释放从客户端到服务器的连接); 3.客户端收到服务端回复的 ACK,此时从客户端到服务器的连接已释放(但服务端到客户端的连接还未释放,并且客户端还可以接收数据); 4.服务端继续发送之前没发完的数据给客户端; 5.服务端发送 FIN+ACK 给客户端,说明服务端发送完了数据(请求释放从服务端到客户端的连接,就算没收到客户端的回复,过段时间也会自动释放); 6.服务端发送 FIN+ACK 给客户端,说明服务端发送完了数据(请求释放从服务端到客户端的连接,就算没收到客户端的回复,过段时间也会自动释放); 7.服务端收到客户端的 ACK 后,断开从服务端到客户端的连接
释放连接的详细过程:
a.A 和 B 都处于 ESTABLISHED 状态,A 的应用进程首先向其 TCP 发出连接释放报文段,并停止再发送数据,主动关闭 TCP 连接。A 把连接释放报文段首部的 FIN 置 1,其序列号 seq=u,它等于前面已经传送过的数据的最后一个字节的序号加 1。这时,A 进入 FIN-WAIT-1(终止等待 1)状态,等待 B 的确认。注意:TCP 规定:FIN 报文段即使不携带数据,它也会消耗一个序号。
b.B 收到连接释放报文段后即发出确认,确认号是 ack=u+1,而这个报文段自己的序号是 v,等待 B 前面已经传送过的数据的最后一个字节的序号加 1。然后 B 就进入 CLOSE-WAIT(关闭等待)状态。TCP 服务器进程这时应通知高层应用进程,因而从 A 到 B 这个方向的连接就释放了,这时的 TCP 连接处于半关闭状态,即 A 已经没有数据要发送了,但 B 若发送数据,A 仍要接收。也就是说,从 B 到 A 这个方向的连接并没有关闭,这个连接可能会持续一段时间。
c.A 收到来自 B 的确认后,就进入 FIN-WAIT-2(终止等待 2)状态,等待 B 发出的连接释放报文段。如果 B 已经没有要向 A 发送的数据,其应用进程就会通知 TCP 释放连接。这时 B 发出的连接释放报文段必须使 FIN=1。现假定 B 的序号为 w(在半关闭状态 B 可能又发送了一些数据)。B 还必须重复上次已经发送过的确认号 ack=u+1。这时,B 就进入 LAST-ACK(最后确认状态),等待 A 的确认。
d.A 在收到 B 的释放连接报文段后,必须对此发出一个确认。在确认报文段中把 ACK 置 1,确认号 ack=w+1,而自己的序号是 seq=u+1(根据 TCP 标准,前面发送过的 FIN 报文段要消耗一个序号)。然后经过时间等待计时器(TIME-WAIT)设置的时间 2MSL 后,A 才进入到 CLOSED 状态。时间 MSL 叫做最长报文段寿命
Q2:TCP 为什么要进行四次挥手?
因为 TCP 是全双工模式,客户端请求关闭连接后,客户端向服务端的连接关闭(一二次挥手),服务端继续传输之前没传完的数据给客户端(数据传输),服务端向客户端的连接关闭(三四次挥手)。所以 TCP 释放连接时服务器的 ACK 和 FIN 是分开发送的(中间隔着数据传输),而 TCP 建立连接时服务器的 ACK 和 SYN 是一起发送的(第二次握手),所以 TCP 建立连接需要三次,而释放连接则需要四次。
Q3:为什么 TCP 建立连接时可以 ACK 和 SYN 一起发送,而断开连接时则 ACK 和 FIN 分开发送呢?(ACK 和 FIN 分开是指第二次和第三次挥手)
因为客户端请求释放时,服务器可能还有数据需要传输给客户端,因此服务端要先响应客户端 FIN 请求(服务端发送 ACK),然后数据传输,传输完成后,服务端再提出 FIN 请求(服务端发送 FIN);而连接时则没有中间的数据传输,因此连接时可以 ACK 和 SYN 一起发送。
Q4:为什么客户端断开连接时,最后需要 TIME-WAIT 等待 2MSL 呢? 1.为了保证客户端发送的最后一个 ACK 报文能够到达服务端。若未成功到达,则服务端超时重传 FIN+ACK 报文段,客户端再重传 ACK,并重新计时。 2.防止已失效的连接请求报文段出现在本连接中。TIME-WAIT 持续 2MSL 可使本连接持续的时间内所产生的所有报文段都从网络中消失,这样可使下次连接中不会出现旧的连接报文段。
6.8 TCP 有限状态机
7.应用层
7.1 DNS(Domain Name System,域名系统)
DNS 是互联网的一项服务。它作为将域名和 IP 地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS 使用 TCP 和 UDP 端口 53。当前,对于每一级域名长度的限制是 63 个字符,域名总长度则不能超过 253 个字符。
域名 ::= {<三级域名>.<二级域名>.<顶级域名>},如:blog.huihut.com
7.2 FTP(File Transfer Protocol,文件传输协议)
FTP 是用于在网络上进行文件传输的一套标准协议,使用客户/服务器模式,使用 TCP 数据报,提供交互式访问,双向传输。
TFTP(Trivial File Transfer Protocol,简单文件传输协议)一个小且易实现的文件传输协议,也使用客户-服务器方式,使用 UDP 数据报,只支持文件传输而不支持交互,没有列目录,不能对用户进行身份鉴定
7.3 TELNET
TELNET 协议是 TCP/IP 协议族中的一员,是 Internet 远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。
7.4 HTTP(HyperText Transfer Protocol,超文本传输协议)
HTTP 是用于从 WWW(World Wide Web,万维网)服务器传输超文本到本地浏览器的传送协议。
7.5 SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)
SMTP 是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式。SMTP 协议属于 TCP/IP 协议簇,它帮助每台计算机在发送或中转信件时找到下一个目的地。它是在 Internet 传输 Email 的标准,是一个相对简单的基于文本的协议。在其之上指定了一条消息的一个或多个接收者(在大多数情况下被确认是存在的),然后消息文本会被传输。可以很简单地通过 Telnet 程序来测试一个 SMTP 服务器,SMTP 使用 TCP 端口 25。
7.6 DHCP(Dynamic Host Configuration Protocol,动态主机设置协议)
DHCP 是一个局域网的网络协议,使用 UDP 协议工作,主要有两个用途:
用于内部网络或网络服务供应商自动分配 IP 地址给用户
用于内部网络管理员作为对所有电脑作中央管理的手段
7.7 SNMP(Simple Network Management Protocol,简单网络管理协议)
SNMP 构成了互联网工程工作小组(IETF,Internet Engineering Task Force)定义的 Internet 协议族的一部分。该协议能够支持网络管理系统,用以监测连接到网络上的设备是否有任何引起管理上关注的情况。 8.相关概念
8.1 Socket(套接字)
Socket 建立网络通信连接至少要一对端口号(Socket)。Socket 本质是编程接口(API),对 TCP/IP 的封装,TCP/IP 也要提供可供程序员做网络开发所用的接口,这就是 Socket 编程接口。
8.2 WWW(World Wide Web,环球信息网,万维网)
WWW 是一个由许多互相链接的超文本组成的系统,通过互联网访问
8.3 URL(Uniform Resource Locator,统一资源定位符)
概念:URL 是因特网上标准的资源的地址(Address)
标准格式:协议类型:[//服务器地址[:端口号]][/资源层级unix文件路径]文件名[?查询][#片段id]
完整格式:协议类型:[//[访问资源需要的凭证信息@]服务器地址[:端口号]][/资源层级unix文件路径]文件名[?查询][#片段id] 注意:其中[访问凭证信息@;:端口号;?查询;#片段 ID]都属于选填项,可以省略,如:https://github.com/cdlwhm1217096231
9.HTTP 详解
概念:HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP 是万维网的数据通信的基础。
请求方法:
状态码:
1xx:表示通知信息,如请求收到了或正在进行处理
100 Continue:继续,客户端应继续其请求
101 Switching Protocols 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到 HTTP 的新版本协议
2xx:表示成功,如接收或知道了
200 OK: 请求成功
3xx:表示重定向,如要完成请求还必须采取进一步的行动
301 Moved Permanently: 永久移动。请求的资源已被永久的移动到新 URL,返回信息会包括新的 URL,浏览器会自动定向到新 URL。今后任何新的请求都应使用新的 URL 代替
4xx:表示客户的差错,如请求中有错误的语法或不能完成
400 Bad Request: 客户端请求的语法错误,服务器无法理解
401 Unauthorized: 请求要求用户的身份认证
403 Forbidden: 服务器理解请求客户端的请求,但是拒绝执行此请求(权限不够)
404 Not Found: 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置 “您所请求的资源无法找到” 的个性页面
408 Request Timeout: 服务器等待客户端发送的请求时间过长,超时
5xx:表示服务器的差错,如服务器失效无法完成请求
500 Internal Server Error: 服务器内部错误,无法完成请求
503 Service Unavailable: 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的 Retry-After 头信息中
504 Gateway Timeout: 充当网关或代理的服务器,未及时从远端服务器获取请求
10.DNS(域名解析协议)
域名的来由:我们可以通过 IP 地址去访问网站,但是对大多数用户来说,访问每个网站都需要记住一串数字是不现实的,所以用户可以通过域名来访问网站。域名,其实是具有一定含义的字符组合。域名系统是因特网上作为域名和 IP 地址相互映射的一个分布式数据库,能让用户更方便使用互联网。
DNS 劫持:指用户访问一个被标记的地址时,DNS 服务器故意将此地址指向一个错误的 IP 地址的行为。范例:收到各种推送广告等网站
DNS 污染:指的是用户访问一个地址,国内的服务器(非 DNS)监控到用户访问的已经被标记地址时,服务器伪装成 DNS 服务器向用户发回错误的地址的行为。比如不能访问 Google、YouTube 等。
域名表达式为,在地址表达式中,最右边的是最高层次的域名,最左边的是主机名,域与域之间用圆点隔开;
域名解析:通过主机最终得到该主机名对应的 IP 地址;
11.Http 协议格式
HTTP 请求报文与响应报文格式
请求报文包含三部分:
请求行:包含请求方法、URI、HTTP 版本信息
请求首部字段
请求内容实体
响应报文包含三部分:
状态行:包含 HTTP 版本、状态码、状态码的原因短语
响应首部字段
响应内容实体
HTTP(超文本传输协议)是一个基于请求与响应模式的、无状态的、应用层的协议,常基于 TCP 的连接方式,HTTP1.1 版本中给出一种持续连接的机制,绝大多数的 Web 开发,都是构建在 HTTP 协议之上的 Web 应用。
常用的 HTTP 方法有哪些?
GET:用于请求访问已经被 URI(统一资源标识符)识别的资源,可以通过 URL 传参给服务器。
POST:用于传输信息给服务器,主要功能与 GET 方法类似,但一般推荐使用 POST 方式。
PUT:传输文件,报文主体中包含文件内容,保存到对应 URI 位置。
HEAD:获得报文首部,与 GET 方法类似,只是不返回报文主体,一般用于验证 URI 是否有效。
DELETE:删除文件,与 PUT 方法相反,删除对应 URI 位置的文件。
OPTIONS:查询相应 URI 支持的 HTTP 方法。
GET 方法与 POST 方法的区别
get 重点在从服务器上获取资源,post 重点在向服务器发送数据;
get 传输数据是通过 URL 请求,以 field(字段)= value 的形式,置于 URL 后,并用”?“连接,多个请求数据间用”&”连接,如http://127.0.0.1/Test/login.action?name=admin&password=admin,这个过程用户是可见的;post 传输数据通过 Http 的 post 机制,将字段与对应值封存在请求实体中发送给服务器,这个过程对用户是不可见的;
get 传输的数据量小,因为受 URL 长度限制,但效率较高;Post 可以传输大量数据,所以上传文件时只能用 Post 方式;
get 是不安全的,因为 URL 是可见的,可能会泄露私密信息,如密码等;post 较 get 安全性较高;
get 方式只能支持 ASCII 字符,向服务器传的中文字符可能会乱码。post 支持标准字符集,可以正确传递中文字符。
HTTP1.1 版本新特性
默认持久连接节省通信量,只要客户端服务端任意一端没有明确提出断开 TCP 连接,就一直保持连接,可以发送多次 HTTP 请求
管线化,客户端可以同时发出多个 HTTP 请求,而不用一个个等待响应
断点续传原理
常见 HTTP 首部字段
通用首部字段(请求报文与响应报文都会使用的首部字段)
Date:创建报文时间
Connection:连接的管理
Cache-Control:缓存的控制
Transfer-Encoding:报文主体的传输编码方式
请求首部字段(请求报文会使用的首部字段)
Host:请求资源所在服务器
Accept:可处理的媒体类型
Accept-Charset:可接收的字符集
Accept-Encoding:可接受的内容编码
Accept-Language:可接受的自然语言
响应首部字段(响应报文会使用的首部字段)
Accept-Ranges:可接受的字节范围
Location:令客户端重新定向到的
URI Server:HTTP 服务器的安装信息
实体首部字段(请求报文与响应报文的的实体部分使用的首部字段)
Allow:资源可支持的 HTTP 方法
Content-Type:实体主类的类型
Content-Encoding:实体主体适用的编码方式
Content-Language:实体主体的自然语言
Content-Length:实体主体的的字节数
Content-Range:实体主体的位置范围,一般用于发出部分请求时使用
HTTP 的缺点与 HTTPS
通信使用明文不加密,内容可能被窃听
不验证通信方身份,可能遭到伪装
无法验证报文完整性,可能被篡改
HTTPS 就是 HTTP 加上加密处理(一般是 SSL 安全通信线路)+认证+完整性保护 12.当你输入一个网址/点击一个链接,发生了什么?(以www.baidu.com为例) 1.点击网址后,应用层的 DNS 协议会将网址解析为 IP 地址;
DNS 查找过程:浏览器会检查缓存中有没有这个域名对应的解析过的 IP 地址,如果缓存中有,这个解析过程就将结束。如果用户的浏览器缓存中没有,浏览器会查找操作系统缓存(hosts 文件)中是否有这个域名对应的 DNS 解析结果。若还没有,此时会发送一个数据包给 DNS 服务器,DNS 服务器找到后将解析所得 IP 地址返回给用户。 2.在应用层,浏览器会给 web 服务器发送一个 HTTP 请求;
请求头为:GET http://www.baidu.com/HTTP/1.1 3.在传输层,(上层的传输数据流分段)HTTP 数据包会嵌入在 TCP 报文段中;
TCP 报文段需要设置端口,接收方(百度)的 HTTP 端口默认是 80,本机的端口是一个 1024-65535 之间的随机整数,这里假设为 1025,这样 TCP 报文段由 TCP 首部(包含发送方和接收方的端口信息)+ HTTP 数据包组。 4.在网络层中,TCP 报文段再嵌入 IP 数据包中;
IP 数据包需要知道双方的 IP 地址,本机 IP 地址假定为 192.168.1.5,接受方 IP 地址为 220.181.111.147(百度),这样 IP 数据包由 IP 头部(IP 地址信息)+TCP 报文段组成。 5.在网络接口层,IP 数据包嵌入到数据帧(以太网数据包)中在网络上传送;
数据帧中包含源 MAC 地址和目的 MAC 地址(通过 ARP 地址解析协议得到的)。这样数据帧由头部(MAC 地址)+IP 数据包组成。 6.数据包经过多个网关的转发到达百度服务器,请求对应端口的服务;
服务接收到发送过来的以太网数据包开始解析请求信息,从以太网数据包中提取 IP 数据包—>TCP 报文段—>HTTP 数据包,并组装为有效数据交与对应线程池中分配的线程进行处理,在这个过程中,生成相应 request、response。 7.请求处理完成之后,服务器发回一个 HTTP 响应;
请求处理程序会阅读请求及它的参数和 cookies。它会读取也可能更新一些数据,并将数据存储在服务器上。处理完毕后,数据通过 response 对象给客户输出信息,输出信息也需要拼接 HTTP 协议头部分,关闭后断开连接。断开后,服务器端自动注销 request、response 对象,并将释放对应线程的使用标识(一般一个请求单独由一个线程处理,部分特殊情况有一个线程处理多个请求的情况)。响应头为:HTTP/1.1200 OK 8.浏览器以同样的过程读取到 HTTP 响应的内容(HTTP 响应数据包),然后浏览器对接收到的 HTML 页面进行解析,把网页显示出来呈现给用户。
客户端接收到返回数据,去掉对应头信息,形成也可以被浏览器认识的页面 HTML 字符串信息,交与浏览器翻译为对应页面规则信息展示为页面内容。 13.计算机的 OSI 和 TCP/IP 网络模型 1.计算机的网络模型分为两种 OSI 模型和 TCP/IP 模型,它们的对应关系如下:
2.针对 OSI 模型,每一层都有各自的功能。
应用层
应用层是 OSI 模型中最靠近用户的一层,负责为用户的应用程序提供网络服务 。包括为相互通信的应用程序或进程之间建立连接,进行同步建立关于错误纠正和控制同时还包含大量的应用协议,例如远程登录(talent)、简单的邮件传输协议(SMTP)、简单的网络管理协议(SNMP),超文本传输协议(HTTP)。所有能超声网络流量的程序都在应用层。
表示层
表示层负责在不同的数据格式之间进行转换操作,以实现不同的计算机系统间的信息交换。还负责数据的加密,在传输的过程中进行保护,在发送端加密,在接收端解密,使用加密秘钥来对数据进行加密和解密。
会话层
会话层的主要功能是在两个节点间建立连接、维护、释放面向用户的连接,并对会话进行管理和控制,保证会话数据可靠传输,在会话的过程中决定到底使用全双工还是使用半双工模式传输。
传输层
传输层是 OSI 模型中唯一负责端到端节点数据传输和控制的层,传输层是在 OSI 模型中起承上启下的作用,它下面的三层主要主要面向网络通信,以确保信息准确有效的传输,上面的三层树妖面向主机用户,为用户提供各种服务。 传输层为了向会话层提供可靠的端到端传输服务,也使用差错控制和流量控制等机制。4 层的协议有传输控制协议(TCP),用户数据报协议(UDP),顺序包交换协议(SPX)。
网络层
负责选择最佳的路径,规划 IP(Internet Protocol)地址。
数据链路层
数据帧的开始和结束,同时提供透明传输,差错校验。
物理层
是 OSI 模型的最底层,它面向原始的比特流的传输,同时规范了接口标准。 3.针对 TCP/IP 模型,每一层都有各自的功能。
物理层:对应 OSI 模型的低两层,物理层和数据链路层;常用协议:Ethernet、FDDI、令牌环
网络层:对应 OSI 模型的网络层;常用协议:IP、ARP、RAR、ICMP
传输层:对应 OSI 模型的传输层;常用协议:TCP、UDP
应用层:对应 OSI 模型的高三层,会话层、表示层、应用层;常用协议:DNS、HTTP、SMTP、POP、TELNET、FTP
14.TCP/FTP 简析
1.TCP/FTP 简析
TCP/IP 是个协议组,可分为三个层次:网络层、传输层和应用层:
网络层:IP 协议、ICMP 协议、ARP 协议、RARP 协议和 BOOTP 协议
传输层:TCP 协议与 UDP 协议
应用层:FTP、HTTP、TELNET、SMTP、DNS 等协议
2.TCP 连接的三次握手
第一次握手:客户端发送 syn 包(syn=j)到服务器,并进入 SYN_SEND 状态,等待服务器确认;
第二次握手:服务端接收到客户端的请求后,给出一个确认 ACK(ack=j+1),同时自己也发送出一个 SYN 包(syn=k),此时服务器进入 SYN_RECV 状态。
第三次握手:客户端接收到服务端的 SYN+ACK 包后,向服务器发出一个确认 ACK(ack=k+1),此包发送完毕,客户端和服务端进入 ESTABLIST 状态,完成三次握手。
3.FTP
文件传输协议(File Transfer Protocol, FTP)是 TCP/IP 网络上两台计算机传送文件的协议,FTP 是在 TCP/IP 网络和 INTERNET 上最早使用的协议之一,它属于网络协议组的应用层。FTP 客户机可以给服务器发出命令来下载文件,上载文件,创建或改变服务器上的目录。
15.IP 地址与子网掩码
1.IP 地址:Internet 上每台主机都必须有一个唯一的地址以区别于其他主机,这个地址就是 Internet 地址,也称作 IP 地址;IPv4(第四版本的 IP 协议)是构成现今互联网技术的基石协议;
2.IP 地址的构成:IP 地址由 32 位二进制构成,共约 40 亿个,由网络地址和主机地址构成;一个 IP 地址分为四段:a.b.c.d ,段与段之间用原点分开。IP 地址表示方法:二进制形式和点分十进制形式;
3.IP 地址的分类:5 类(A 类地址,B 类地址,C 类地址,D 类地址,E 类地址)
IP 地址的分类是根据第一段(a 字段,前 8 位)的取值范围来划分的;
A 类地址:以 0 开头,前 8 位为网络地址,后 24 位为主机地址。A 类地址第一段 a 字段的取值范围 1126。每一个 A 类地址中可以容纳的主机的数目约为 1600 万。地址范围:1.0.0.0126.255.255.255
B 类地址:以 10 开头,前 16 位为网络地址,后 16 位为主机地址。B 类地址第一段 a 字段取值范围为 128191。每一个 B 类地址中可以容纳主机数目约为 65000。地址范围:128.0.0.0191.255.255.255
C 类地址:以 110 开头,前 24 位为网络地址,后 8 位为主机地址。C 类地址第一段 a 字段的取值范围是 192223。每一个 C 类地址可容纳主机的数目约为 254。地址范围:192.0.0.0223.255.255.255
4.IPv6(第六版 IP 协议):一个 IP 地址由 128 位二进制组成,采用冒分 16 进制。 5.特殊的 IP 地址:
a.专用 IP 地址:三类网络号,这些地址不会被 Internet 分配因此也不能被路由。
A 类:1.0.0.010.255.255.255 (长度相当于 1 个 A 类 IP 地址)172.31.255.255 (长度相当于 16 个连续的 B 类 IP 地址)
B 类:172.16.0.0
C 类:192.168.0.0~192.168.255.255 (长度相当于 256 个连续的 C 类 IP 地址)
b. 特殊 IP 地址:
0.0.0.0 是全零网络代表默认网络,帮助路由器发送路由表中无法查询的包。如果设置了全零网络路由,路由器中无法查询的包都会被送到全零网络的路由中去;
127.0.0.1 称作回送地址,属于环回地址,IP 从 127.0.0.0 到 127.255.255.255 都将环回到本地主机中;
255.255.255.255 限制广播地址,对本机来说,这个地址指本网段内(同一广播域)所有主机; 6.子网掩码
子网掩码:是一个 32 位二进制的值,可以将 IP 地址分离出网络地址和主机地址,采用点分十进制的形式。子网掩码不能单独存在它必须结合 IP 地址一起使用。
子网掩码由 1 和 0 组成,且 1 和 0 分别连续。子网掩码的长度也是 32 位。左边是网络位,用二进制数字 1 表示,1 的数目等于网络位的长度;右边是主机位用 2 进制数字 0 表示,0 的数目等于主机位的长度;这样做的目的是为了让掩码与 IP 地址做与运算时用 0 遮住原主机数,而不改变网络段的数字;而且很容易通过 0 的位数确定子网的主机数;将 32 位 IP 地址与 32 位的子网掩码各位进制进行 ‘与’ 运算,得到的是该 IP 地址的网络地址;
方法:子网掩码可以判断两台计算机是否属于同一网段,将计算机 10 进制的 IP 地址和子网掩码转换为 2 进制的形式,然后进行‘与’运算,如果网络地址是相同的,那么两台计算机就属于同一网段;
子网掩码可分为缺省子网掩码和自定义子网掩码:
a.缺省(默认)子网掩码:即为划分子网,对应的网络号都是 1,主机号位都是 0;
A 类网络缺省(默认)子网掩码:255.0.0.0
B 类网络缺省(默认)子网掩码:255.255.0.0
C 类网络缺省(默认)子网掩码:255.255.255.0
b.自定义子网掩码是将一个网络划分为几个子网,需要每一段使用不同的网络号或者子网号,实际上我们可以认为是将主机号分为两个部分:子网号和子网主机号。形式如下:
未做子网划分的 IP 地址:网络号+主机号
做子网划分的 IP 地址:网络号+子网号+子网主机号。也就是说,IP 地址在子网划分后,以前的主机号一部分给了子网号,剩下的是子网主机号;
子网掩码通常有两种格式的表示方式:
点分十进制格式,如:255.255.255.128
IP 地址后面加上‘/’符号以及 1-32 位的数字,其中 1-32 的数字表示子网掩码中网络标识位的长度;例如:192.168.1.1/24 的子网掩码也可以表示为 255.255.255.0 16.路由器
路由器:属于网络层,是连接因特网中的各局域网、广域网的设备,它会根据信道的情况自动选择和设定路由,以最佳路径,按照前后顺序发送信号。连接不同的网络,所谓不同的网络就是网络地址不同;路由器工作在 IP 协议网络层,用于实现子网之间转发数据,路由器一般包含多个网络接口,包括局域网和广域网接口,每个网络接口连接不同的网络;路由器记录着每个网络端口连接的网络信息;路由器中还包含路由表:记录了去往不同的网络地址应该送往的端口号;
作用:为每个数据帧寻找最佳的传输路径,并将其有效的传送到目的站点,在路由器中通过路由表保存着各种传输路径的相关数据,供路由选择时使用。
路由表:保存各种传输路径的相关数据,供路由选择时使用;路由表中保存着子网的标志信息、网上路由器的个数和下一个路由器的名字等内容;
静态路由表:由系统管理员事先设定好的路由表;
动态路由表:路由器根据网络系统的运行情况动态调整的路由表;
工作流程:数据包送到路由器后,通过数据包首部的目的主机 IP 地址和子网掩码计算出网络地址,即目的主机所在的网络,查找当前路由器的路由表,选择端口进行转发;下一台 IP 路由器收到数据包后继续转发,直到目的地;路由转发策略称为路由选择;
路由器和交换机之间的主要区别是:交换机发生在 OSI 参考模型的第二层(数据链路层),而路由器发生在第三层(网络层)。路由器是不同网络之间相互连接的枢纽,路由器构成了 Internet 的骨架;路由器具有判断网路地址,选择 IP 路径的功能;
17.MAC 地址(物理地址、硬件地址)的概念和作用
概念:MAC 地址就是在媒体接入层上使用的地址,也叫物理地址、硬件地址或链路地址,其被固化在适配器的 ROM 中。可见,MAC 地址实际上就是适配器地址或适配器标识符。当某台计算机使用某块适配器后,适配器上的标识符就成为该计算机的 MAC 地址。MAC 地址长度为 6 字节(48 比特),由 IEEE 的注册管理结构 RA 进行管理分配。
作用:MAC 地址是计算机的唯一标识,在数据链路层中,交换机通过识别 MAC 地址进行数据包的传输。 18.路由表中的内容
Network Destination:目标网段;Netmask:子网掩码,IP 地址与子网掩码按位与,可以得出该 IP 地址的网络号,IP 地址与子网掩码取反后按位与,可以得出该 IP 地址的主机号。Interface:达到该目标网段的本地路由器的出口 IP;Gateway:网关 IP,下一跳路由器的入口 IP,通常情况下,interface 和 gateway 是同一网段的。Metric:跳数,该条路由记录的质量,一般情况下,如果有多条到达相同目的地的路由记录,路由器会采用 metric 值小的那条路由。 19.分组转发算法流程 1.从数据报的首部提取目的主机的 IP 地址 D,得出目的网络地址 N(子网掩码和 IP 地址做与运算可得出网络地址)。 2.若 N 就是于此路由器直接相连的某个网络地址,则进行直接交付,不需要在经过其他路由器,直接把数据报交付给目的主机(这里包括把目的 IP 地址 D 转换为具体的 MAC 地址(ARP 协议),把数据报封装成 MAC 帧,在发送此帧);否则就是间接交付。 3.若路由表中有目的地址为 D 的特定主机路由,则把数据报传送给路由表中所指明的下一跳路由器。 4.若路由表中有达到网络 N 的路由,则把数据报传送给路由表中所指明的下一跳路由器。 5.若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器。 6.报告转发分组出错。
20.TFTP 与 FTP 的区别
FTP(File Transfer Protocol,文件传输协议)协议在 TCP/IP 协议族中属于应用层协议,用于在远端服务器和本地客户端之间传输文件,使用 TCP 端口 20 和 21 进行传输。端口 20 用于传输数据,端口 21 用于传输控制消息。
TFTP(Trivial File Transfer Protocol,简单文件传输协议)也是用于在远端服务器和本地主机之间传输文件的,相对于 FTP,TFTP 没有复杂的交互存取接口和认证控制,适用于客户端和服务器之间不需要复杂交互的环境。
TFTP 协议的运行基于 UDP 协议,使用 UDP 端口 69 进行数据传输。
区别:
基于的传输协议不一样:FTP 是基于 TCP TFTP 是基于 UDP
端口号不一样:FTP 是用 21.20 TFTP 是 69
速度和安全方面:FTP 在速度方面没有 TFTP 快但是 FTP 安全好
21.MTU 的概念,什么是路径 MTU? MTU 发现机制,TraceRoute
MTU:即 Maximum Transmission Unit 最大传输单元。它是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位)。
路径 MTU:路径 MTU 是指一条因特网传输路径中,从源地址到目的地址所经过的“路径”上的所有 IP 跳的最大传输单元的最小值。或者从另外一个角度来看,就是无需进行分片处理就能穿过这条“路径”的最大传输单元的最大值。
路径 MTU 的发现方法:这是确定两个 IP 主机之间路径最大传输单元的技术,其目的就是为了避免 IP 分片。首先源地址将数据报的 DF 位置位,在逐渐增大发送的数据报的大小——路径上任何需要将分组进行分片的设备都会将这种数据报丢弃并返回“数据报过大“的 ICMP 响应到源地址——这样源主机就”学习“到了无需分片就能通过这条路径的最大的最大传输单元。
Traceroute:用来侦测主机到目的主机之间所经路由情况的重要工具。原理如下:它受到目的主机的 IP 后,首先给目的主机发送一个 TTL=1 的 UDP 数据包(每次送出的为 3 个 40 字节的包,包括源地址,目的地址和包发出的时间标签),而经过的第一个路由器收到这个数据包以后,就自动把 TTL 减 1,而 TTL 变为 0 以后,路由器就把这个包给抛弃了,并同时产生 一个主机不可达的 ICMP 数据报给主机。主机收到这个数据报以后再发一个 TTL=2 的 UDP 数据报给目的主机,然后刺激第二个路由器给主机发 ICMP 数据报。如此往复直到到达目的主机。这样,traceroute 就拿到了所有的路由器 ip。Traceroute 提取发送 ICMP TTL 到期消息设备的 IP 地址并作域名解析。每次 ,Traceroute 都打印出一系列数据,包括所经过的路由设备的域名及 IP 地址,三个包每次来回所花时间。
22.ICMP 协议
概念:ICMP 的全称是 Internet Control Message Protocol ,它是一种“错误侦测与回报机制”,不传输用户数据,其目的就是让我们能够检测网路的连线状况。ICMP 数据包由一个 8 字节长的包头,其中前四个字节是固定格式,包括 8 位类型字段、8 位代码字段个 16 位校验和;后 4 个字节根据 ICMP 类型的不同而取不同的值;
作用:
侦测远端主机是否存在
建立及维护路由资料
重导数据传送路径
数据流量控制
Internet 控制报文协议,负责发送消息,报告错误;属于 TCP/IP 协议族;主要用在主机和路由器之间,ICMP 提供移动的出错报告信息,但是他的功能是报告问题而不是纠正问题,他将出错的报文返回发送方,纠正问题的功能由发送方完成;发送方根据 ICMP 提供的错误类型来确定如何才能更好的重新发送失败的数据包;
应用:Ping 命令其实就是 ICMP 协议的工作过程;Tracert 命令,跟踪路由的命令也是基于 ICMP 协议的; 23.组播和广播的概念,IGMP 的用途
组播:主机之间的通讯模式,也就是加入了同一个组的主机可以接收到此组内的所有数据,网络中的交换机和路由器只向有需求者复制并转发其所需数据。主机可以向路由器请求加入或退出某个组,网络中的路由器和交换机有选择的复制并传输数据,即只将组内数据传输给那些加入组的主机。这样既能一次将数据传输给多个有需要(加入组)的主机,又能保证不影响其他不需要(未加入组)的主机的其他通讯。
广播: 是指在 IP 子网内广播数据包,所有在子网内部的主机都将收到这些数据包。广播意味着网络向子网每一个主机都投递一份数据包,不论这些主机是否乐于接收该数据包。所以广播的使用范围非常小,只在本地子网内有效,通过路由器和网络设备控制广播传输。组播协议与现在广泛使用的单播协议的不同之处在于,一个主机用单播协议向 n 个主机发送相同的数据时,发送主机需要分别向 n 个主机发送,共发送 n 次。一个主机用组播协议向 n 个主机发送相同的数据时,只要发送 1 次,其数据由网络中的路由器和交换机逐级进行复制并发送给各个接收方,这样既节省服务器资源也节省网络主干的带宽资源。
IGMP(Internet Group Management Protocol)的用途
它用来在 ip 主机和与其直接相邻的组播路由器之间建立、维护组播组成员关系。组播路由器不需要保存所有主机的成员关系,它只是通过 IGMP 协议了解每个接口连接的网段上是否存在某个组播组的组成员,而主机只需要保存自己加入了哪些组播组。简而言之,IGMP 协议是让连接在本地局域网上的组播路由器知道本局域网上是否有主机上的某个进程参加或退出了某个组播组。
环回地址/广播地址
环回地址:127.0.0.1,通常被称为本地回环地址(Loop back address),不属于任何一个有类别地址类。它代表设备的本地虚拟接口,所以默认被看作是永远不会宕掉的接口。
作用:
a.一是测试本机的网络配置,能 PING 通 127.0.0.1 说明本机的网卡和 IP 协议安装都没有问题;
b.某些 SERVER/CLIENT 的应用程序在运行时需调用服务器上的资源,一般要指定 SERVER 的 IP 地址,但当该程序要在同一台机器上运行而没有别的 SERVER 时就可以把 SERVER 的资源装在本机,SERVER 的 IP 地址设为 127.0.0.1 同样也可以运行。
广播地址:是专门用于同时向网络中所有工作站进行发送的一个地址。在使用 TCP/IP 协议的网络中,主机标识段 host ID 为全 1 的 IP 地址为广播地址,广播的分组传送给 host ID 段所涉及的所有计算机。例如,对于 10.1.1.0 (255.255.255.0 )网段,其广播地址为 10.1.1.255(255 即为 2 进制的 11111111 ),当发出一个目的地址为 10.1.1.255 的分组(封包)时,它将被分发给该网段上的所有计算机。
24.DNS 的概念,用途,DNS 查询的实现算法
DNS 用途:DNS 是由解析器以及域名服务器组成的。域名服务器是指保存有该网络中所有主机的域名和对应 IP 地址,并具有将域名转换为 IP 地址功能的服务器。DNS 使用 TCP 与 UDP 端口号都是 53,主要使用 UDP,服务器之间备份使用 TCP。
域名到 IP 地址的解析过程的要点如下:
a.当某一个应用进程需要主机名解析为 IP 地址时,该应用进程就调用解析程序,并成为 DNS 的一个客户,把待解析的域名放在 DNS 请求报文中,以 UDP 用户数据报方式发给本地域名服务器。
b.本地域名服务器在查找域名后,把对应的 IP 地址放在回答报文中返回。应用进程获得目的主机的 IP 地址后即可进行通信。
c.若本地域名服务器不能回答该请求,则此域名服务器就暂时成为 DNS 中的另一个客户,并向其他域名服务器发出查询请求。这种过程直至找到能够回答该请求的域名服务器为止。
DNS 查询算法
主机向本地域名服务器的查询一般都是采用递归查询,即如果主机所询问的本地域名服务器不知道被查询域名的 IP 地址,那么本地域名服务器就以 DNS 客户的身份,向其他根域名服务器继续发出查询请求报文,而不是让该主机自己进行下一步的查询。 因此,递归查询返回的查询结果或是所要查询的 IP 地址,或是报错。
本地域名服务器向根服务器的查询通常采用迭代查询,即当根域名服务器收到本地域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的 IP 地址,要么告诉本地域名服务器“下一次应向哪个域名服务器进行查询”。然后让本地域名服务器进行后续的查询。根域名服务器通常把自己知道的顶级域名服务器的 IP 地址告诉本地域名服务器,让本地域名服务器再向顶级域名服务器查询。顶级域名服务器在收到本地域名服务器的查询请求后,要么给出所要查询的 IP 地址,要么告诉本地域名服务器下一步应当向哪一个权限域名服务器进行查询。本地域名服务器就这样进行迭代查询。
25.TCP 的流量控制 1.利用滑动窗口实现流量控制
如果发送方把数据发送得过快,接收方可能会来不及接收,这就会造成数据的丢失。所谓流量控制就是让发送方的发送速率不要太快,要让接收方来得及接收。利用滑动窗口机制可以很方便地在 TCP 连接上实现对发送方的流量控制。设 A 向 B 发送数据,在连接建立时,B 告诉了 A:“我的接收窗口是 rwnd = 400 ”(这里的 rwnd 表示 receiver window) 。因此,发送方的发送窗口不能超过接收方给出的接收窗口的数值。请注意,TCP 的窗口单位是字节,不是报文段。TCP 连接建立时的窗口协商过程在图中没有显示出来。再设每一个报文段为 100 字节长,而数据报文段序号的初始值设为 1。大写 ACK 表示首部中的确认位 ACK,小写 ack 表示确认字段的值 ack。
从图中可以看出,B 进行了三次流量控制。第一次把窗口减少到 rwnd=300 ,第二次又减到了 rwnd=100 ,最后减到 rwnd=0 ,即不允许发送方再发送数据了。这种使发送方暂停发送的状态将持续到主机 B 重新发出一个新的窗口值为止。B 向 A 发送的三个报文段都设置了 ACK=1 ,只有在 ACK=1 时确认号字段才有意义。TCP 为每一个连接设有一个持续计时器(persistence timer)。只要 TCP 连接的一方收到对方的零窗口通知,就启动持续计时器。若持续计时器设置的时间到期,就发送一个零窗口控测报文段(携 1 字节的数据),那么收到这个报文段的一方就重新设置持续计时器。 2.必须考虑传输速率
可以用不同的机制来控制 TCP 报文段的发送时机。如:
a.TCP 维持一个变量,它等于最大报文段长度 MSS。只要缓存中存放的数据达到 MSS 字节时,就组装成一个 TCP 报文段发送出去。
b.由发送方的应用进程指明要求发送报文段,即 TCP 支持的推送( push )操作。
c.发送方的一个计时器期限到了,这时就把已有的缓存数据装入报文段(但长度不能超过 MSS)发送出去。
3.Nagle 算法
若发送应用进程把要发送的数据逐个字节地送到 TCP 的发送缓存,则发送方就把第一个数据字节先发送出去,把后面到达的数据字节都缓存起来。当发送方接收对第一个数据字符的确认后,再把发送缓存中的所有数据组装成一个报文段再发送出去,同时继续对随后到达的数据进行缓存。只有在收到对前一个报文段的确认后才继续发送下一个报文段。当数据到达较快而网络速率较慢时,用这样的方法可明显地减少所用的网络带宽。Nagle 算法还规定:当到达的数据已达到 发送窗口大小的一半或已达到报文段的最大长度时,就立即发送一个报文段。 4.糊涂窗口综合证:TCP 接收方的缓存已满,而交互式的应用进程一次只从接收缓存中读取 1 字节(这样就使接收缓存空间仅腾出 1 字节),然后向发送方发送确认,并把窗口设置为 1 个字节(但发送的数据报为 40 字节的的话)。接收,发送方又发来 1 个字节的数据(发送方的 IP 数据报是 41 字节)。接收方发回确认,仍然将窗口设置为 1 个字节。这样,网络的效率很低。要解决这个问题,可让接收方等待一段时间,使得或者接收缓存已有足够空间容纳一个最长的报文段,或者等到接收方缓存已有一半空闲的空间。只要出现这两种情况,接收方就发回确认报文,并向发送方通知当前的窗口大小。此外,发送方也不要发送太小的报文段,而是把数据报积累成足够大的报文段,或达到接收方缓存的空间的一半大小。
26.TCP 拥塞控制 1.拥塞:拥塞:即对资源的需求超过了可用的资源。若网络中许多资源同时供应不足,网络的性能就要明显变坏,整个网络的吞吐量随之负荷的增大而下降。拥塞控制:防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提:网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机、路由器,以及与降低网络传输性能有关的所有因素。流量控制:指点对点通信量的控制,是端到端正的问题。流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。拥塞控制代价:需要获得网络内部流量分布的信息。在实施拥塞控制之前,还需要在结点之间交换信息和各种命令,以便选择控制的策略和实施控制。这样就产生了额外的开销。拥塞控制还需要将一些资源分配给各个用户单独使用,使得网络资源不能更好地实现共享。 2.几种拥塞控制方法
慢开始( slow-start )、拥塞避免( congestion avoidance )、快重传( fast retransmit )和快恢复( fast recovery )。 3.慢开始和拥塞避免
发送方维持一个拥塞窗口 cwnd ( congestion window )的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞。发送方控制拥塞窗口的原则是:只要网络没有出现拥塞,拥塞窗口就再增大一些,以便把更多的分组发送出去。但只要网络出现拥塞,拥塞窗口就减小一些,以减少注入到网络中的分组数。
慢开始算法:当主机开始发送数据时,如果立即所大量数据字节注入到网络,那么就有可能引起网络拥塞,因为现在并不清楚网络的负荷情况。因此,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。通常在刚刚开始发送报文段时,先把拥塞窗口 cwnd 设置为一个最大报文段 MSS 的数值。而在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个 MSS 的数值。用这样的方法逐步增大发送方的拥塞窗口 cwnd ,可以使分组注入到网络的速率更加合理。每经过一个传输轮次,拥塞窗口 cwnd 就加倍。一个传输轮次所经历的时间其实就是往返时间 RTT。不过“传输轮次”更加强调:把拥塞窗口 cwnd 所允许发送的报文段都连续发送出去,并收到了对已发送的最后一个字节的确认。另外,慢开始的“慢”并不是指 cwnd 的增长速率慢,而是指在 TCP 开始发送报文段时先设置 cwnd=1,使得发送方在开始时只发送一个报文段(目的是试探一下网络的拥塞情况),然后再逐渐增大 cwnd。为了防止拥塞窗口 cwnd 增长过大引起网络拥塞,还需要设置一个慢开始门限 ssthresh 状态变量(如何设置 ssthresh)。慢开始门限 ssthresh 的用法如下:
当 cwnd < ssthresh 时,使用上述的慢开始算法。
当 cwnd > ssthresh 时,停止使用慢开始算法而改用拥塞避免算法。
当 cwnd = ssthresh 时,既可使用慢开始算法,也可使用拥塞控制避免算法。
拥塞避免算法:让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,而不是加倍。这样拥塞窗口 cwnd 按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。无论在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认),就要把慢开始门限 ssthresh 设置为出现拥塞时的发送方窗口值的一半(但不能小于 2)。然后把拥塞窗口 cwnd 重新设置为 1,执行慢开始算法。这样做的目的就是要迅速减少主机发送到网络中的分组数,使得发生拥塞的路由器有足够时间把队列中积压的分组处理完毕。如下图,用具体数值说明了上述拥塞控制的过程。现在发送窗口的大小和拥塞窗口一样大。
a.当 TCP 连接进行初始化时,把拥塞窗口 cwnd 置为 1。前面已说过,为了便于理解,图中的窗口单位不使用字节而使用报文段的个数。慢开始门限的初始值设置为 16 个报文段,即 cwnd = 16 。
b.在执行慢开始算法时,拥塞窗口 cwnd 的初始值为 1。以后发送方每收到一个对新报文段的确认 ACK,就把拥塞窗口值另 1,然后开始下一轮的传输(图中横坐标为传输轮次)。因此拥塞窗口 cwnd 随着传输轮次按指数规律增长。 当拥塞窗口 cwnd 增长到慢开始门限值 ssthresh 时(即当 cwnd=16 时),就改为执行拥塞控制算法,拥塞窗口按线性规律增长。
c.假定拥塞窗口的数值增长到 24 时,网络出现超时(这很可能就是网络发生拥塞了)。更新后的 ssthresh 值变为 12(即变为出现超时时的拥塞窗口数值 24 的一半),拥塞窗口再重新设置为 1,并执行慢开始算法。 当 cwnd=ssthresh=12 时改为执行拥塞避免算法,拥塞窗口按线性规律增长,每经过一个往返时间增加一个 MSS 的大小。强调:“拥塞避免”并非指完全能够避免了拥塞。利用以上的措施要完全避免网络拥塞还是不可能的。“拥塞避免”是说在拥塞避免阶段将拥塞窗口控制为按线性规律增长,使网络比较不容易出现拥塞。 4.快重传和快恢复
快重传:如果发送方设置的超时计时器时限已到但还没有收到确认,那么很可能是网络出现了拥塞,致使报文段在网络中的某处被丢弃。这时,TCP 马上把拥塞窗口 cwnd 减小到 1,并执行慢开始算法,同时把慢开始门限值 ssthresh 减半。这是不使用快重传的情况。快重传算法首先要求接收方每收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时才进行捎带确认。接收方收到了 M1 和 M2 后都分别发出了确认。现在假定接收方没有收到 M3 但接着收到了 M4。显然,接收方不能确认 M4,因为 M4 是收到的失序报文段。根据可靠传输原理,接收方可以什么都不做,也可以在适当时机发送一次对 M2 的确认。但按照快重传算法的规定,接收方应及时发送对 M2 的重复确认,这样做可以让发送方及早知道报文段 M3 没有到达接收方。发送方接着发送了 M5 和 M6。接收方收到这两个报文后,也还要再次发出对 M2 的重复确认。这样,发送方共收到了接收方的四个对 M2 的确认,其中后三个都是重复确认。快重传算法还规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段 M3,而不必继续等待 M3 设置的重传计时器到期。由于发送方尽早重传未被确认的报文段,因此采用快重传后可以使整个网络吞吐量提高约 20%。
快恢复:
a.当发送方连续收到三个重复确认,就执行“乘法减小”算法,把慢开始门限 ssthresh 减半。这是为了预防网络发生拥塞。请注意:接下去不执行慢开始算法。
b.由于发送方现在认为网络很可能没有发生拥塞,因此与慢开始不同之处是现在不执行慢开始算法(即拥塞窗口 cwnd 现在不设置为 1),而是把 cwnd 值设置为慢开始门限 ssthresh 减半后的数值,然后开始执行拥塞避免算法(“加法增大”),使拥塞窗口缓慢地线性增大。 5.停止等待协议和滑动窗口协议
停止等待协议:是 tcp 保证传输可靠的重要途径,”停止等待”就是指发送完一个分组就停止发送,等待对方的确认,只有对方确认过,才发送下一个分组.
滑动窗口协议:之所以叫滑动窗口协议,是因为窗口是不断向前走的,该协议允许发送方在停止并等待确认前发送多个数据分组。由于发送方不必每发一个分组就停下来等待确认,因此该协议可以加速数据的传输,还可以控制流量的问题。滑动窗口协议中,允许发送方发送多个分组(当有多个分组可用时)而不需等待确认,但它受限于在流水线 中为未确认的分组数不能超过某个最大允许数 N。滑动窗口协议是 TCP 使用的一种流量控制方法,此协议能够加速数据的传输。 只有在接收窗口向前滑动时(与此同时也发送了确认),发送窗口才有可能向前滑动。收发两端的窗口按照以上规律不断地向前滑动,因此这种协议称为滑动窗口协议。当发送窗口和接收窗口的大小都等于 1 时,就是停止等待协议。
27.TIME_WAIT 状态及存在原因
客户端与服务器端建立 TCP/IP 连接后关闭 SOCKET 后,服务器端连接的端口状态为 TIME_WAIT;
主动关闭的 Socket 端会进入 TIME_WAIT 状态,并且持续 2MSL 时间长度,MSL 就是 maximum segment lifetime(最大分节生命期);这是一个 IP 数据包能在互联网上生存的最长时间,超过这个时间将在网络中消失。MSL 在 RFC 1122 上建议是 2 分钟,而源自 berkeley 的 TCP 实现传统上使用 30 秒,因而,TIME_WAIT 状态一般维持在 1-4 分钟。
主动关闭的一方在发送最后一个 ack 后就会进入 TIME_WAIT 状态停留 2MSL(max segment lifetime)时间这个是 TCP/IP 必不可少的,也就是“解决”不了的。
TIME_WAIT 状态存在的理由:
a.防止上一次连接中的包,迷路后重新出现,影响新连接(经过 2MSL,上一次连接中所有的重复包都会消失)
b.可靠的关闭 TCP 连接:在进行关闭连接四路握手协议时,最后的 ACK 是由主动关闭端发出的,如果这个最终的 ACK 丢失,服务器将重发最终的 FIN,因此客户端必须维护状态信息允 许它重发最终的 ACK。
如果不维持这个状态信息,那么客户端将响应 RST 分节,服务器将此分节解释成一个错误。因而,要实现 TCP 全双工连接的正常终止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭 的客户端必须维持状态信息进入 TIME_WAIT 状态。
Http2.0 采用二进制格式非文本格式;
Http2.0 是完全多路复用的,非有序并阻塞的只需要一个连接即可实现并行;
使用报头压缩,降低了开销;
Http2.0 让服务器可以将响应主动推送到客户端缓存中。
SMTP 用来发生邮件的,端口号 25;POP3 用来接收邮件的,端口号 110,使用 TCP 协议。
Redis 是一个由 ANSI C 语言编写,性能优秀、支持网络、可持久化的 K-K 内存数据库,并提供多种语言的 API。它常用的类型主要是 String、List、Hash、Set、ZSet 这 5 种。
Redis 在互联网公司一般有以下应用:
String:缓存、限流、计数器、分布式锁、分布式 Session
Hash:存储用户信息、用户主页访问量、组合查询
List:微博关注人时间轴列表、简单队列
Set:赞、踩、标签、好友关系
Zset:排行榜
再比如电商在大促销时,会用一些特殊的设计来保证系统稳定,扣减库存可以考虑如下设计:
上图中,直接在 Redis 中扣减库存,记录日志后通过 Worker 同步到数据库,在设计同步 Worker 时需要考虑并发处理和重复处理的问题。
通过上面的应用场景可以看出 Redis 是非常高效和稳定的,那 Redis 底层是如何实现的呢?
当我们执行 set hello world 命令时,会有以下数据模型:
dictEntry:Redis 给每个 key-value 键值对分配一个 dictEntry,里面有着 key 和 val 的指针,next 指向下一个 dictEntry 形成链表,这个指针可以将多个哈希值相同的键值对链接在一起,由此来解决哈希冲突问题(链地址法)。
sds:键 key“hello”是以 SDS(简单动态字符串)存储,后面详细介绍。
redisObject:值 val“world”存储在 redisObject 中。实际上,redis 常用 5 中类型都是以 redisObject 来存储的;而 redisObject 中的 type 字段指明了 Value 对象的类型,ptr 字段则指向对象所在的地址。
redisObject 对象非常重要,Redis 对象的类型、内部编码、内存回收、共享对象等功能,都需要 redisObject 支持。这样设计的好处是,可以针对不同的使用场景,对 5 中常用类型设置多种不同的数据结构实现,从而优化对象在不同场景下的使用效率。
无论是 dictEntry 对象,还是 redisObject、SDS 对象,都需要内存分配器(如 jemalloc)分配内存进行存储。jemalloc 作为 Redis 的默认内存分配器,在减小内存碎片方面做的相对比较好。比如 jemalloc 在 64 位系统中,将内存空间划分为小、大、巨大三个范围;每个范围内又划分了许多小的内存块单位;当 Redis 存储数据时,会选择大小最合适的内存块进行存储。
前面说过,Redis 每个对象由一个 redisObject 结构表示,它的 ptr 指针指向底层实现的数据结构,而数据结构由 encoding 属性决定。比如我们执行以下命令得到存储“hello”对应的编码:
redis 所有的数据结构类型如下:
字符串对象的底层实现可以是 int、raw、embstr(上面的表对应有名称介绍)。embstr 编码是通过调用一次内存分配函数来分配一块连续的空间,而 raw 需要调用两次。
int 编码字符串对象和 embstr 编码字符串对象在一定条件下会转化为 raw 编码字符串对象。embstr:<=39 字节的字符串。int:8 个字节的长整型。raw:大于 39 个字节的字符串。
简单动态字符串(SDS),这种结构更像 C++的 String 或者 Java 的 ArrayList
struct sdshdr {
// buf 中已占用空间的长度
int len;
// buf 中剩余可用空间的长度
int free;
// 数据空间
char buf[]; // ’\0’空字符结尾
};
get:sdsrange—O(n)
set:sdscpy—O(n)
create:sdsnew—O(1)
len:sdslen—O(1)
常数复杂度获取字符串长度:因为 SDS 在 len 属性中记录了长度,所以获取一个 SDS 长度时间复杂度仅为 O(1)。
预空间分配:如果对一个 SDS 进行修改,分为一下两种情况:
1、SDS 长度(len 的值)小于 1MB,那么程序将分配和 len 属性同样大小的未使用空间,这时 free 和 len 属性值相同。举个例子,SDS 的 len 将变成 15 字节,则程序也会分配 15 字节的未使用空间,SDS 的 buf 数组的实际长度变成 15+15+1=31 字节(额外一个字节用户保存空字符)。
2、SDS 长度(len 的值)大于等于 1MB,程序会分配 1MB 的未使用空间。比如进行修改之后,SDS 的 len 变成 30MB,那么它的实际长度是 30MB+1MB+1byte。
惰性释放空间:当执行 sdstrim(截取字符串)之后,SDS 不会立马释放多出来的空间,如果下次再进行拼接字符串操作,且拼接的没有刚才释放的空间大,则那些未使用的空间就会排上用场。通过惰性释放空间避免了特定情况下操作字符串的内存重新分配操作。
杜绝缓冲区溢出:使用 C 字符串的操作时,如果字符串长度增加(如 strcat 操作)而忘记重新分配内存,很容易造成缓冲区的溢出;而 SDS 由于记录了长度,相应的操作在可能造成缓冲区溢出时会自动重新分配内存,杜绝了缓冲区溢出。
List 对象的底层实现是 quicklist(快速列表,是 ziplist 压缩列表 和 linkedlist 双端链表 的组合)。Redis 中的列表支持两端插入和弹出,并可以获得指定位置(或范围)的元素,可以充当数组、队列、栈等。
typedef struct listNode {
// 前置节点
struct listNode *prev;
// 后置节点
struct listNode next;
// 节点的值
void \value;
} listNode;
typedef struct list {
// 表头节点
listNode head;
// 表尾节点
listNode *tail;
// 节点值复制函数
void *(*dup)(void *ptr);
// 节点值释放函数
void (*free)(void *ptr);
// 节点值对比函数
int (*match)(void *ptr, void *key);
// 链表所包含的节点数量
unsigned long len;
} list;
rpush: listAddNodeHead —O(1)
lpush: listAddNodeTail —O(1)
push: listInsertNode —O(1)
index : listIndex —O(N)
pop: ListFirst/listLast —O(1)
llen: listLength —O(N)
*4.1 linkedlist(双端链表)**
此结构比较像 Java 的 LinkedList,有兴趣可以阅读一下源码。
从图中可以看出 Redis 的 linkedlist 双端链表有以下特性:节点带有 prev、next 指针、head 指针和 tail 指针,获取前置节点、后置节点、表头节点和表尾节点的复杂度都是 O(1)。len 属性获取节点数量也为 O(1)。
与双端链表相比,压缩列表可以节省内存空间,但是进行修改或增删操作时,复杂度较高;因此当节点数量较少时,可以使用压缩列表;但是节点数量多时,还是使用双端链表划算。
4.2 ziplist(压缩列表)
当一个列表键只包含少量列表项,且是小整数值或长度比较短的字符串时,那么 redis 就使用 ziplist(压缩列表)来做列表键的底层实现。
ziplist 是 Redis 为了节约内存而开发的,是由一系列特殊编码的连续内存块(而不是像双端链表一样每个节点是指针)组成的顺序型数据结构;具体结构相对比较复杂,有兴趣读者可以看 Redis 哈希结构内存模型剖析。在新版本中list 链表使用 quicklist 代替了 ziplist 和 linkedlist:
quickList 是 zipList 和 linkedList 的混合体。它将 linkedList 按段切分,每一段使用 zipList 来紧凑存储,多个 zipList 之间使用双向指针串接起来。因为链表的附加空间相对太高,prev 和 next 指针就要占去 16 个字节 (64bit 系统的指针是 8 个字节),另外每个节点的内存都是单独分配,会加剧内存的碎片化,影响内存管理效率。
quicklist 默认的压缩深度是 0,也就是不压缩。为了支持快速的 push/pop 操作,quicklist 的首尾两个 ziplist 不压缩,此时深度就是 1。为了进一步节约空间,Redis 还会对 ziplist 进行压缩存储,使用 LZF 算法压缩。
Hash 对象的底层实现可以是 ziplist(压缩列表)或者 hashtable(字典或者也叫哈希表)。
Hash 对象只有同时满足下面两个条件时,才会使用 ziplist(压缩列表):1.哈希中元素数量小于 512 个;2.哈希中所有键值对的键和值字符串长度都小于 64 字节。
hashtable 哈希表可以实现 O(1)复杂度的读写操作,因此效率很高。源码如下:
typedef struct dict {
// 类型特定函数
dictType type;
// 私有数据
void privdata;
// 哈希表
dictht ht[2];
// rehash 索引
// 当 rehash 不在进行时,值为 -1
int rehashidx; /_ rehashing not in progress if rehashidx == -1 /
// 目前正在运行的安全迭代器的数量
int iterators; / number of iterators currently running _/
} dict;
typedef struct dictht {
// 哈希表数组
dictEntry \table;
// 哈希表大小
unsigned long size;
// 哈希表大小掩码,用于计算索引值
// 总是等于 size - 1
unsigned long sizemask;
// 该哈希表已有节点的数量
unsigned long used;
} dictht;
typedef struct dictEntry {
void *key;
union {void *val;uint64_t u64;int64_t s64;} v;
// 指向下个哈希表节点,形成链表
struct dictEntry *next;
} dictEntry;
typedef struct dictType {
// 计算哈希值的函数
unsigned int (*hashFunction)(const void *key);
// 复制键的函数
void *(*keyDup)(void *privdata, const void *key);
// 复制值的函数
void *(*valDup)(void *privdata, const void *obj);
// 对比键的函数
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
// 销毁键的函数
void (*keyDestructor)(void *privdata, void *key);
// 销毁值的函数
void (*valDestructor)(void *privdata, void *obj);
} dictType;
上面源码可以简化成如下结构:
这个结构类似于 JDK7 以前的 HashMap<String,Object>,当有两个或以上的键被分配到哈希数组的同一个索引上时,会产生哈希冲突。Redis 也使用链地址法来解决键冲突。即每个哈希表节点都有一个 next 指针,多个哈希表节点用 next 指针构成一个单项链表,链地址法就是将相同 hash 值的对象组织成一个链表放在 hash 值对应的槽位。
Redis 中的字典使用 hashtable 作为底层实现的话,每个字典会带有两个哈希表,一个平时使用,另一个仅在 rehash(重新散列)时使用。随着对哈希表的操作,键会逐渐增多或减少。为了让哈希表的负载因子维持在一个合理范围内,Redis 会对哈希表的大小进行扩展或收缩(rehash),也就是将 ht【0】里面所有的键值对分多次、渐进式的 rehash 到 ht【1】里。
Set 集合对象的底层实现可以是 intset(整数集合)或者 hashtable(字典或者也叫哈希表)。
intset(整数集合)当一个集合只含有整数,并且元素不多时会使用 intset(整数集合)作为 Set 集合对象的底层实现。
typedef struct intset {
// 编码方式
uint32_t encoding;
// 集合包含的元素数量
uint32_t length;
// 保存元素的数组
int8_t contents[];
} intset;
sadd: intsetAdd—O(1)
smembers: intsetGetO(1)—O(N)
srem: intsetRemove—O(N)
slen: intsetlen —O(1)
intset 底层实现为有序,无重复数组保存集合元素。 intset 这个结构里的整数数组的类型可以是 16 位的,32 位的,64 位的。如果数组里所有的整数都是 16 位长度的,如果新加入一个 32 位的整数,那么整个 16 的数组将升级成一个 32 位的数组。升级可以提升 intset 的灵活性,又可以节约内存,但不可逆。
ZSet 有序集合对象底层实现可以是 ziplist(压缩列表)或者 skiplist(跳跃表)。
当一个有序集合的元素数量比较多或者成员是比较长的字符串时,Redis 就使用 skiplist(跳跃表)作为 ZSet 对象的底层实现。
typedef struct zskiplist {
// 表头节点和表尾节点
struct zskiplistNode header, tail;
// 表中节点的数量
unsigned long length;
// 表中层数最大的节点的层数
int level;
} zskiplist;
typedef struct zskiplistNode {
// 成员对象
robj *obj;
// 分值
double score;
// 后退指针
struct zskiplistNode *backward;
// 层
struct zskiplistLevel {
// 前进指针
struct zskiplistNode \forward;
// 跨度—前进指针所指向节点与当前节点的距离
unsigned int span;
} level[];
} zskiplistNode;
zadd—zslinsert—平均 O(logN), 最坏 O(N)
zrem—zsldelete—平均 O(logN), 最坏 O(N)
zrank–zslGetRank—平均 O(logN), 最坏 O(N)
skiplist 的查找时间复杂度是 LogN,可以和平衡二叉树相当,但实现起来又比它简单。*跳跃表(skiplist)是一种有序数据结构,它通过在某个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。**
** **
加上一个版本控制即可
会话 0:
lock table tablelock read;
select * from tablelock; —读(查),可以(自己加了锁,自己可以读)
delete from tablelock where id=1; —写(增删改),不可以
在当前会话中访问其他表(emp 表,未加锁):
select * from emp; —读,不可以
delete from emp where eid = 1; —写,不可以
结论 1:
—如果某一个会话,对 A 表加了锁,则该会话可以对 A 表进行读操作,不能进行写操作;且该会话不能对其他表进行读、写操作。
—即如果给 A 表加了读锁,则当前会话只能对 A 表进行读操作。
会话 1(其他会话):
select * from tablelock; –读(查),可以
delete from tablelock where id=1; —写,会“等待”会话 0 将锁释放
select * from emp; –读(查),可以
delete from emp where eid=1; —写,可以
结论 2:
会话 0 给 A 表加了锁:
其他会话的操作:
a.可以对其他表(A 表以外的表)进行读、写操作
b.对 A 表:读–可以;写–需要等待释放锁
加写锁:
会话 0:
lock table tablelock write;
结论:当前会话(会话 0)可以对加了写锁的表进行任何操作(增删改查);但是不能操作(增删改查)其他表
其他会话:
对会话 0 中加写锁的表可以进行增删改查的前提是:等待会话 0 释放写锁
MySQL 表级锁的锁模式:
MyISAM 在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁,
在执行更新操作(增删改(DML))前,会自动给涉及的表加写锁。
所以对 MyISAM 表进行操作,会有以下情况:
a.对 MyISAM 表的读操作(如读锁),不会阻塞其他进程(会话)对同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其它进程的写操作。
b.对 MyISAM 表的写操作(加写锁),会阻塞其他进程(会话)对同一表的读和写操作,只有当写锁释放后,才会执行其他进程的读写操作。
创建行锁条件:
1、表中创建索引, select 。。。 where 字段(必须是索引) 不然行锁就无效。
2、必须要有事务,这样才是 行锁(排他锁)
3、在 select 语句后面 加 上 FOR UPDATE;
例子: 测试是否 加了锁,先执行 1 后执行 2
查询 1
START TRANSACTION; #加事务
SELECT UserID,Password,Age FROM AccountsDB.Accounts_InFo WHERE Accounts = ‘hello2’ FOR UPDATE; #加锁
select SLEEP(5); # 睡眠
COMMIT; #提交
查询 2 :
START TRANSACTION;
SELECT UserID,Password,Age FROM AccountsDB.Accounts_InFo WHERE Accounts = ‘hello2’ FOR UPDATE;
COMMIT;
类型 4 种
1。普通索引 index
2。唯一索引 unique
3。全文索引 fulltext 不支持中文
4。多列值索引 index
① 普通索引
| 1
2
3
4
5
6
7 | create table t_dept(
no int not null primary key,
name varchar(20) null,
sex varchar(2) null,
info varchar(20) null,
index index_no(no)
) |
| — | — |
② 唯一索引
| 1
2
3
4
5
6
7 | create table t_dept(
no int not null primary key,
name varchar(20) null,
sex varchar(2) null,
info varchar(20) null,
unique index index_no(no)
) |
| — | — |
③ 全文索引
| 1
2
3
4
5
6 | create table t_dept(
no int not null primary key,
name varchar(20) null,
sex varchar(2) null,
info varchar(20) null,
fulltext index index_no(no) |
| — | — |
④ 多列索引
| 1
2
3
4
5
6
7 | create table t_dept(
no int not null primary key,
name varchar(20) null,
sex varchar(2) null,
info varchar(20) null,
key index_no_name(no,name)
) |
| — | — |
① 普通索引
| 1
2 | create index index_name
on t_dept(name); |
| — | — |
② 唯一索引
| 1
2 | create unique index index_name
on t_dept(name); |
| — | — |
③ 全文索引
| 1
2 | create fulltext index index_name
on t_dept(name); |
| — | — |
④ 多列索引
| 1
2 | create index index_name_no
on t_dept(name,no) |
| — | — |
① 普通索引
| 1
2 | alter table t_dept
add index index_name(name); |
| — | — |
② 唯一索引
| 1
2 | alter table t_dept
add unique index index_name(name); |
| — | — |
③ 全文索引
| 1
2 | alter table t_dept
add fulltext index_name(name); |
| — | — |
④ 多列索引
| 1
2 | alter table t_dept
add index index_name_no(name,no); |
| — | — |
缺失模块。
1、请确保node版本大于6.2
2、在博客根目录(注意不是yilia根目录)执行以下命令:
npm i hexo-generator-json-content --save
3、在根目录_config.yml里添加配置:
jsonContent: meta: false pages: false posts: title: true date: true path: true text: false raw: false content: false slug: false updated: false comments: false link: false permalink: false excerpt: false categories: false tags: true