跳转至

网络知识

参考1 参考2

网络协议作用

网络协议是计算机网络中用于数据交换的一组规则和标准。它们定义了数据如何在网络中传输、如何被封装、如何被识别以及如何被处理。

1 HTTP(HyperText-Transfer-Protocol)

1.1 HTTP 协议格式

HTTP 的请求和响应的消息协议是一样的,分为三个部分,起始行消息头消息体。这三个部分以 CRLF 作为分隔符。最后一个消息头有两个 CRLF,用来表示消息头部的结束。 1. HTTP 请求的起始行称为请求行,形如 GET /index.html HTTP/1.1 2. HTTP 响应的起始行称为状态行,形如 200 ok 3. 消息头部有很多键值对组成,多个键值对之间使用 CRLF 作为分隔符,也可以完全没有键值对。形如 Content-Encoding: gzip 4. 消息体是一个字符串,字符串的长度是由消息头部的 Content-Length 键指定的。

客户端报文示例:

GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi

服务端响应报文示例:

HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain
GET、HEAD请求没有消息体,POST、PUT请求有消息体

1.2 http 状态码

状态码 状态码英文名称 中文描述
200 OK 请求成功。一般用于 GET 与 POST 请求
301 Moved Permanently 永久性重定向。请求的资源已被永久的移动到新 URI,返回信息会包括新的 URI,浏览器会自动定向到新 URI。今后任何新的请求都应使用新的 URI 代替
302 Found 临时性重定向。与 301 类似。但资源只是临时被移动。客户端应继续使用原有 URI
304 Not Modified 极少人知道这个错误,因为大部分后端开发者的前端 Javascript 开发经验都严重不足。当你用 Chrome 打开一个经常访问的网站,看看 Network 传输的静态资源就可以看到很多 304 状态码。它表示该资源被浏览器缓存了不需要重新请求服务器。
400 Bad Request 用于参数验证,少了一个参数或者参数类型错误之类的。
401 Unauthorized 权限不足,这个很好理解,就是资源存在但是不让你访问。
403 Forbidden 资源禁止访问,如果你的 IP 列为黑名单了,就会发生这种错误。
404 Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置 " 您所请求的资源无法找到 " 的个性页面。也可以在服务器拒绝请求且不想说明理由时使用
500 Internal Server Errorv 服务器内部错误,无法完成请求,也可能是 web 应用存在 bug 或某些临时故障
502 Bad Gateway 后端服务挂掉或者压力过大的时候, Nginx 接到的请求无法及时传递给后端的服务进行处理,这个时候就会出现 502 错误。这个也非常常见,知乎豆瓣网站经常开小差的时候发生的错误就是这个。

参考 https://juejin.cn/post/6844904202863394830

1.3 HTTP 请求方法

1.3.1 GET

  1. GET 是最常用的方法。通常用于请求服务器发送某个资源

1.3.2 HEAD

类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头

1.3.3 POST

POST 方法起初是用来向服务器输入数据的。实际上,通常会用它来支持 HTML 的表单。表单中填好的数据通常会被送给服务器,然后由服务器将其发送到它要去的地方 (比如,送到一个服务器网关程序中,然后由这个程序对其进行处理)。

1.3.4 PUT

从客户端向服务器传送的数据取代指定的文档的内容。与 GET 从服务器读取文档相反,PUT 方法会向服务器写入文档。有些发布系统允许用户创建 Web 页面,并用 PUT 直接将其安装到 Web 服务器上去。

1.4 网址,URL(Uniform Resource Locator ),域名

网址一般又称 URL 或域名,但域名和网址间还是有点区别的。 如 https://www.baidu.com,域名指 baidu.com,网址和 url 是 https://www.baidu.com

1.5 网址组成

1.6 域名结构

域名结构层次:主机名.机构名.网络名.顶级域名

1.7 域名服务器

13 个根域名 https://www.internic.net/domain/named.root

1.8 域名解析过程

