计算机网络常见面试整理

80 阅读14分钟

长连接和短连接是什么?为什么是必要的?

如何避免队头阻塞?

根据此图谈谈网络优化

网络优化

说说抓包? ⭐

[原创]某直播APP逆向TCP协议分析 Android抓包总结 扯一扯HTTPS单向认证、双向认证、抓包原理、反抓包策略

心跳包作用、格式的定义及处理

作用是保持长连接避免不断创建tcp连接

报文头的定义,就是你发送数据的时候需要先发送报文头,报文里面能解析出你将要发送的数据长度 你发送数据包的格式,是json的还是其他序列化的方式

Socket连接池干嘛的

Socket连接池,就是维护着一定数量Socket长连接的集合。它能自动检测Socket长连接的有效性,剔除无效的连接,补充连接池的长连接的数量

http的各个版本缺陷

三次握手在高延迟的场景下影响较明显,慢启动则对大量小文件请求影响较大(没有达到最大窗口请求就被终止) HTTP/1 有连接无法复用、队头阻塞、协议开销大和安全因素等多个缺陷 HTTP/2 通过多路复用、二进制流与 Header 压缩等技术,极大地提高了性能,但是还是存在一些问题 HTTP/3 抛弃 TCP 协议,以全新的视角重新设计 HTTP。其底层支撑是 QUIC 协议,该协议基于 UDP,有 UDP 特有的优势,同时它又取了 TCP 中的精华,实现了即快又可靠的协议

http rfc规范

HTTP/2 由两个规范组成: Hypertext Transfer Protocol version 2 - RFC7540 HPACK - Header Compression for HTTP/2 - RFC7541

Http加密过程是怎样的

TLS需要2个RTT,TLS 1.3 需要1个RTT

完整三次握手https验证

自己书写的https连接全过程

另外这篇文章有IP和TCP包头格式

https连接全过程

让第三方CA来确保server公钥正确

CA签名 1)server向CA提供基本信息、公钥等,CA用CA私钥对server公钥和信息进行加密形成数字证书(防止server被伪造)。 2)client向server发出https请求和随机数c,server返回数字证书和随机数s。 3)client收到数字证书,用CA公钥对数字证书进行解密,得到server的公钥。

得到server公钥后,client可以用server公钥解密server签名得到摘要。 client也把原文hash一遍得到的摘要比对,确认内容没有经过更改

生成会话密钥

client用server公钥加密client生成的随机数premaster secret,发送给server。 server收到加密的随机数premaster secret,用server私钥解密随机数,然后和随机数c、随机数s一起生成会话密钥。此时client也拥有三个随机数生成相同的会话密钥(Session Key)。双方用会话密钥进行加密通信(防止内容被窃听进行加密)

用会话密钥(Session Key)进行加密通信是对称加密的。如DES,AES等对称加密,只有一把钥匙 而获得会话密钥的过程是不对称加密的。最常见的非对称加密是RSA,有公钥和私钥,公钥加密只有私钥解密,私钥加密只有对应公钥解密

如何防止数据被篡改

  1. 数字签名是server对通信内容进行hash,得到信息摘要。
  2. 然后用server私钥对摘要进行加密得到数字签名。server把明文连同数字签名一起发送给client
  3. client用server公钥对数字签名解密得到信息摘要,然后用相同的hash对数据进行计算得到信息摘要,比对两个信息摘要是否一致。

为什么客户端在TIME_WAIT状态时还需要等2MSL后才能返回到CLOSED状态?

TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。 四次挥手 报文最大生存时间(Maximum Segment Lifetime)

  • 1 个 MSL 确保四次挥手中主动关闭方最后的 ACK 报文最终能达到对端
  • 1 个 MSL 确保对端没有收到 ACK 后重传的 FIN 报文可以到达

虽然双方都同意关闭连接了,但网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到。 因此对方处于LAST_ACK状态下的Socket可能会因为超时未收到ACK报文,而重发FIN报文

TIME_WAIT状态所导致的服务器问题

一台机器上端口号数量的上限是65536个,TCP服务器最大并发连接数是多少?

  • 如果在同一台机器上进行压力测试模拟上万的客户请求,并且循环与服务端进行短连接通信,那么这台机器将产生4000个左右的TIME_WAIT Socket,后续的短连接就会产生address already in use : connect的异常
  • 如果使用Nginx作为方向代理也需要考虑TIME_WAIT状态,发现系统存在大量TIME_WAIT状态的连接,通过调整内核参数解决。

tcp为什么是四次挥手而不是三次?

因为服务端收到客户端发送的FIN后,可能还有需要发送给客户端的内容,所以只先回一个ACK 如果服务端的ACK延迟到服务端发完数据后ACK和FIN一起发,会导致超过一个MSL后客户端会以为第一个客户端发起的FIN丢了,不断地重发FIN

