banner
NEWS LETTER

网络

Scroll down

OSI七层模型

参考:
https://www.cloudflare.com/zh-cn/learning/ddos/glossary/open-systems-interconnection-model-osi/
https://aws.amazon.com/cn/what-is/osi-model/?nc1=h_ls

http

http1.1 vs http2 vs http3

http1.0

  • 仅定义了16种状态码,默认是短连接,每一次http请求都要经历3次握手四次挥手的TCP连接

http1.1 是最早的http协议,稳定性高,兼容性好,头部信息相对较小。

  • 它使用串行请求,每个请求必须等待前一个请求的响应才能进行下一个请求,存在队头阻塞问题。
  • 没有cookie机制时,http协议是无状态的,这一次http请求和上一次http请求之间没有关联。
  • 无加密,不安全
  • http1.1不支持多路复用(真正的多路复用是从http2开始引入的),但通过引入一些技术可以模拟多路复用:keep-alive:持久连接,不必像http1.0那样每次建立tcp连接
  • 引入了更多的缓存控制策略,比如e-tag、if-unmodified-since、if-match/if-none-match
  • 断点续传,引入请求头range,状态码206(Partial Content)表示请求资源的部分内容
  • 更多的错误状态码

http2

  • 相比于http1.1的纯文本格式报文,http2采用二进制格式传输数据,
  • header请求头使用encoder压缩,减少重复发送,在服务端和客户端连接时期内会保存一个“首部表”,不必每次发送所有的header,而只需发差异数据,从而降低冗余
  • http2使用了多路复用的技术,同一个连接可以处理多个请求
  • 服务端推送,允许服务端向客户端推送数据

http3

  • QUIC(quick udp internet connections) 基于udp

为什么有cookie

因为http协议是无状态的,服务器不会记住上次和用户做了什么,引入cookie后,服务器会在返回的响应中设置set-cookie头部,set-cookie中包含sessionID,在用户这一边浏览器保存了cookie,浏览器在发起请求时带上cookie,服务器读取cookie,根据cookie识别用户身份。

域名相同,端口相同的网址可以共享cookie(不必协议相同)

cookie属性

  • name:cookie名字,不可更改
  • value:cookie的值
  • domain:指定cookie所属域名,domain的匹配从后往前
  • path:指定cookie所属路径,path和domain一起限定了cookie用于哪些URL
  • expires/max-age: expire属性是http1.0中的,到了http1.1被max-age代替。expires是一个日期格式,max-age是cookie可以存在的秒数。同时指定max-age和expires时,max-age优先生效。没有指定expires或者max-age时,这个cookie是session cookie。
  • secure是一个标记,标记了secure的cookie只有在https或其他安全协议下才会发送,想在网页中用js设置secure类型的cookie,必须保证协议是https的。
  • httponly:设置此属性目的在于:使用js的document.cookie不能访问到此cookie,从而提供一种安全措施阻止XSS攻击。
  • Samesite:是一种机制,用于定义cookie如何跨域发送,目的在于阻止CSRF(跨站请求伪造)和XSSI攻击。samesite的可选值有:lax和strict。其中strict表示禁止所有第三方的cookie,比如cookie(domain=a.com, samesite=strict),那么从其他网站访问a.com时都不会带上cookie;lax表示只有在使用危险http方法时阻止带上cookie,比如cookie(domain=a.com, samesite=lax),那么从其他网站用post方法访问a.com时不会带上此cookie。

浏览器会将cookie存储在内部数据库中,不同浏览器的存储机制不同,通常不会直接以文件的形式暴露给用户,常见浏览器cookie存储方式:

  • Chrome:使用SQLite数据库存储
  • Firefox:也使用SQLite存储,文件位于cookies.sqlite
  • Edge:与Chrome类似,使用SQLite
  • Safari:使用Plist文件或SQLite数据库存储

前端操作cookie

从前端可通过document.cookie设置和获取cookie,通过设置max-age或者expires的值来删除cookie。

cookie种类

  • session cookie,只在会话期间有效,不设置expires
  • persistent cookie,持久型cookie,储存在本地,设置了expires

因为http协议的无状态性,需要一个标记来区分不同的客户端,就有了session,session代表了客户端和服务端的一次会话过程,session中可以保存会话中的任何对象,每个session有唯一的标识符号sessionID。session保存在服务器上,这给服务器带来了负担。客户端只需要保存sessionID。在浏览器中,使用cookie的方式,sessionID通常存放在cookie中,服务端读取cookie中的sessionID,可从session列表中识别用户身份。浏览器会自动带上cookie