域名解析步骤: 1. 浏览器缓存 - 当用户通过浏览器访问某域名时,浏览器首先会在自己的缓存中查找是否有该域名对应的 IP 地址(若曾经访问过该域名且没有清空缓存便存在); 2. 系统缓存 - 当浏览器缓存中无域名对应 IP 则会自动检查用户计算机系统 Hosts 文件 DNS 缓存是否有该域名对应 IP; 3. 路由器缓存 - 当浏览器及系统缓存中均无域名对应 IP 则进入路由器缓存中检查,以上三步均为客服端的 DNS 缓存; 4. ISP(互联网服务提供商)DNS 缓存 - 当在用户客服端查找不到域名对应 IP 地址,则将进入 ISP DNS 缓存中进行查询。比如你用的是电信的网络,则会进入电信的 DNS 缓存服务器中进行查找; 5. 根域名服务器 - 当以上均未完成,则进入根服务器进行查询。全球仅有 13 台根域名服务器,1 个主根域名服务器,其余 12 为辅根域名服务器。根域名收到请求后会查看区域文件记录,若无则将其管辖范围内顶级域名(如.com)服务器 IP 告诉本地 DNS 服务器; 6. 顶级域名服务器 - 顶级域名服务器收到请求后查看区域文件记录,若无则将其管辖范围内主域名服务器的 IP 地址告诉本地 DNS 服务器; 7. 主域名服务器 - 主域名服务器接受到请求后查询自己的缓存,如果没有则进入下一级域名服务器进行查找,并重复该步骤直至找到正确纪录; 8. 保存结果至缓存 - 本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时将该结果反馈给客户端,客户端通过这个 IP 地址与 web 服务器建立链接。

1.9 DNS 递归查询和迭代查询

  • 递归查询:主机向本地域名服务器的查询一般都是采用递归查询
  • 特点:本地 DNS 服务器只返回一次结果给客户端
  • 迭代查询:本地域名服务器向根域名服务器的查询的迭代查询。
  • 特点:本地 DNS 服务器多次与顶层 DNS 服务器通信

1.10 如何刷新 dns 缓存

windows 上

ipconfig /flushdns

1.11 从浏览器输入网址发生了什么

参考 https://juejin.cn/post/6844903922084085773 大致步骤如下: 1. URL 解析 2. DNS 查询 3. TCP 连接 4. 处理请求 5. 接受响应 6. 渲染页面

2 网络模型

2.1 OSI 七层网络模型

  1. 物理层 (Physical Layer):物理层是 OSI 参考模型的最低层,它利用传输介质为数据链路层提供物理连接。它主要关心的是通过物理链路从一个节点向另一个节点传送比特流,物理链路可能是铜线、卫星、微波或其他的通讯媒介。它关心的问题有:多少伏电压代表 1 ?多少伏电压代表 0 ?时钟速率是多少?采用全双工还是半双工传输?总的来说物理层关心的是链路的机械、电气、功能和规程特性。
  2. 数据链路层 (Data Link Layer):数据链路层是为网络层提供服务的,解决两个相邻结点之间的通信问题,传送的协议数据单元称为 数据帧。数据帧中包含物理地址(又称 MAC 地址)、控制码、数据及校验码等信息。该层的主要作用是通过校验、确认和反馈重发等手段,将不可靠的物理链路转换成对网络层来说无差错的数据链路。此外,数据链路层还要协调收发双方的数据传输速率,即进行流量控制,以防止接收方因来不及处理发送方来的高速数据而导致缓冲器溢出及线路阻塞。
  3. 网络层 (Network Layer):网络层是为传输层提供服务的,传送的协议数据单元称为 数据包(分组)。该层的主要作用是解决如何使数据包通过各结点传送的问题,即通过路径选择算法(路由)将数据包送到目的地。另外,为避免通信子网中出现过多的数据包而造成网络阻塞,需要对流入的数据包数量进行控制(拥塞控制)。当数据包要跨越多个通信子网才能到达目的地时,还要解决网际互连的问题。
  4. 传输层 (Transport Layer):传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务,包括处理差错控制和流量控制等问题,传输层传送的协议数据单元称为 数据段(报文)。该层向高层屏蔽了下层数据通信的细节,使高层用户看到的只是在两个传输实体间的一条主机到主机的、可由用户控制和设定的、可靠的数据通路。
  5. 会话层 (Session Layer):会话层主要功能是管理和协调不同主机上各种进程之间的通信(对话),即负责建立、管理和终止应用程序之间的会话。会话层得名的原因是它很类似于两个实体间的会话概念。例如,一个交互的用户会话以登录到计算机开始,以注销结束。
  6. 表示层 (Presentation Layer):数据压缩、加密以及数据描述,这使得应用程序不必关心在各台主机中数据内部格式不同的问题。
  7. 应用层 (Application Layer):应用层是 OSI 参考模型的最高层,是用户与网络的接口。该层通过应用程序来完成网络用户的应用需求,如文件传输、收发电子邮件等。

