文章

网络

复习用

网络

tcp报文

TCP 头格式

tcp三次握手

TCP 三次握手

  • 客户端发送:SYN、随机序列号x
  • 服务端发送:SYN、ACK、随机序列号y、确认应答号x+1
  • 客户端发送:ACK,可以携带数据

tcp为什么不是两次握手

  • 防止旧的SYN建立连接:如果只有两次握手,那么服务端收到SYN后直接进入established状态(此时可以发送数据),然后返回ack给客户端,如果这个SYN是旧的,那么最终客户端发现不是想要的ack,就会发送rst断开连接,那么服务端又要去断开已经建立好的连接,浪费资源。

    如果是三次握手,那么服务端不会直接进入established。

  • 同步序列号:初始化序列号是最重要的,所以客户端发送初始序列号x(第一次握手),客户端需要得知服务端已经收到并且服务端发送初始序列号y(第二次握手),服务端需要得知客户端已经收到(第三次握手)

tcp keepalive

keepalive是TCP保鲜定时器,链接空闲时的心跳机制。

当超过一段时间之后,TCP自动发送一个数据为空的报文给对方,如果对方回应了这个报文,说明对方还在线,链接可以继续保持,如果对方没有报文返回,并且重试了多次之后则认为链接丢失,没有必要保持链接。

tcp四次挥手