session像是用户信息表,cookie是用户的通行证。

token是服务端生成的一段字符串,作为客户端请求的一个标识,称为令牌,通常是JWT,包含uid+time+sign+固定参数。uid是用户身份唯一标识,time是时间戳;sign是签名, 使用 hash/encrypt 压缩成定长的十六进制字符串,以防止第三方恶意拼接;固定参数可选。当用户首次登录后,服务端会生成一个token,用户的信息被加密存放到token中,两边都会保存好这个token,在客户端,token一般存放在localStorage、cookie或者sessionStorage中;而在服务端,token存放在数据库中?。服务端不需要保存token,JWT就是一种无状态认证。用户再次发送请求时就会带上这个token,服务器收到token后验证并解析用户身份。token需要开发者手动添加

传递token的方法

在前端传递 token 到后端,通常有以下几种方法:

  1. Cookie:通过设置 HttpOnlycookie,将 token 存储在浏览器中发送给服务器。优点是简单易用,缺点是容易受到 CSRF 攻击。
  2. Header Authorization:使用 Authorization 头将 token 发送给服务器,通常格式是Bearer <token>。优点是安全性高,缺点是需要额外的配置和处理,并且一旦JWT被发放,不可撤销直到过期。
  3. Query Parameter:将 token 通过 URL 的查询参数传递给服务器。优点是简单易用,缺点是安全性较差,容易被泄露。
  4. HTML5 Web Storage:通过 localStoragesessionStorage 存储 token,再通过 Ajax 请求发送给服务器。优点是可以在前端轻松管理 token,缺点是安全性较差。

其中,推荐使用 Header Authorization 或者 Cookie 的方式,因为它们在安全性和易用性之间取得了比较好的平衡。如果需要存储用户信息等其他数据,可以考虑使用 HTML5 Web Storage。而 Query Parameter 的方式不太安全,不建议使用。

参考文档:cookie/localstorage/sessionstorage区别


通过域名可以访问网站,但只用ip不能访问,因为一个ip对应一台机器,一个机器上可能与多个网站,仅凭ip不知道要访问哪个网站,而域名进过dns解析到ip


ARP(Address Resolution Protocol)地址解析协议


http方法

HTTP 协议定义了多种请求方法,但最常用的是 GET 和 POST。这两种方法在使用和语义上有一些重要的区别:

  1. 用途

    • GET:主要用于获取(或查询)资源信息。
    • POST:主要用于提交数据到指定资源。
  2. 参数传递方式

    • GET:参数在 URL 中进行传递,形式为 http://example.com?param1=value1&param2=value2
    • POST:参数在请求体(request body)中传递,不会在 URL 中显示。
  3. 长度限制

    • GET:由于参数在 URL 中,因此受限于 URL 的长度限制。虽然 HTTP 协议本身没有定义 URL 的长度,但是大多数浏览器和服务器对 URL 长度有限制,通常约为 2000 个字符。
    • POST:理论上没有大小限制,但实际上大小可能会受到服务器的限制。
  4. 缓存

    • GET:可以被浏览器或代理服务器缓存。
    • POST:默认情况下不会被缓存。
  5. 历史记录

    • GET:由于参数在 URL 中,所以会被浏览器历史记录保存。
    • POST:请求数据不会保存在历史记录中。
  6. 数据类型

    • GET:只允许 ASCII 字符。
    • POST:没有限制,可以发送任何类型的数据。
  7. 安全性

    • GET:由于参数在 URL 中,所以安全性较低,不适合传递敏感信息(例如密码)。
    • POST:由于参数在请求体中,所以相比 GET 更安全,但仍然不应被视为完全安全的数据传输方式,因为数据默认情况下是不加密的。如果需要传输敏感信息,应该使用 HTTPS。
  8. 幂等性

    • GET:是幂等的,意味着无论请求一次还是多次,资源的状态都不会改变。
    • POST:不是幂等的,每次请求都可能导致资源状态的改变。
  9. 可见性

    • GET:数据在 URL 中对所有人都是可见的。
    • POST:数据不会显示在 URL 中。

这些区别的理解有助于我们更好地选择使用哪种 HTTP 方法来满足特定的需求。

常见错误提示

  • ERR_CONNECTION_RESET 连接被重置
  • Nginx

nginx 配置