DNS属于应用层协议

2.2 TCP/IP 模型

TCP/IP 模式简化了 OSI 模型,只分四层,第一层也叫网络接口层。

3 IP

IP 地址被分为 5 个类别 此外还有私有地址,不会在 internet 网上出现,用于企业网络(内网) - 1 个 A 类地址:10.0.0.0~10.255.255.255/8 ($2^{24}$ 个地址) - 16 个 B 类地址:172.16.0.0~172.31.255.255/16 ($162^{16}$ 个地址) - 256 个 C 类地址:192.168.0.0~192.168.255.255/24 ($2562^8$ 个地址)

表一:IP 地址的分配范围

网络类别 最大可分配网络数 第一个可分配网络号 最后一个可分配网络号 每一个网络中主机最大数
A $2^{7}-2$ 1 126 $2^{24}-2$
B $2^{14}-1$ 128.1 191.255 $2^{16}-2$
C $2^{21}-1$ 192.0.1 223.255.255 $2^{8}-2$

表二: 特殊的 IP 地址

网络号 主机号 范围 源地址使用 目的地址使用 含义
0 0 0.0.0.0 可以 不可以 在本网络上的本主机
127 非全 0 或非全 1 可以 可以 用于本地软件环回测试

IP 数据包结构组成如下: IP 数据包中记录了 (部分) - 源 IP 地址(Source Addresses) - 目标 IP 地址(Destination Addresses) - 分段序号(Fragment Offset) - 生存时间(TTL) - 上层协议类型 (Protocol): 用于标识数据块交付给 udp 还是 tcp 或者 icmp 等。 - IP 数据报长度(Total Length):$2^{16}-1=65535$,但要受 MTU(Maximum Transmission Unit)限制,MTU 一般 1500 字节

title: IP头部字节数
IP头部范围20~60字节。
IP头部字节数由IHL(占4bit)标识,计算公式=$IHL*4byte$,IHL占4bit,最大表示15,所以最大值是60.
title: TTL作用
TTL长度8 bit,最大值是255,TTL的一个推荐值是64。虽然从字面上翻译,TTL是IP数据包在计算机网络中的存在的最长时间。但实际上TTL是IP数据包在网络中可以转发的最大跳数。TTL字段由IP数据包的发送者设置,在IP数据包从源到目的的整个转发路径上,每经过一个路由器,把该TTL的值减1,然后再将IP包转发出去。如果在IP包到达目的IP之前,TTL减少为0,路由器将会丢弃收到的TTL=0的IP包并向发送者发送 ICMP time exceeded消息。TTL的主要作用是避免IP包在网络中的无限循环和收发,节省了网络带宽,并能使IP包的发送者能收到告警消息。这个字段可以防止由于故障而导致IP包在网络中不停被转发。
title: 分段序号作用
该字段对包含分段的上层数据包的IP包赋予序号。由于IP包在网络上传送的时候不一定能按顺序到达,这个字段保证了目标路由器在接受到IP包之后能够还原分段的上层数据包。到某个包含分段的上层数据包的IP包在传送时丢失,则整个一系列包含分段的上层数据包的IP包都会被要求重传。

4 TCP