客户端主动关闭连接 —— TCP 四次挥手

  • 客户端发送:FIN
  • 服务端发送:ACK
  • 服务端发送:FIN
  • 客户端发送:ACK
  • 客户端进入TIME_WAIT,等待2MSL(报文最大生存时间

其中,客户端一直收不到第三次握手FIN的话,客户端有两种情况:

  • 对于客户端调用shutdown()的情况,只关闭发送数据不关闭接收数据,因此客户端死等
  • 对于客户端调用close()的情况,同时关闭发送和接收数据,长时间收不到FIN就会主动close

服务端一直收不到第四次握手ACK的话(在这之前处于CLOSED_WAIT,并且服务端调用close(),发送了FIN),就会主动close。

tcp四次挥手客户端为什么要TIME_WAIT

原因:

  • 等待历史连接的数据都已经在网络中自然消亡:如果没有TIME_WAIT,假设此时客户端建立新的连接,并收到了上个连接中延迟到达的报文,并且序列号恰好在客户端的滑动窗口内,那么则接收到了错误的数据。
  • 保证服务端能正确关闭:等待足够的时间让ACK发到对面,如果由于网络原因服务端收不到的话就会重发FIN,客户端收到后重置计时器为2MSL,重传ACK。如果没用TIME_WAIT,客户端收到重传FIN的时候就会回一个RST,虽然服务端也能关闭,但是是将其解释为错误,可能会使用户迷惑。

滑动窗口

滑动窗口用于提高发送数据的速率以及流量控制,每个窗口的单位为1个MSS大小的数据(一个 TCP 报文的最大长度,为了避免超过MTU造成分片,因为丢失一个分片就得重传整个tcp报文)

滑动窗口就是一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。这样一来,就不用发一个数据就等一个ack,可以把窗口的数据连续发送了。

累计确认:ack=n表示序号为n之前的报文都收到了,就算之前的ack都丢失也没关系。

窗口大小:窗口的大小由接收方的窗口大小来决定,由接收方告诉自己还有多少缓冲区可以接收数据,即发送端窗口不能大于接收端窗口

tcp Nagle发送算法

解决发送数据量太小,头部占比很大,性价比很低(即糊涂窗口综合症)。伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
if 有数据要发送 {
    if 可用窗口大小 >= MSS and 可发送的数据 >= MSS {
    	立刻发送MSS大小的数据
    } else {
        if 有未确认的数据 {
            将数据放入缓存等待接收ACK
        } else {
            立刻发送数据
        }
    }
}

根据代码,为了避免糊涂窗口综合症,需要:接收方「小窗口直接告诉发送方窗口为0」+ 发送方开启 Nagle 算法

拥塞控制

为了有了流量控制后,还需要拥塞控制?只需要考虑最极端的情况,如果发送方和接收方的传输和接收能力都是无限的,那么瓶颈就出现在网络中,如果无限制地发送,网络只会越来越拥塞,因此需要拥塞控制。

拥塞控制也基于滑动窗口,并加入「拥塞窗口」的概念,因此发送方窗口=min(接收方窗口,拥塞窗口)

拥塞窗口如何增长:

  • 慢启动:每收到一个ACK,拥塞窗口+1。拥塞窗口初始为1,第一次收到ACK,1+1=2,发送两个包,收到两个ACK,2+2=4, 8, 16…慢启动每轮发送是指数增长的。
  • 拥塞避免:当慢启动超过阈值,每收到一个ACK,拥塞窗口+1/cwnd,总的来看就是每轮发送才+1,而不是每收到一个ACK+1.拥塞避免每轮发送是线性增长的。

拥塞窗口如何收缩(发生拥塞后):

  • 拥塞发生:发生超时重传的时候,慢启动阈值设为cwnd/2,cwnd设为1
  • 快速回复:发生快速重传的时候,慢启动阈值设为cwnd/2,cwnd设为慢启动阈值

tcp粘包解决

  • 特殊字符作为消息结束符
  • 自定义消息结构,比如在头部定义一个消息长度

tcp已经处于established服务端收到新的SYN

服务端会回复属于它的连接的ack,这样客户端发现不是自己想要的ack,就会回一个rst,然后服务端就会释放这个连接

tcp和udp

udp没有重传、不保证包的到达顺序、没有流量控制、拥塞控制。只保证首部+数据的校验

http和https

http1.1性能:

  • 长连接:避免每次都建立tcp连接
  • 管道传输:客户端可以并行发送
  • 响应队头阻塞(缺点):响应方必须按顺序处理并返回一个请求再处理下一个请求

http不安全在于会出现:

  • 窃听:明文传输
  • 伪装:不验证身份
  • 篡改:报文不校验

httpS解决http的不安全:

  • 数据加密:防止窃听
  • 证书:防止伪装
  • 数据校验:防止篡改

tls握手协议(RSA)

  1. ClientHello:客户端通过发送”client hello”消息向服务器发起握手请求,该消息包含了客户端所支持的 TLS 版本、密码组合以供服务器进行选择、还有一个”client random”随机字符串
  2. ServerHello:服务器发送”server hello”消息对客户端进行回应,该消息包含了数字证书、服务器选择的密码组合、”server random”随机字符串
  3. 验证:客户端对服务器发来的证书进行验证(检查数字签名、证书链、证书有效期、证书撤回状态),确保对方的合法身份,并取出证书中的公钥,然后客户端发送公钥加密的另一个随机字符串”premaster secret (预主密钥)”
  4. 双方生成对称加密密钥:此时双方都拥有了上述的三个随机数,双方用这三个随机数生成共享密钥KEY
  5. 双方就绪:双方用KEY加密「finish信号」并发送给对方
  6. 握手完成

其中第3步中的验证证书,数字签名是对证书进行做摘要,并用CA的私钥对摘要加密得到的,数字签名目的是保证证书的没被篡改。

验证证书实际上是验证证书链

img

  1. 客户端收到服务端证书(比如baidu.com),发现不是根证书(根证书是预先加载到操作系统/浏览器,一定受信任的),则查看证书颁发机构是图中的「中间证书」,然后向CA请求该证书
  2. 请求到「中间证书」后,发现不是根证书,则查看证书颁发机构是图中的「根证书」,如果该根证书是提前安装到操作系统受信任的,那么使用该根证书的公钥验证中间证书,如果验证通过,那么中间证书就是可信的(没被篡改)
  3. 由于此时中间证书可信了,那么使用中间证书的公钥验证服务器证书,如果验证通过,那么服务器证书就是可信的(没被篡改)

整个握手过程,加密/摘要算法一般是:

  • 非对称加密:RSA等
  • 对称加密:AES、3DES等
  • 摘要:SHA-256、SHA-1、MD5等

tls记录协议

tls握手协议用于连接建立(主要是生成密钥)。

tls记录协议用于后续通信,主要负责消息(HTTP 数据)的压缩、加密、解密认证。

img

  • http明文数据分割为消息片段,然后对片段压缩
  • 计算压缩片段的MAC值(摘要),保证消息不被篡改。并加上片段的编码,防止重放攻击
  • 最后加上数据的元信息

http1.1

相对于http1.0:

  • 长连接:解决每次都建立tcp连接
  • 管道传输:解决发送方队头阻塞

http2

相对于http1.1:

  • 头部压缩:以kv形式缓存头部在服务端,客户端只需要发送对应索引号
  • 二进制格式:头部和数据都用二进制格式
  • Stream:不同stream可以并发传输,用stream ID保持消息的顺序
  • 服务器主动推送:比如渲染页面需要html+css,只需要一次请求,返回html,并且还能主动推送css

http3(QUIC)

相比http2,虽然http2使用了stream解决了http1的队头阻塞,但依然存在tcp层面的队头阻塞,比如:stream2 stream3已经完全到达,但是stream1还未到达,tcp层认为stream2 stream3还不能被应用层接收,因此导致队头阻塞

http3使用了udp,在应用层解决tcp存在的问题:

  • 无队头阻塞:依然使用stream,但stream由多个udp包组成,某个stream的udp包丢失不会影响其他stream的接收,解决了队头阻塞
  • 更快的连接建立:QUIC包含了tls,因此QUIC握手可以包含tls握手所需的信息,减少了建立连接的通信次数
  • 连接迁移:tcp连接基于四元组,而QUIC连接基于连接id,服务端和客户端各自选择一组id标识自己,就算网络IP发生变化(比如wifi变成4g),但由于是同一个设备,只要设备支持quic,保存了id和tls密钥等,继续用他们来通信即可,做到无缝切换。

http和rpc

  • 服务发现:http的服务发现比如dns(根据域名发现ip)。rpc的服务发现比如etcd(根据服务找到ip和端口),区别不大
  • 底层连接:http的keepalive可以复用tcp连接。rpc也可以基于tcp,并使用连接池复用tcp连接
  • 传输内容:rpc可以自定义消息结构,比如protobuf,自定义除冗余字段、压缩字段等,效率更高

综上,rpc更多用于内部服务之间的通信,对外则是使用统一标准的比如http(B/S架构)

http和websocket

由于http设计为一问一答的协议,ws出现之前基于http的「伪」服务端推送:

  • 轮询:客户端定时请求,服务端查询数据后马上返回
  • 长轮询:客户端请求,服务端挂起这个请求,直到检测到有新数据后再返回,要求服务端有挂起多个请求的能力

ws:

1
2
3
4
5
6
7
8
9
# 请求
Connection: Upgrade
Upgrade: WebSocket
Sec-WebSocket-Key: T2a6wZlAwhgQNqruZ2YUyg==\r\n
# 响应
HTTP/1.1 101 Switching Protocols\r\n
Sec-WebSocket-Accept: iBJKv/ALIW2DobfoA4dmr3JHBCY=\r\n
Upgrade: WebSocket\r\n
Connection: Upgrade\r\n

响应状态码101 switching protocols

ws报文结构,使用「头+数据」解决tcp粘包:

图片

CDN(用户点击URL时发生的步骤)

image-20241122004728841

  1. 本地DNS系统解析,DNS系统会最终将域名的解析权交给CNAME指向的CDN专用DNS服务器
  2. CDN的DNS服务器将CDN的全局负载均衡设备IP地址返回用户
  3. 用户向CDN的全局负载均衡设备发起内容URL访问请求,CDN全局负载均衡设备根据用户IP地址,以及用户请求的内容URL,选择一台用户所属区域的区域负载均衡设备,告诉用户向这台设备发起请求
  4. 域负载均衡设备会为用户选择一台合适的缓存服务器提供服务,返回其ID地址给用户
  5. 用户向缓存服务器发起请求,如果缓存服务器没有用户要的资源,那么这台服务器就要向它的上一级缓存服务器请求内容,直至追溯到网站的源服务器将内容拉到本地缓存,最终返回资源给用户

访问url发生什么

https://xiaolincoding.com/network/1_base/what_happen_url.html

  1. DNS:查询DNS缓存/DNS服务器得到域名对应的IP
  2. TCP:与ip:port建立tcp连接,分割http报文不超过MSS
  3. IP:根据路由表确定下一跳的ip地址(网关),如果ip地址为空(网关为空)说明已经到达了终点
  4. MAC:mac地址才能唯一标识一个设备,因此使用arp协议广播获取拥有该下一跳ip的设备mac地址,然后加上mac头、起始分界符、校验和,发送到交换机
  5. 交换机:交换机根据目标mac查询mac地址表,将信号发送到对应的端口,如果找不到的话就广播到所有端口,接收信号的设备会检查目的mac是否自己,如果不是则丢弃,否则响应一个mac地址给交换机,交换机将记录端口与该mac地址的映射
本文由作者按照 CC BY 4.0 进行授权