tcp为什么发起握手连接是三次,而不是两次?

根本原因: 无法确认客户端的接收能力。

三次握手的目的是确认双方发送和接收的能力 如果第一次客户端发起的SYN A丢包,等到客户端和服务器重新建立且正常关闭了连接后,这个丢包了的SYN A被服务器收到了,由于只需要两次,服务器会建立新连接,而客户端不知道

tcp三次握手过程中可以携带数据么?

第三次握手的时候,可以携带。前两次握手不能携带数据。

如果前两次握手能够携带数据,那么一旦有人想攻击服务器,那么他只需要在第一次握手中的 SYN 报文中放大量数据,那么服务器势必会消耗更多的时间和内存空间去处理这些数据,增大了服务器被攻击的风险。

SYN Flood 攻击是什么,如何解决?

典型的 DoS/DDoS 攻击

  • 原因:客户端短时间内伪造大量不存在的IP,疯狂向服务端发送第一次握手SYN

    1. 服务端处理到大量的SYN,需要返回第二次握手的ACK和SYN,半连接队列很快被占满无法接受正常的请求

    半连接队列:服务端收到以后回复第二次握手的ACK和SYN,状态由LISTEN变为SYN_RCVD,并连接挂入SYN半连接队列

    1. 由于是不存在的IP,服务端发送第二次握手的ACK和SYN后,长期收不到第三次握手,会导致服务端不断重发第二次握手
  • 解决方法:

  1. 增加服务端SYN半连接队列容量
  2. 减少服务端重试第二次握手的次数,避免短期遇到大量的超时重发
  3. 利用SYN Cookie验证客户端存在:服务端在接收到第一次握手的SYN时不立即分配资源,根据这个SYN计算出一个Cookie,连同第二次握手回复给客户端,客户端在回复第三次握手时带上Cookie,服务端在验证Cookie合法后才会分配连接资源

介绍一下 TCP 报文头部的字段

摘自TCP 协议面试灵魂 12 问 ! TCP报文Header

  1. 两个端口,各2字节:传输层TCP 报文怎么没有源 IP 和目标 IP 呢?这是因为在网络层 IP 层就已经处理了 IP。
  2. 序列号(Sequence number)本报文段第一个字节的序号。4字节 - [0,2^32-1],可以循环到0
    • 保证数据包按正确的顺序组装。
    • 在 SYN 报文中交换彼此的初始序列号
    • ISN(Initial Sequence Number)三次握手时的初始序列号。ISN动态增长每4ms加一,溢出则从0重启,使得无法被攻击者猜测ISN。

  3. ACK(Acknowledgment number),告知对方下一个期望收到的序列号,小于ACK的所有字节都已经全部收到
  4. SYN/ACK/FIN
  5. RST:Reset,强制断开连接
  6. PSH:Push,对方这些数据包收到后应该马上交给上层的应用,不能缓存
  7. 窗口大小,2字节(16位)但不够用。可以有窗口缩放作为比例因子,将窗口值扩大为2^n
  8. 校验和(check sum) 2字节
  9. 可选部分:
    • TimeStamp: TCP 时间戳。计算往返时延 RTT(Round-Trip Time),防止序列号的回绕问题
      • kind = 8, length = 10, info 有两部分构成: timestamptimestamp echo,各占 4 个字节。
      • 计算RTT时遇到的问题
      • 计算RTT时遇到的问题:是以第一次报文为开始还是重发的为开始?所以有timestamptimestamp echo分别记录本报文时间,和透传上一条对方发的报文的时间。
        1. a 向 b 发送的时候,timestamp 中存放的内容就是 a 主机发送时的内核时刻 ta1。
        2. b 向 a 回复 s2 报文的时候,timestamp 中存放的是 b 主机的时刻 tb, timestamp echo字段为从 s1 报文中解析出来的 ta1。
        3. a 收到 b 的 s2 报文之后,此时 a 主机的内核时刻是 ta2, 而在 s2 报文中的 timestamp echo 选项中可以得到 ta1, 也就是 s2 对应的报文最初的发送时刻。然后直接采用 ta2 - ta1 就得到了 RTT 的值。
      • 防止序列号回绕问题:网络中如果存在Seq相同报文,需要用时间戳做区分。
      • 由于Seq溢出后会返回0,此时溢出前的Seq序列号,在溢出后从0重启后又到了相同Seq序列号。

    • MSS: 指的是 TCP 允许的从对方接收的最大报文段。
    • SACK: 选择确认选项。
    • Window Scale:窗口缩放选项。

TCP 的超时重传时间是如何计算的?

重传间隔也叫做超时重传时间(Retransmission TimeOut, 简称RTO),根据RTT密切变化 每次传输会根据RTT更新SRTT(Smoothed round trip time,即平滑往返时间),然后利用|RTT - SRTT|再和SRTT占不同权重求得

