Java八股文——网络协议

  1. HTTP协议

    • 一次http请求的过程
      • 用户输入url,浏览器本地解析url,如果在host文件中存有对应ip则访问对应ip,否则将域名交给DNS服务器,DNS服务器返回对应IP地址,应用层向ip地址发送http请求,然后是传输层TCP的三次握手确认连接,第一次是客户端向服务器发送syn,第二次是服务器发送syn和ack到客户端,第三次是客户端发送syn与ack确认,此时TCP握手成功,然后到网络层,通过ARP协议,使用ip解析出MAC地址,然后通过MAC地址在数据链路层传输数据,服务器接收到数据包后,由web服务器处理该请求,查找客户端请求的资源,并返回响应报文。
      • 断开连接需要TCP4次挥手,任意一方可开始,机器1发送fin=1到机器2表示数据传输完毕,断开请求,机器2返回ack=1确认收到关闭请求,等待机器2的信息传送完毕后,机器2发送fin=1到机器1,此时机器2会有定时器等待机器1返回ack=1,如果没有返回,会重发fin=1,机器1返回ack=1确认关闭请求。
    • http与https的区别
      • https需要去CA申请证书
      • http运行在TCP之上,所有传输内容都是明文,HTTPS运行在SSL/TLS之上,SSL/TLS运行在TCP之上,所有传输内容都是加密的
      • http默认端口80,HTTPS默认端口443
    • GET和POST区别
      • GET方法是从服务器获取资源
      • POST方法是向指定URI提交数据,数据放在body中
      • GET请求的URL有长度限制,而POST请求数据会放在消息体中,没有长度限制
      • GET请求会被浏览器主动cache,而post不会
      • GET请求在发送过程中产生一个TCP数据包,POST请求会产生两个数据包。对于GET请求,浏览器会将header和data一起发送,服务器返回响应,而POST请求,浏览器是先发送header,服务器响应100 continue,浏览器再发送data
  2. TCP协议

    1. TCP三次握手与四次挥手,本文第一条
    2. 如何可以绕过三次握手?
      • Linux3.7内核版本之后,提供了TCP Fast Open功能,可以减少TCP连接建立的时延
        1. 客户端发送 SYN 报文,该报文包含 Fast Open 选项,且该选项的 Cookie 为空,这表明客户端请求 Fast Open Cookie;
        2. 支持 TCP Fast Open 的服务器生成 Cookie,并将其置于 SYN-ACK 数据包中的 Fast Open 选项以发回客户端;

        3. 客户端收到 SYN-ACK 后,本地缓存 Fast Open 选项中的 Cookie。

      • 所以第一次发起请求时,还是需要正常的三次握手,之后如果再次向服务器建立连接的过程:
        1. 客户端发送 SYN 报文,该报文包含「数据」(对于非 TFO 的普通 TCP 握手过程,SYN 报文中不包含「数据」)以及此前记录的 Cookie;
        2. 支持 TCP Fast Open 的服务器会对收到 Cookie 进行校验:如果 Cookie 有效,服务器将在 SYN-ACK 报文中对 SYN 和「数据」进行确认,服务器随后将「数据」递送至相应的应用程序;如果 Cookie 无效,服务器将丢弃 SYN 报文中包含的「数据」,且其随后发出的 SYN-ACK 报文将只确认 SYN 的对应序列号;
        3. 如果服务器接受了 SYN 报文中的「数据」,服务器可在握手完成之前发送「数据」,这就减少了握手带来的 1 个 RTT 的时间消耗
        4. 客户端将发送 ACK 确认服务器发回的 SYN 以及「数据」,但如果客户端在初始的 SYN 报文中发送的「数据」没有被确认,则客户端将重新发送「数据」;
        5. 此后的 TCP 连接的数据传输过程和非 TFO 的正常情况一致
      • 之后发起 HTTP GET 请求的时候,可以绕过三次握手,这就减少了握手带来的 1 个 RTT 的时间消耗
      •  /proc/sys/net/ipv4/tcp_fastopen
        • 0 关闭
        • 1 作为客户端使用 Fast Open 功能
        • 2 作为服务端使用 Fast Open 功能
        • 3 无论作为客户端还是服务器,都可以使用 Fast Open 功能
    3. UDP与TCP的区别
    4. TCP半连接队列和全连接队列
      1. TCP三次握手的时候,Linux内核会维护两个队列:
        • 半连接队列,也称SYN队列
        • 全连接队列,也称accept队列
      2. 服务端收到客户端发起的SYN请求后,内核会把该连接存储到半连接队列,并向客户端返回ACK,服务端收到第三次握手的ACK后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到accept队列,等待进程调用accept函数时把连接取出来.
      3. 不管是半连接还是全连接队列,都有最大长度限制,超过限制时,内核会直接丢弃,或返回RST包
      4. 全连接队列:
        • 查看TCP全连接队列的大小:服务端使用ss命令
          • ss
            • -l  显示正在监听(listening)的socket
            • -n 不解析服务名称
            • -t  只显示tcp socket
          • 在LISTEN状态时(ss -lnt),Recv-Q/Send-Q表示的含义如下:
            • Recv-Q:当前全连接队列的大小
            • Send-Q:当前全连接最大队列长度
          • 非LISTEN状态时(ss -nt)
            • Recv-Q:已收到但未被应用进程读取的字节数
            • Send-Q:已发送但未收到确认的字节数
        • 当服务端并发处理大量请求时,如果TCP全连接队列过小,就容易溢出。发生了全连接队列溢出时,后续的请求就会被丢弃,出现服务端请求数量上不去的现象
        • 丢弃连接是Linux的默认行为,我们可以选择向客户端发送RST复位报文,告诉客户端连接建立失败。
          /proc/sys/net/ipv4/tcp_abort_on_overflow
          这个文件默认为0
          • 0:表示如果全连接队列满了,就扔掉连接
          • 1:表示如果全连接队列满了,就发送一个reset包给client,表示废除握手过程和连接
        • 通常情况下,tcp_abort_on_overflow应该设置为0,更有利于应对突发流量
        • 如何增大TCP全连接队列?
          • TCP全连接队列最大值取决于somaxconn和backlog的最小值
            • somaxconn 是 Linux 内核的参数,默认值是 128,可以通过 /proc/sys/net/core/somaxconn 来设置其值;
            • backlog 是 listen(int sockfd, int backlog) 函数中的 backlog 大小,Nginx 默认值是 511,可以通过修改配置文件设置其长度;
            • nginx中backlog设置成5000的demo:
            • 设置完后需要重启 

      5. 半连接队列:
        • 服务端没有命令单独查看半连接队列,但可以抓住TCP半连接队列的特点,就是服务端处于SYN_RECV状态的TCP连接,就是在半连接队列中
        • 因此,计算TCP半连接队列长度的命令如下:模拟半连接队列的溢出场景,实际上就是一直对服务端发送TCP SYN包,但是不回第三次握手的ACK,这样就会有大量的处于SYN_RECV状态的TCP连接,其实这就是所谓的SYN洪泛,SYN攻击,DDos攻击
          • netstat -natp | grep SYN_RECV | wc -l
        • 半连接队列的溢出处理是怎么做的?
          • 如果半连接队列满了,并且没有开启 tcp_syncookies,则会丢弃;
          • 若全连接队列满了,且没有重传 SYN+ACK 包的连接请求多于 1 个,则会丢弃
          • 如果没有开启 tcp_syncookies,并且 max_syn_backlog 减去 当前半连接队列长度小于 (max_syn_backlog >> 2),则会丢弃;
        • 半连接队列最大值
          • 当 max_syn_backlog > min(somaxconn, backlog) 时, 半连接队列最大值 max_qlen_log = min(somaxconn, backlog) * 2;
          • 当 max_syn_backlog < min(somaxconn, backlog) 时, 半连接队列最大值 max_qlen_log = max_syn_backlog * 2;
        • syncookies
          • 如果 SYN 半连接队列已满,开启 syncookies 功能就可以在不使用 SYN 半连接队列的情况下成功建立连接
          • syncookies 是这么做的:服务器根据当前状态计算出一个值,放在己方发出的 SYN+ACK 报文中发出,当客户端返回 ACK 报文时,取出该值验证,如果合法,就认为连接建立成功,如下图所示。
          • syncookies 参数主要有以下三个值:

            • 0 值,表示关闭该功能;

            • 1 值,表示仅当 SYN 半连接队列放不下时,再启用它;

            • 2 值,表示无条件开启功能;

          • 因此,应对SYN攻击时,只需要设置为1即可
        • 防御SYN攻击的方法:
          • 增大半连接队列
            • 通过源码得知,如果想增大半连接队列,不能单纯增加tcp_max_syn_backlog,还需一同增大somaxconn和backlog,也就是增大全连接队列,增大tcp_max_syn_backlog和somaxconn的方法就是修改Linux内核参数:路径分别为:/proc/sys/net/ipv4/tcp_max_syn_backlog
              /proc/sys/net/core/somaxconn
          • 开启tcp_syncookies功能
            • 路径为/proc/sys/net/ipv4/tcp_syncookies
          • 减少SYN+ACK重传次数
            • 当收到SYN攻击时,就会有大量处于SYN_RECV的TCP连接,服务端此时会重传SYN+ACK,当重传次数达到上限后,就会断开连接,因此,我们可以通过减少SYN+ACK的重传次数,加快连接断开来抵御攻击
            • /proc/sys/net/ipv4/tcp_synack_retries

参考博文:https://mp.weixin.qq.com/s/tRXlq1hErqKQLMMLcxoXvg

原文地址:https://www.cnblogs.com/gtblog/p/13657643.html