TCP(Transmission Control Protocol) 为应用程序之间提供面向连接的可靠的字节流服务。TCP 为全双工协议,提供流控制机制,即允许接收方控制发送方的发送速度,此外还提供拥塞控制功能。 - TCP 头部组成如下 - Source Port(Destination Port) - Sequence Number:用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。主要用来解决网络报乱序的问题; - Acknowledgment Number:期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A 的确认报文段中确认号就为 701。 - Window Size:窗口大小,也就是有名的滑动窗口,用来进行流量控制。指定从被确认的字节算起可以发送多少个字节,窗口大小字段为 0 是合法的,说明已经接收到了 确认号-1 个字节,但是接收端没有来得及取走数据。 - ACK:此标志表示应答域有效,就是说前面所说的 TCP 应答号将会包含在 TCP 数据包中; - SYN:表示同步序号,用来建立连接。SYN 标志位和 ACK 标志位搭配使用,当连接请求的时候,SYN=1,ACK=0;连接被响应的时候,SYN=1,ACK=1; - FIN:表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了。

4.1 TCP 协议作用

TCP 协议使用的是面向连接的方法进行通信的,其作用如下: - 面向流的处理:TCP 以流的方式处理数据。换句话说,TCP 可以一个字节一个字节地接收数据,而不是一次接收一个预订格式的数据块。TCP 把接收到的数据组成长度不等的段,再传递到网际层。 - 重新排序:如果数据以错误的顺序到达目的地,TCP 模块能够对数据重新排序,来恢复原始数据。 - 流量控制:TCP 能够确保数据传输不会超过目的计算机接收数据的能力。 - 优先级与安全:为 TCP 连接设置可选的优先级和安全级别。 - 适当的关闭:以确保所有的数据被发送或接收以后,再进行关闭连接。

4.2 TCP3 次握手 4 次挥手

title: 为什么acknum=seq+1?
在建立连接时,tcp携带的数据是空的,这时标准默认+1。
在传输数据过程中,acknum=seq+datasize。
title: 为什么需要3次握手?
避免失效的连接请求到达服务端时,服务端再次确认连接请求,并又一次向客户端发送确认连接报文段。
title: 为什么要4次挥手
客户端发送FIN报文段给服务端时,服务端首先响应客户端的断开请求,但服务端可能还有数据需要传输给客户端;所以当服务端也发送完毕数据后又发送FIN报文段给客户端,表明我数据也发完了,可以关闭连接了。
title: 为什么需要TIME_WAIT
1. 确保最后一个确认报文能够到达。如果 B 没收到 A 发送来的确认报文,那么就会重新发送连接释放请求报文,A 等待一段时间就是为了处理这种情况的发生。

2. 等待一段时间是为了让本连接持续时间内所产生的所有报文都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文。
title: 为什么TIME_WAIT是2MSL
其中MSL(Maximum Segment Lifetime)表示报文段的最大生命周期,RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。

1. 保证TCP协议的全双工连接能够可靠关闭(客户端和服务端均正常关闭连接)
2. 保证这次连接的重复数据段从网络中消失(因为极端情况下,一来一回需要的最大时间就是2MSL。)
title: syn攻击
攻击客户端在短时间内伪造大量不存在的IP地址,向服务器不断地发送SYN包,服务器回复确认包,并等待客户的确认。由于源地址是不存在的,服务器需要不断的重发直至超时,这些伪造的SYN包将长时间占用未连接队列,正常的SYN请求被丢弃,导致目标系统运行缓慢,严重者会引起网络堵塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。

参考资料 1:TCP的seq和ack号计算方法

4.3 TCP 滑动窗口

  • TCP 头部 window size 块指定了接收端可以接收数据的大小;
  • 发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。

4.4 TCP 流量控制

流量控制是为了控制发送方发送速率,保证接收方来得及接收。

接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。

4.5 TCP 拥塞控制

如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。 TCP 主要通过四个算法来进行拥塞控制: 1. 慢开始 2. 拥塞避免 3. 快重传 4. 快恢复。

4.5.1 慢开始