nginx配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$tree /usr/local/nginx
├── client_body_temp
├── conf # Nginx所有配置文件的目录
│ ├── fastcgi.conf # fastcgi相关参数的配置文件
│ ├── fastcgi.conf.default # fastcgi.conf的原始备份文件
│ ├── fastcgi_params # fastcgi的参数文件
│ ├── fastcgi_params.default
│ ├── koi-utf
│ ├── koi-win
│ ├── mime.types # 媒体类型
│ ├── mime.types.default
│ ├── nginx.conf # Nginx主配置文件
│ ├── nginx.conf.default
│ ├── scgi_params # scgi相关参数文件
│ ├── scgi_params.default
│ ├── uwsgi_params # uwsgi相关参数文件
│ ├── uwsgi_params.default
│ └── win-utf
├── fastcgi_temp # fastcgi临时数据目录
├── html # Nginx默认站点目录
│ ├── 50x.html # 错误页面优雅替代显示文件,例如当出现502错误时会调用此页面
│ └── index.html # 默认的首页文件
├── logs # Nginx日志目录
│ ├── access.log # 访问日志文件
│ ├── error.log # 错误日志文件
│ └── nginx.pid # pid文件,Nginx进程启动后,会把所有进程的ID号写到此文件
├── proxy_temp # 临时目录
├── sbin # Nginx命令目录
│ └── nginx # Nginx的启动命令
├── scgi_temp # 临时目录
└── uwsgi_temp # 临时目录

Nginx.conf主配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
root@e8a4a894649f:/etc/nginx# cat nginx.conf 
# 运行用户,默认Nginx,可不设置
user nginx;
# Nginx进程数,一般和CPU内核数同
worker_processes auto;
# 错误日志存放目录
error_log /var/log/nginx/error.log notice;
# 进程pid存放位置
pid /var/run/nginx.pid;

events {
accept_mutex on; #设置网路连接序列化,防止惊群现象发生,默认为on
multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off
#use epoll; #事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport
worker_connections 1024;# 单个后台进程最大并发数
}