TCP Fast Open (TFO) 是什么?

优化后的 TCP 握手流程,快速打开一个TCP连接。

  • 在首次三次握手的后续握手,服务端验证Cookie后可以直接返回 HTTP 响应.充分利用了1 个RTT(Round-Trip Time,往返时延)的时间提前进行数据传输
  • 可以通过上文提到的Cookie达成,后续的TCP请求客户端直接发送缓存的CookieSYNHTTP请求。
  • 服务器可以把第二次握手改为,仅验证了Cookie是否有效,就可以返回HTTP响应。客户端再第三次握手ACK TCP Fast Open

注意: 客户端最后握手的 ACK 不一定要等到服务端的 HTTP 响应到达才发送,两个过程没有任何关系。

TCP 的流量控制?

接收端接受能力低,对接收端来说: 滑动窗口

SND 即send, WND 即window, UNA 即unacknowledged, 表示未被确认(已经发送未被确认),NXT 即next, 表示下一个发送的位置(未发送可以发送) 上图是发送方的发送窗口。 由于接收端处理能力低:大量负载来不及处理的情况,只能处理一部分,剩下的一部分留在缓冲队列里,这样下次能接受的窗口就小了。

  • 需要发送端少发送,接收端的ACK报文首部会带上能被接受窗口大小。
  • 此时发送端收到ACK,UNA往右移动,同时发送窗口会整个缩小

TCP 的拥塞控制?

发送端发送能力低,容易丢包,对发送端来说: TCP 每条连接都需要维护两个核心状态:

  • 拥塞窗口(Congestion Windowcwnd
  • 接收窗口(rwnd)是接收端给的限制;拥塞窗口(cwnd)是发送端的限制

  • 发送窗口大小 = min(rwnd, cwnd)

  • 慢启动阈值(Slow Start Thresholdssthresh) 涉及到的算法有这几个:
  • 慢启动(三次握手后每一个RTT窗口大小翻倍)
  • 拥塞避免(慢启动阈值后开始转为缓慢线性增长,避免堵塞)
  • 快速重传和快速恢复(如果丢包,发现多个重复ACK后不用等待一个RTO,直接进行重传)
    1. 快速重传:接收端选择性重传(SACKSelective Acknowledgment)丢掉的部分
    2. 快速恢复:(防止加重网络堵塞)
      • cwnd和拥塞阈值同时降低为 cwnd 的一半
      • cwnd变为线性增长

延迟发送和延迟接收

如果延迟发送和延迟接收同时一起使用,可能会造成更大的延迟,产生性能问题

如何避免小包的频繁发送? Nagle 算法

  • 当第一次发送数据时不用等待,就算是 1byte 的小包也立即发送
  • 后面发送满足下面条件之一就可以发了:
    1. 数据包大小达到最大段大小(Max Segment Size, 即 MSS)
    2. 之前所有包的 ACK 都已接收到

延迟确认

短时间内连续收到包,可以合并ACK,除非需要立即回复ACK的场景:

  1. 接收到了大于一个 frame 的报文,且需要调整窗口大小
  2. TCP 处于 quickack 模式(通过tcp_in_quickack_mode设置)
  3. 发现了乱序包

HTTPDNS

Android Webview + HttpDns最佳实践 DNS优化 - 百度的HTTPDNS大部分标准DNS都是基于UDP与DNS服务器交互的,HTTPDNS则是利用HTTP协议与DNS服务器交互

摘要一则:对于客户端,在PC上要学会使用tcpdump和Wireshark等工具,适当使用Fiddler和Charles等工具,很多时候电脑和手机的网络环境不见得一致,所以要在手机上使用iNetTools,Ping&DNS或终端工具。学会使用工具后,要学着创造不同的网络环境,有很多工具能帮助你完成这点,比如苹果的Network Link Conditioner,FaceBook的ATC(Augmented Traffic Control)等

负载均衡

nginx负载均衡调度算法默认是 round robin,也就是轮询调度算法

    j = i;
    do
    {
        j = (j + 1) mod n;
        if (W(Sj) > 0){
            i = j;
            return Si;
        }
    } while (j != i);
    return NULL;

压缩算法用的是什么?

客户端发送Accept-Encoding,服务端回应

Accept-Encoding: gzip, compress, br
Accept-Encoding: br;q=1.0, gzip;q=0.8, *;q=0.1

(除了 identity 之外,都是无损压缩)
gzip:表明实体采用 GNU zip 编码。(gzip 对媒体文件的压缩效果相对较差)
compress:表明实体采用 Unix 的文件压缩程序。
deflate:表明使用是用 zlib 的格式压缩的。
br:表明实体使用 Brotli 算法的压缩格式。
identity:表明没有对实体进行编码,为默认值。

具体说明见Accept-Encoding