慢开始算法的思路是这样的: 当主机开始发送数据时,由于并不清楚网络的负荷情况,所以如果立即把大量数据字节注入到网络,那么就有可能引起网络发生拥塞。经验证明,较好的方法是先探测一下,即 由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。

慢开始

4.5.2 拥塞避免

拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢地增大,即每经过一个往返时间 RTT 就把 发送方的拥塞窗口 cwnd 加 1,而不是像开始阶段那样加增长。因此在拥塞避免阶段就有“加法增大”AI (Additive Increase) 的特点。这表明 在拥塞避免阶段,拥塞窗口 cwnd 按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。

4.5.3 快重传

快重传要求接收方在收到一个失序的报文段后就 立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方,可提高网络吞吐量约 20%)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要 一连收到三个重复确认 就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。

快重传

4.5.4 快恢复

快恢复算法是配合快重传 的,考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是 将 cwnd 设置为 ssthresh 减半后的值,然后执行拥塞避免算法,使 cwnd 缓慢增大。

5 一些网络设计机制

5.1 心跳机制

  • 参考
  • 心跳作用:
  • 保持连接:一个客户端连接服务器以后,如果长期没有和服务器有数据来往,可能会被防火墙程序关闭连接,有时候我们并不想要被关闭连接。
  • 检测死链

5.1.1 TCP 的 keepalive 选项

  • 设置 keepalive
//on 是 1 表示打开 keepalive 选项,为 0 表示关闭,0 是默认值  
inton = 1;  
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
  • 我们可以使用如下命令查看 Linux 系统上的上述三个值的设置情况:以下 linux 下是默认配置
$ sysctl -a | grep keepalive
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_intvl = 75

上面的 keepalive 选项表示如果一个连接上 7200s 后没有任何数据发送,则设置了这个选项的端向对端发送 keepalive 保活报文,它会有如下三种结果: - 对端回复 ACK。则本端 TCP 认为该连接依然存活。继续等 7200s 后再发送 keepalive 报文。 - 对端回复 RESET。说明对端进程已经重启,本端的应用程序应该关闭该连接。 - 没有对端的任何回复。则本端做重试,如果重试 9 次(前后重试间隔为 75 秒)仍然不可达,则向应用程序返回错误信息,ETIMEOUT(无任何应答)或 EHOST - 设置 keepalive 选项

//发送 keepalive 报文的时间间隔  
intval = 7200;  
setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val));  
//两次重试报文的时间间隔  
intinterval = 75;  
setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(interval));  
intcnt = 9;  
setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt));

6 粘包和拆包

6.1 为什么 UDP 不会发生粘包拆包问题

由于UDP有消息保护边界,每个UDP数据包都是独立的,每个数据报都有自己的完整性,不依赖于其他数据报,不会发生粘包拆包问题;而TCP是面向流,没有边界,而操作系统在发送TCP数据时,会通过缓冲区来进行优化,这导致TCP协议中可能发生粘包拆包问题。

6.2 什么是消息保护边界

**消息保护边界**,就是指传输协议把数据当作一条独立的消息在网上传输,接收端只能接收独立的消息。也就是说存在保护消息边界,接收端一次只能接收发送端发出的一个数据包。而面向流则是指无保护消息保护边界的,如果发送端连续发送数据,接收端有可能在一次接收动作中,会接收两个或者更多的数据包。
  • 什么是粘包:指 TCP 协议中,发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
  • 什么是拆包:如果一次请求发送的数据量比较大,超过了缓冲区大小,TCP 就会将其拆分为多次发送,这就是拆包。

6.3 粘包和拆包解决方案

  • 发送端将每个包都封装成固定的长度,比如 100 字节大小。如果不足 100 字节可通过补 0 或空等进行填充到指定长度;
  • 发送端在每个包的末尾使用固定的分隔符,例如 \r\n。如果发生拆包需等待多个包发送过来之后再找到其中的 \r\n 进行合并;例如,FTP 协议;
  • 将消息分为头部和消息体,头部中保存整个消息的长度,只有读取到足够长度的消息之后才算是读到了一个完整的消息;
  • 通过自定义协议进行粘包和拆包的处理。