http {
include /etc/nginx/mime.types; # 支持的媒体类型和文件扩展名
default_type application/octet-stream; # 默认文件类型
# 日志模式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# Nginx访问日志存放位置
access_log /var/log/nginx/access.log main;

sendfile on; # 开启高效传输模式
#tcp_nopush on; # 减少网络报文段数量

keepalive_timeout 65; # 超时时间

#gzip on; # 开启gzip压缩

include /etc/nginx/conf.d/*.conf; # 包含的子配置项
}

子配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
server {
listen 80; # 配置的监听端口
listen [::]:80;
server_name localhost; # 配置域名

#access_log /var/log/nginx/host.access.log main;

location / {
root /usr/share/nginx/html; # 默认启动目录
index index.html index.htm; # 默认访问文件
}

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

location匹配规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
location 匹配规则
#优先级1,精确匹配,根路径
location =/ {
return 400;
}

#优先级2,以某个字符串开头,以av开头的,优先匹配这里,区分大小写
location ^~ /av {
root /data/av/;
}

#优先级3,区分大小写的正则匹配,匹配/media*****路径
location ~ /media {
alias /data/static/;
}

#优先级4 ,不区分大小写的正则匹配,所有的****.jpg|gif|png 都走这里
location ~* .*\.(jpg|gif|png|js|css)$ {
root /data/av/;
}

#优先7,通用匹配
location / {
return 403;
}

配置项

  • try_files
    语法:try_files [files],比如:
1
2
3
location / {
try_files $uri $uri/ index.html
}

位于server或location块内,当匹配到此location块内时,按照指定的顺序检查文件是否存在,使用最先找到的文件响应请求。如果最后仍然找不到可用的文件,ng将返回404

http状态码

==301 move permanent==
永久地移到了新的url,新的url在location中给出,浏览器自动访问这个新的url。浏览器会缓存这个重定向关系,下次访问会跳过原url的请求,转而请求新的。和302相比减少了
一次网络请求。
image.png image.png

==302 ==
暂时移动到新地址,每次发送请求时,浏览器都需要向服务器询问一次,收到302响应后再重定向
==304 not modified(协商缓存)==
用户的缓存和服务器端相比没有变化,本次请求不会返回内容

域名

  • ROOT 根域,一个.,是整个域名系统的起点
  • TLD(top-level-domain)顶级域名,比如.com, .cn, .org。TLD有一个记录列表,记录了所有顶级域名
  • eTLD(effective-top-level-domain)有效顶级域名,它是将TLD和某种关系绑定起来,形成一个新的顶级域名。比如github.io,它把github和.io顶级域名绑定在一起形成新的eTLD,a.github.io和b.github.io将不能共享cookie,解决了cookie共享的问题。eTLD也有自己的列表,叫Public Suffix List。通过查询PSL,就可以知道域名是否是有效顶级域名。

参考文档:TLD和eTLD的区别

查看内网出口ip

1
2
3
4
5
6
7
8
9
10
11
# linux
curl myip.ipip.net
当前 IP:119.39.38.201 来自于:中国 湖南 长沙 联通

curl ifconfig.me
119.39.38.201%

curl httpbin.org/ip
{"origin": "119.39.38.201"}

curl www.trackip.net/i

本地使用mkcert安装ssl证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
lilonghui@lilonghuideMacBook-Pro ~ % mkcert -install
Sudo password:
The local CA is now installed in the system trust store! ⚡️
Warning: "certutil" is not available, so the CA can't be automatically installed in Firefox! ⚠️
Install "certutil" with "brew install nss" and re-run "mkcert -install" 👈
The local CA is now installed in Java's trust store! ☕️

lilonghui@lilonghuideMacBook-Pro ~ % mkcert -cert-file ~/ssl/server.crt -key-file ~/ssl/server.key csdn.net '*.csdn.net'’ localhost 127.0.0.1 ::1
Note: the local CA is not installed in the Firefox trust store.
Run "mkcert -install" for certificates to be trusted automatically ⚠️

Created a new certificate valid for the following names 📜
- "csdn.net"
- "*.csdn.xn--net-to0a"
- "localhost"
- "127.0.0.1"
- "::1"

Reminder: X.509 wildcards only go one level deep, so this won't match a.b.csdn.xn--net-to0a ℹ️

The certificate is at "/Users/lilonghui/ssl/server.crt" and the key at "/Users/lilonghui/ssl/server.key" ✅

It will expire on 26 April 2024 🗓
1
2
#测试端口是否开启,使用netcat
nc -vz localhost 443

http header

range表示

大文件断点续传

localhost/127.0.0.1/0.0.0.0的异同

参考文档:The Difference Between 127.0.0.1, 0.0.0.0, And localhost That Every Developer Should Know

共同点

  • 这三个都是特殊ip
  • 均属于A类ip地址
  • 都是IPV4地址

不同

127.0.0.1

127.0.0.1属于loopback地址,所有网络号为127的ip地址都属于loopback address

Loopback address: 所有发送到此类地址的包都会是一个回环

127.0.0.1表示本地地址,只在本地机器上有效

localhost

localhost127.0.0.1对应的域名,可用来获取在这台机器上运行的网络服务。在大部分系统中,localhost指向IPV4中的127.0.0.1以及IPV6中的::1

0.0.0.0

在服务器上,0.0.0.0指向此机器上所有可用的IPV4地址,比如,一台机器同时拥有192.168.1.110.1.22.0两个ip地址,当某个服务在0.0.0.0监听时,通过这两个ip均可访问到此服务。

在路由中,0.0.0.0指向默认路由,即在路由表中未找到指定路由时的默认路由,表示目标机器不可用

如果一个主机未被分配ip0.0.0.0指向本机

【注】当一个服务在localhost监听时,例如localhost:2233,通过0.0.0.0:2233是访问不到的;反过来,当一个服务在0.0.0.0:2233监听时,通过127.0.0.1:2233、localhost:2233、局域网ip:2233 都可以访问到这个服务

DNS协议

DNS服务器

连接网络后,如何知道dns服务器的ip地址?
最常见的是通过DHCP自动分配,DHCP(Dynamic Host Config Protocol)动态主机配置协议,这个协议用于动态地给网络(内网)中的设备分配ip,分配的过程有四个步骤(DORA)
image.png

  1. Discover: 设备连接网络后,广播一个DHCP Discover包,询问:“有DHCP服务器吗,请返回ip和网络配置”,0.0.0.0->255.255.255.255(本地链路广播地址),子网内所有设备都将听到这个请求。值得注意的是,在收到回复之前,本设备是没有子网ip的,因此将0.0.0.0作为临时源地址,同时将本机MAC放到DHCP数据里面(如下图所示),而且数据链路层的Ethernet II也存放了源和目标设备的MAC地址
    image.png

image.png

  1. Offer:DHCP服务器(图中的172.20.52.1,通常是路由器)收到请求,回复一个offer包,其中包含:分配给请求设备的子网ip、子网掩码、网关地址、域名服务器(关键),如果返回了多个域名服务器,。发送Offer包的形式可广播可单播,广播比较通用也最兼容。
    image.png

  2. Request:设备在收到DHCP Offer包后会再次广播一个Request包,表示接收这个Offer包的配置。Request还是走广播,0.0.0.0 -> 255.255.255.255

  3. Ack:被选择的DHCP服务器收到Request包后最后发送一个ACK包,表示收到和确认本次配置

以上是一次完整的DHCP过程,另一个常见的场景是DHCP Lease Renewal租约续期。设备在经过一次完整的DORA过程后,下一次重新连接该网络,发现上次分配的ip还在有效期内(上图中可见一个IP Address Lease Time),于是简化过程跳过Discover和Offer步骤,只Request和ACK即完成

mDns 多播dns

DNS缓存

dns缓存分为os缓存和浏览器缓存,使用浏览器访问时,会优先访问浏览器缓存,未命中则访问OS缓存,最后在递归访问DNS服务器。
浏览器缓存——》OS缓存——》hosts文件——》dns服务器
image.png

浏览器缓存

浏览器DNS缓存的时间于DNS服务器返回的ttl值无关,每种浏览器会有自己的dns缓存时间

  • Chrome缓存时间默认是1min,通过chrome://net-internals/#dns可以查看各域名的dns缓存时间
  • Firefox缓存时间默认1min,在about:config首选项中搜索network.dnscachenetwork.dnsCacheExpiration为缓存时间、network.dnsCacheEntries为缓存条数、network.dnsCacheExpirationGracePeriod为缓存时间,等于0时表示不缓存

OS缓存

操作系统的dns缓存会参考DNS服务器的TTL值,但不完全等于。

  • windows下,ipconfig/displaydns命令展示已缓存的域名,ipconfig/flushdns清空已缓存的dns记录
  • Linux下,nscd进程负责管理dns缓存,重启nscd进程就
  • macOS下,lookupd -flushcache清空缓存

比较几个命令

dig nslookup traceroute
用途 用于查询dns记录 用于查询dns记录 网络路径追踪,从源主机到目标主机的路径
输出格式 详细 简单 每一跳的延迟和状态
指定dns服务器 支持 支持
适用场景 脚本化处理,详细的dns查询 快速查询dns 网络路径诊断
协议 DNS DNS ICMP TCP UDP

SSE

http协议无法做到服务器主动推送消息,但有一种变通的方法:服务器向客户端声明,接下来要发送的是流信息,发送的不是一次性的数据包,而是一个数据流,将连续不断的发送过来。这是客户端就不会关闭链接,会一直等着服务器发新的数据流,视频就是这样的方式。可以理解为是一次用时很长的下载。

和websocket不同,sse是单向通道,从服务器到浏览器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// server.js
const http = require('http')
http.createServer((req, res) => {
const path = "."+req.url;
if(path==='./stream') {
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Access-Control-Allow-Origin": "*",
});
res.write("retry: 10000\n");
res.write("event: connecttime\n");
res.write("data: " + (new Date()) + "\n\n");
res.write("data: " + (new Date()) + "\n\n");

const timer = setInterval(function() {
res.write("data: "+new Date()+"\n\n");
}, 5000);
req.socket.addListener("close", function() {
console.log("client connection stopped");
clearInterval(timer)
}, false)
}
}).listen('8844', '127.0.0.1');
console.log('server-side-event is running at port 8844')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!-- client.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="example"></div>
<button id="closeBtn">close</button>
<script>
const source = new EventSource('http://127.0.0.1:8844/stream');
const div = document.getElementById('example');
const closeBtn = document.querySelector("#closeBtn");
closeBtn.onclick = function(event) {
source.close();
}

source.onopen = function (event) {
div.innerHTML += '<p>Connection open ...</p>';
};

source.onerror = function (event) {
div.innerHTML += '<p>Connection close.</p>';
};

source.addEventListener('connecttime', function (event) {
div.innerHTML += ('<p>Start time: ' + event.data + '</p>');
}, false);

source.onmessage = function (event) {
div.innerHTML += ('<p>Ping: ' + event.data + '</p>');
};

</script>
</body>
</html>

websocket

理解websocket

  • 为什么有了http还需要websocket
其他文章