吊打面试官系列 计算机网络面试题之TCP连接、可靠性、重传机制和多路复用等30问万字长文(二)

文档大纲

TCP可靠性机制有哪些?

1 TCP如何保证消息顺序?

  • TCP协议会给每个数据包分配一个序列号

  • 发送方在发送消息后,会等待接收方对该报文进行确认,如果一段时间内没有收到确认,那么会触发超时重传,发送方会重传该报文

  • 接收方接收到数据后,会先进行缓存,如果数据的顺序是正确的,那么就上传到应用层使用,否则暂时保存在缓冲区

2 TCP的重传机制是怎么实现的?

2.1 超时重传机制?

  • 在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据

  • 数据包丢失或者确认应答丢失就会触发

  • 数据包在网络中的往返时间(RTT)

  • 超时重传时间(RTO)

    • 很显然,RTO应该设置为略大于RTT,以便获得最高的性能

    • 但是由于网络情况不断变化,RTT时间是一个动态变化的值

    • Linux系统下是采用某种公式来计算对应的RTO时间(公式的参数是通过大量的实验得到的)

    • 如果超时重发的数据,再次超时的时候,又需要重传的时候,TCP 的策略是超时间隔加倍

    • 也就是每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。

2.2 快速重传机制?

  • 发送方收到了三次同样的 ACK 确认报文,于是就会触发快速重发机制

  • 但是在重传的时候无法确认是重传之前一个,还是所有

2.3 SACK 选项的作用?

  • 在 TCP 头部「选项」字段里加一个 SACK 的东西

  • 将缓存的地图发送给发送方

    • 告诉发送方我收到了哪些数据
  • 只重传丢失的数据

  • Linux 通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开)

  • 注意:启动sack后,接受方接收到带sack的报文也认为收到重复ack

    • 开启该功能需要双方支持并协商成功

  • D-SACK

  • 使用了 SACK 来告诉「发送方」有哪些数据被重复接收了

  • 使用D-SACK的好处

    • 可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了;

    • 可以知道是不是「发送方」的数据包被网络延迟了;

    • 可以知道网络中是不是把「发送方」的数据包给复制了;

  • 在 Linux 下可以通过 net.ipv4.tcp_dsack 参数开启/关闭这个功能(Linux 2.4 后默认打开)。

3 滑动窗口的作用是什么?

  • 没有滑动窗口的话

    • 仅按数据包进行确认应答,数据包的往返时间越长,通信的效率就越低
  • 使用滑窗

    • 窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值

    • 窗口内的数据可以不用等待接收方的应答,先行发送出去

    • 如果收到了应答,就可以将数据从窗口内清除

  • 利用ACK700就能确认前面的数据发送无误,这就是累计确认或者累计应答

3.1 窗口大小由哪一方决定?

  • TCP 头里有一个字段叫 Window,也就是窗口大小。

  • 这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。

  • 所以,一般是否接收方决定。

  • 发送方发送的数据大小不能超过接收方的窗口大小,否则接收方就无法正常接收到数据。

3.2 程序是如何表示发送方的四个部分的呢?

  • 一个指针指向已发送且收到了确认的第一个字节的序列号,绝对指针

  • 一个指针指向已发送但未收到了确认的第一个字节的序列号,绝对指针

  • 一个指针指向未发送,且超出窗口的第一个字节序列号,相对指针,由第一个指针和窗口大小得到

3.3 窗口关闭问题如何解决?

  • 如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭。

  • 死锁问题见下图

  • 解决方法:

    • TCP 为每个连接设有一个持续定时器,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器

    • 如果持续计时器超时,就会发送窗口探测 ( Window probe ) 报文,而对方在确认这个探测报文时,给出自己现在的接收窗口大小

3.4 糊涂窗口综合症如何解决?

  • 接收方太忙,导致发送方的发送窗口越来越小

  • 到最后,如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症

  • TCP + IP 头有 40 个字节,如果只传输几个字节的数据,代价很大

  • 问题形成原因:

    • 接收方可以通告一个小的窗口

    • 而发送方可以发送小数据

  • 解决方法:

    • 接收方判断窗口大小小于小于 MSS 与 12 缓存大小中的最小值时,向发送方通告窗口为 0

    • 发送方使用 Nagle 算法:要等到窗口大小 >= MSS 或是 数据大小 >= MSS;收到之前发送数据的 ack 回包

  • Nagle 算法:

    • 一个 TCP 连接上最多只能有一个未被确认的未完成的小分组,在它到达目的地前,不能发送其它分组。

    • 在上一个小分组未到达目的地前,即还未收到它的 ack 前,TCP 会收集后来的小分组。当上一个小分组的 ack 收到后,TCP 就将收集的小分组合并成一个大分组发送出去。

4 流量控制目的?

  • 发送方不能无脑的发数据给接收方,要考虑接收方处理能力

  • 本质上利用的还是滑动窗口的原理

  • 不会发送超过约定的滑动窗口大小的数据

  • TCP 通过让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制

5 TCP拥塞机制介绍一下?

  • 流量控制只能控制通信双方的流量,但是计算机网络是一个共享的环境,有可能会因为其它主机的通信而使得网络拥塞

  • 目的

    • 防止过多的数据注入到网络中,避免网络中路由器、链路过载

5.1 什么是拥塞窗口?和发送窗口有什么关系呢?

  • 拥塞窗口 cwnd是发送方维护的一个 的状态变量,它会根据网络的拥塞程度动态变化的

  • 发送窗口的值是swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值

5.2 那么怎么知道当前网络是否出现了拥塞呢?

  • 发生了超时重传,就会认为网络出现了拥塞

5.3 拥塞控制算法介绍一下

5.3.1 慢启动
  • 一开始一点一点提高发送数据包的数量

  • cwnd = 10

  • 当发送方每收到一个 ACK,就拥塞窗口 cwnd 的大小就会加 1

  • 呈指数增长

  • 慢启动门限 ssthresh:

    • 默认:65536

    • cwnd < ssthresh 时,使用慢启动算法。

    • cwnd >= ssthresh 时,就会使用「拥塞避免算法」。

  • 拥塞窗口的1代表的是可以发送一个MSS大小的数据

5.3.2 拥塞避免算法
  • 每当收到一个 ACK 时,cwnd 增加 1/cwnd

  • 将指数级增长变为了线性增长

  • 增长速度变慢,但是最终还是会进入网络拥塞

  • 此时会采用拥塞发生算法

5.3.3 拥塞发生算法(快速重传算法)
  • 超时重传

    • ssthresh 设为 cwnd/2

    • cwnd 重置为 1

    • 因为超时没有收到任何ack,所以认为网络拥塞严重

  • 快速重传

    • ssthresh = cwnd/2;

    • cwnd = cwnd/2 ,也就是设置为原来的一半;

    • 进入快速恢复算法

    • 因为还是能连续收到接收方的3个ack,说明拥塞不算太严重

5.3.4 快速恢复算法
  • 拥塞窗口 cwnd = ssthresh + 3 ( 3 的意思是确认有 3 个数据包被收到了)

  • 重传丢失的数据包

  • 如果再收到重复的 ACK,那么 cwnd 增加 1

  • 如果收到新数据的 ACK 后,设置 cwnd 为 ssthresh,接着就进入了拥塞避免算法

关闭TCP连接的方法?

  • RST 报文关闭

    • 进程异常退出,发送RST报文,不走四挥流程,暴力关闭连接
  • FIN 报文关闭

调用 close 函数 和 shutdown 函数有什么区别?

  • close 函数意味着完全断开连接

    • 既不能发送,也不能接收

    • 调用了 close 函数的一方的连接叫做「孤儿连接」

  • 把全双工的两个方向的连接都关闭了

  • shutdown函数可以通过参数控制只关闭一个方向上的连接

    • SHUT_RD(0):关闭连接的「读」这个方向,如果接收缓冲区有已接收的数据,则将会被丢弃,并且后续再收到新的数据,会对数据进行 ACK,然后悄悄地丢弃。也就是说,对端还是会接收到 ACK,在这种情况下根本不知道数据已经被丢弃了。

    • SHUT_WR(1):关闭连接的「写」这个方向,这就是常被称为「半关闭」的连接。如果发送缓冲区还有未发送的数据,将被立即发送出去,并发送一个 FIN 报文给对端。

    • SHUT_RDWR(2):相当于 SHUT_RD 和 SHUT_WR 操作各一次,关闭套接字的读和写两个方向。

TCP全连接队列和半连接队列的作用?

  • 服务端并发处理大量请求时,如果 TCP 全连接队列过小,就容易溢出。发生 TCP 全连接队溢出的时候,后续的请求就会被丢弃,这样就会出现服务端请求数量上不去的现象

1. 全连接队列满了,就只会丢弃连接吗?

tcp_abort_on_overflow 共有两个值分别是 0 和 1,其分别表示:

  • 0 :如果 accept 队列满了,那么 server 扔掉 client 发过来的 ack ;

  • 1 :如果 accept 队列满了,server 发送一个 RST 包给 client,表示废掉这个握手过程和这个连接;

2 如何增大 TCP 全连接队列呢?

  • TCP 全连接队列足最大值取决于 somaxconn 和 backlog 之间的最小值,也就是 min(somaxconn, backlog)

  • somaxconn 是 Linux 内核的参数,默认值是 128,可以通过 /proc/sys/net/core/somaxconn 来设置其值;

  • backloglisten(int sockfd, int backlog) 函数中的 backlog 大小,Nginx 默认值是 511,可以通过修改配置文件设置其长度;

  • NGINX设置

  • 服务端执行 ss 命令,查看 TCP 全连接队列使用情况:

  • 从上面的执行结果,可以发现全连接队列使用增长的很快,但是一直都没有超过最大值,所以就不会溢出,那么 netstat -s 就不会有 TCP 全连接队列溢出个数的显示:

  • 溢出示例

如何查看 TCP 的连接状态?

TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。

假设客户端有多个网卡,就会有多个 IP 地址,那 IP 头部的源地址应该选择哪个 IP 呢?

  • 此时会根据路由表来进行匹配,确定哪一个网卡作为源地址IP

  • 主要是根据网络号进行匹配

  • 如果没有匹配,则选用最后一个,表示默认网关

  • 即后续就把包发给路由器, Gateway 即是路由器的 IP 地址。

交换机如何转发包信息?

  • 交换机的各个端口是没有MAC地址的,是二层网络设备,工作在MAC层

  • 交换机的 MAC 地址表主要包含两个信息:

    • 一个是设备的 MAC 地址,

    • 另一个是该设备连接在交换机的哪个端口上。

  • 根据接收到的包上面的接收方的MAC地址进行匹配,可以得到应该转发到的端口信息。

  • 只要有对交换机发送过消息,MAC地址就会记录下来

  • 如果没有找到MAC地址,就将包转发到除了源端口之外的所有端口上

路由器是怎么工作的?

  • 基于IP设计,三层网络设备,各个端口都有MAC地址和IP地址

  • 收到包后,通过包的MAC头部判断是否给自己的,是的话就放入缓冲区,否则丢弃

  • 然后去掉MAC头部,根据IP头部的目的IP地址在路由表中进行匹配

  • 找到对应的接口信息,然后将报文转发到该接口

  • 是在没有找到就会使用最后的默认路由

  • 通过路由表进行匹配后有两种结果:

    • 如果网关是一个 IP 地址,则这个IP 地址就是我们要转发到的目标地址,还未抵达终点,还需继续需要路由器转发。

    • 如果网关为空,则 IP 头部中的接收方 IP 地址就是要转发到的目标地址,也是就终于找到 IP 包头里的目标地址了,说明已抵达终点。

  • 根据路由表得到的接口就是MAC的发送地址

  • 然后查询ARP缓存或者使用ARP广播得到目标地址的MAC地址

  • 两者组合得到MAC头部,然后继续进行传输

  • 在网络包传输的过程中,源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址,因为需要 MAC 地址在以太网内进行两个设备之间的包传输

HTTP 是什么?描述一下

  • HTTP 是超文本传输协议,也就是HyperText Transfer Protocol。

  • HTTP 是一个在计算机世界里专门在「两点」之间「传输」文字、图片、音频、视频等「超文本」数据的「约定和规范」。

  • HTTP 是超文本传输协议,是在两台计算机之间传送文字、图片、视频等超文本数据的约定和规范

简述 HTTP 1.0 / 1.1 / 2.0 的区别?

1 HTTP1.0

  • 默认使用短连接,每次请求都需要建立连接

2 HTTP1.1

  • 使用 TCP 长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。

  • 支持 管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。

  • 开始支持获取文件的部分内容,这支持了并行下载和断点续传。通过在Header的两个参数实现

    • 客户端请求的时候发送Range参数, 指定第一个字节的位置和最后一个字节的位置。服务端响应的参数是:Content-Range

    • 第一次请求,客户端发起一个get请求;服务器处理请求,返回文件内容以及相应的Header,包括Etag

    • 第二次请求(断点续传):客户端发起get请求,同时发送if-range,这个if-range的值就是第一次请求中服务器返回的etag;服务器判断etag是否匹配,是则返回206,否则返回200

  • 高延迟——队头阻塞

    • 顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一并被阻塞,会导致客户端迟迟收不到数据
  • 无状态特性 — 阻碍交互

    • 无状态是指协议对于连接状态没有记忆能力
  • 明文传输 — 不安全性

    • 传输内容没有加密,中途可能被篡改和劫持
  • 不支持服务端推送

3 HTTP2.0

  • 头部压缩

    • HTTP/2 会压缩头(Header)如果你同时发出多个请求,他们的头是一样的或是相似的,那么,协议会帮你消除重复。

    • 使用的是HPACK算法。在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了

    • 每次请求和响应只发送差异头部

    • 原理:维护一份相同的静态字典(Static Table),包含常见的头部名称,以及特别常见的头部名称与值的组合;维护一份相同的动态字典(Dynamic Table),可以动态的添加内容;支持基于静态哈夫曼码表的哈夫曼编码(Huffman Coding);

    • 静态字典作用:对于完全匹配的头部键值对,例如 “:method :GET”,可以直接使用一个字符表示;对于头部名称可以匹配的键值对,例如 “cookie :xxxxxxx”,可以将名称使用一个字符表示。

    • 同时,浏览器和服务端都可以向动态字典中添加键值对,之后这个键值对就可以使用一个字符表示了。需要注意的是,动态字典上下文有关,需要为每个 HTTP/2 连接维护不同的字典。在传输过程中使用,使用字符代替键值对大大减少传输的数据量。

    Index Header Name Header Value
    1 :authority
    2 :method GET
    3 :method POST
    4 :path /
    5 :path /index.html
    6 :scheme http
    7 :scheme https
    8 :status 200
    32 cookie
    60 via
    61 www-authenticate
  • 二进制格式

    • 报文采用二进制格式。头信息和数据体都是二进制,统称为帧:头信息帧和数据帧

    • 当计算机收到报文后,无需将明文报文转成二进制,而是直接解析二进制报文,增加了数据传输的效率

  • 数据流

    • 数据包不按顺序发送

    • 每个请求或者回应的所有数据包称为一个数据流

    • 每个数据流会标记一个独一无二的编号,其中客户端为奇数,服务器为偶数。使得并行的数据传输成为可能

    • 通过标记来指出它属于哪个回应,通过给数据包添加标签来区分不同的请求和响应

  • 多路复用

    • 在一个连接中能够并发多个请求或回应,不用按顺序一一对应,最终根据每个请求和响应上的标记组合成正常的请求和响应

    • 即对于A、B请求,A的请求在先,但是A的请求处理很慢,B的请求在后,但是处理速度较快。在这种情况下,可以先回应A已完成的部分,然后回应B请求,待A完成后,再回应剩余部分

    • 避免了1.1管道传输过程中,由于前面一个请求的响应被阻塞了,导致后面请求的响应也被阻塞了

    • 好处:单连接多资源的方式,减少服务端的链接压力,内存占用更少,连接吞吐量更大;由于减少TCP 慢启动时间,提高传输的速度

  • 服务器推送

    • 服务器不再完全是被动响应

    • 譬如说在浏览器请求HTML的时候,可以提前将可能用到的静态文件主动发送给客户端,减少延时等待

  • 不足:

    • HTTP/1.1 中的管道( pipeline)传输中如果有一个请求阻塞了,那么队列后请求也统统被阻塞住了

    • HTTP/2 多请求复用一个TCP连接,一旦发生丢包,就会阻塞住所有的 HTTP 请求。

  • 不足:

    • TCP 以及 TCP+TLS 建立连接的延时:TCP连接需要3次握手,即1.5个RTT时间;TLS大致需要1-2个RTT;

    • TCP 的队头阻塞并没有彻底解决:丢包发生时,需要进行重传确认,此时会阻塞整个TCP连接中的所有请求

    • 多路复用导致服务器压力上升:多路复用没有限制同时请求数;请求的平均数量与往常相同,但实际会有许多请求的短暂爆发,导致瞬时 QPS 暴增;

    • 多路复用容易 Timeout:大批量的请求同时发送,由于 HTTP2 连接内存在多个并行的流,而网络带宽和服务器资源有限,每个流的资源会被稀释,虽然它们开始时间相差更短,但却都可能超时

4 HTTP3.0

  • 将HTTP下层的TCP改成了UDP

  • 基于UDP的QUIC协议可以实现类似TCP的可靠传输

    • QUIC 有自己的一套机制可以保证传输的可靠性的。当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响

    • QUIC 是一个在 UDP 之上的伪 TCP + TLS + HTTP/2 的多路复用的协议

  • TL3 升级成了最新的 1.3 版本,头部压缩算法也升级成了 QPack

  • HTTPS 要建立一个连接,要花费 6 次交互,先是建立三次握手,然后是 TLS/1.3 的三次握手。QUIC 直接把以往的 TCP 和 TLS/1.3 的 6 次交互合并成了 3 次,减少了交互次数

同步异步、阻塞非阻塞的区别?

  • 阻塞/非阻塞:

    • 描述的是调用者调用方法后的状态

    • 阻塞:调用者在调用方法后需要一直等待结果,不能干别的事情

    • 非阻塞:调用者在调用后无需等待,可以干别的事情

  • 同步/异步:

    • 描述的是方法跟调用者间通信的方式

    • 同步:调用的方法需要自身完成,在调用方法后,调用者需要一直等待结果返回,也就是要不停地查询结果是否得出

    • 异步:调用的方法无需自身完成,调用方在调用方法后直接返回,调用方法通过回调或者消息通知等方式来通知调用者结果

ARP协议可以确定MAC地址,为什么还需要IP协议?

  • 使用IP协议更像是一种分层的思想

  • ARP协议使用的是广播的方式来确定目标的MAC地址

  • 但是在世界范围内不可能通过广播的方式来从数以千万的计算机中找到目标MAC地址而不超时

  • 因此通过IP协议将报文送达至指定的网段中,然后再使用ARP协议找到目标的MAC地址

连接异常与RST的区别?

  • TCP连接出现严重的错误,必须释放连接就会将报文的RST置位

  • 这种方式属于异常终止连接:

    • 丢弃任何尚未发送的数据,立即发送 RST 报文段

    • RST 接收方会区分另一端是异常关闭还是正常关闭,从而做出不同响应

    • 任何收到 RST 段的一方根本不会为这个 RST 进行确认

    • 主动发送 RST 段的一方,不会进入 TIME_WAIT 状态

半打开(Half-Open)是什么意思?

  • 如果一方已经关闭或异常终止,而另一方却对此毫不知情,这种连接就称为半打开的。

  • 处于半打开的 A 向主机 B 发送数据:

    • 如果主机 B 仍然断网或者已经连接上网络,但是服务未启动,A 向 B 发送数据,经过数次超时重传后放弃连接,并发送 RST 段给对方(不一定非得发送,这系统实现有关)。

    • 如果主机 B 已经连接上网络且重新启动了服务,A 向 B 发送数据,B 收到后因为不认识这个连接,向 A 发送 RST 段。

累计确认和捎带确认是什么意思?

  • 累积确认

有时候,发送方发送速度非常快,接收方一下下接收到了好几个 tcp 段,可以通过累积确认的方式,一次确认好几个 tcp 段,这样减少报文段的传输。

  • 捎带确认

有时候,双方互相发送数据,当接收到对方的 tcp 段后,先不着急确认,而是等待一会儿,连同数据和 ack 一起发送过去,这种情况叫捎带确认。如果等了一会儿(到时间了),接收方还没有数据要发送,那就直接回复一个纯 ack 过去,这样的 ack 称为延时的 ack(Delayed ACK)。

PSH标志位的作用?

  • 用来通知TCP什么时候将数据发送出去(从发送缓冲区中取数据),以及 read 函数什么时候将数据从接收缓冲区读取都是未知的

    • 发送方由TCP模块自行决定什么时候将缓冲区数据打包成TCP报文,并加上PSH标志。一般来说,write一次就会发送一次,或者缓冲区满了也会立即发送

    • 接收方收到了包含PSH标志的TCP报文后,立即将缓冲区的所有数据推送给应用程序

  • 总结

    • 通知发送方立即发送报文

    • 通知接收方立即将数据推送到应用程序

UDP相关问题

1 概述

  • 无连接协议。意味着各个报文没有顺序性,你可能先发了一个数据包 A,后发一个数据包 B,结果对方却先收到 B,后收到 A

  • UDP 协议不提供可靠性,这意味着不能保证对端一定能收到数据

2 UDP也可以是有连接的

  • 有连接的协议,存在sockname + peername

    • bind 函数本质上就是 setsockname,而 connect 函数本质上就是 setpeername 函数
  • 无连接的情况,总是缺少peername

  • 如果想让UDP套接字称为有连接,只需要指定它的peername即可

  • 即在UDP客户端调用connect函数

  • 有连接的UDP套接字的变化

    • 有连接的 UDP 套接字引发错误,会返回给它所在的进程。无连接的 UDP 套接字不会。

    • 不需要再使用 recvfrom 来获取数据报的发送者了,应该改用 read 或 recv 等函数。

    • 不能再使用 sendto 函数指定目的 IP 和 port,这个参数需要指定成 NULL,或者干脆使用 write 函数。

3 有连接和无连接的性能比较

  • 无连接在每次发送数据时,需要在内核中暂时创建该套接字,发送完数据后再次断开

  • 有连接会在第一次的时候建立连接,然后一直复用

  • 因此有连接的性能会更好一些

4 UDP如何增加可靠性?(使用UDP做为传输层协议时,如何确保数据传输的可靠性?)

  • 借鉴TCP的思想,可以采用确认+重传的方式来增加UDP的可靠性

  • 与TCP的不同:

    • 可靠性的安全机制可以根据自己的需求进行灵活的设计
  • 可以利用RUDP 等开源库来实现可靠的UDP传输

TCP粘包问题的原因和解决方法是什么?

  • 主要原因:

    • TCP 协议是面向字节流的协议,它可能会组合或者拆分应用层协议的数据;

    • 应用层协议的没有定义消息的边界导致数据的接收方无法拼接数据;

  • 发送方原因:

  • Nagle算法:

    • 当数据包较小时,不会将数据包直接发送出去,而是在缓存区间等待一段时间,看一下后续有没有其它的数据包可以合并一起发送

    • 能够降低网络拥塞的可能性,减少数据发送的额外开销(IP首部+TCP首部将近40字节)

    • 内核参数:

      • TCP_NODELAY

      • TCP_CORK:延迟发送数据;当发送的数据小于 MSS 时,TCP 协议就会延迟 200ms 发送该数据或者等待缓冲区中的数据超过 MSS

  • 接收方原因:

    • TCP接收到数据包时,并不会马上交到应用层进行处理,或者说应用层并不会立即处理

    • TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组

    • 当接收速度大于应用程序的读取速度,应用程序就有可能得到首尾相连粘在一起的包

  • 消息边界

    • 由于TCP协议是面向字节流的协议,它有可能组合或者拆分应用层协议的数据,因此,应用层的协议需要自己划分消息边界。

    • 使用固定长度的消息

    • 在协议头中增加表示负载长度的字段

    • 举例:

      • HTTP 消息中,使用 Content-Length 头表示 HTTP 消息的负载大小,这样接收方在接收到足够的字节数后,就可以分离出完整的HTTP消息

      • HTTP还会使用基于终结符的策略:终结符不能包含在数据正文中

      • 在使用块传输机制时,不再包含 Content-Length 字段,使用负载为0的HTTP消息作为终结符表示消息的边界

  • UDP协议是面向报文的

  • UDP的发送单元与应用程序的消息单元是一一对应的关系,因此不存在将包的数据组合或者拆分的问题,本身就具备了消息边界,也就不存在粘包问题

  • 单个报文不能超过MTU大小,否则IP层会拆分

  • 对于TCP而言,多个数据包可以一次接收完成,然后放在缓冲区中

  • 但是对于UDP而言,不论缓冲区有多大,每个数据包需要单独一次接收动作,然后将接收的数据包挂载在接收端的链式结构上,每一次接收端读取数据时会从链上读取一个数据包出来。而且UDP本身支持一对多的通信,因此数据包会保留消息头(消息来源地址、端口等消息),对于接收端而言能够更好地区分不同的数据包,避免粘包问题

点对点和端对端的区别

  • 点对点

    • ==发送端把数据传给与它直接相连的设备==,这台设备在合适的时候又把数据传给与之直接相连的下一台设备,通过一台一台直接相连的设备,把数据传到接收端

    • 优点:发送端发送完数据后,任务就结束了,不会浪费发送端资源

    • 如果接收设备故障,就利用存储转发技术进行缓冲

    • 不足:发送数据后,不知道接收端是否成功接收和何时接收

  • 端对端

    • 数据传输前,经过各种各样的交换设备,在两端设备间建立一条链路,就象它们是直接相连的一样,链路建立后,发送端就可以发送数据,直至数据发送完毕,接收端确认接收成功

    • 发送前,经过各种交换设备建立一条链路后,发送端再发送数据,而且发送端需要一直等待,直到接收端确认成功

    • 优点:确认消息送达,中间设备无需存储转发,延迟低

    • 不足:发送端需要一直参与整个过程,浪费发送端资源

    • 如果接收设备故障,那么端到端不可能实现

  • 数据可靠性

  • 数据链路层和网络层一般是点对点

  • 传输层一般是端对端

在浏览器输入 URL 回车之后发生了什么?

过程 使用的协议
1、浏览器查找域名DNS的IP地址 DNS查找过程(浏览器缓存、路由器缓存、DNS缓存) DNS:获取域名对应的ip
2、根据ip建立TCP连接 TCP:与服务器建立连接
3、浏览器向服务器发送HTTP请求 HTTP:发送请求
4、服务器响应HTTP响应 HTTP
5、浏览器进行渲染
  • URL 解析

    • 判断输入的是否为一个合法的URL
  • DNS查询(域名解析)

    • 首先检查本地是否有缓存

      • 浏览器缓存

      • 操作系统缓存,本地hosts文件

      • 路由器缓存

      • IPS DNS 服务器缓存

    • 发送网络请求来查询:

      • 本地域名服务器先找根域名服务器

      • 再找顶级域名服务器

      • 最后找权威域名服务器

  • 得到IP地址后,进行TCP连接

    • 三次握手
  • 连接成功后,发送http请求

  • 服务器处理请求

  • 浏览器接收响应

    • 对接收到的响应资源分析

    • 根据不同的响应码做不同的事情

    • 根据响应类型来解析响应内容

  • 渲染页面

    • 不同内核的渲染过程有所不同

套接字类型有哪些?

  • 数据报套接字(Datagram sockets):

    • 无连接的服务

    • 主要用于UDP协议

  • 流套接字(Stream sockets)

    • 面向连接

    • 主要用于TCP协议

  • 原始套接字(Raw sockets)

    • 可以直接接收和发送IP数据包,无需基于特定的传输层协议

UDP协议的特点?

  • 无连接协议,无需建立连接就可以发送数据

  • 特点:

    • 速度快

    • 无需建立连接,因此没有建立连接的时延

    • 无连接状态:TCP需要维护序号、确认号、拥塞控制参数、发送缓存等数据;UDP没有这些数据,也不存在发送缓存和接收缓存,因此使用UDP协议一般能支持更多的活跃用户;

    • 报文首部开销小:UDP为8个字节;TCP为20字节;

    • UDP协议本身是不保证报文的可靠送达,但是应用层可以自己采取措施(如增加确认和重传机制),从而保证数据的可靠传输

  • 组成:

    • 源端口号

    • 目标端口号

    • 长度

    • 校验和:前面四个都是16位,一共8字节的头部

    • 数据

  • UDP的不可靠原因在于它虽然提供了差错检测(通过校验和),但是对于差错没有恢复功能,更没有重传机制

  • UDP收到应用层数据会立即交给IP层进行发送

UDP与TCP区别?

  • TCP提供面向连接的传输,UDP提供无连接的传输

  • TCP提供可靠的传输,UDP不保证可靠传输

  • TCP面向字节流,有额外的分组开销;UDP面向数据报,没有分组开销

  • TCP有拥塞和流控机制;UDP在网络繁忙的情况依然会继续发送

  • 上层协议

  • UDP

    • DNS
  • TCP

    • HTTP,FTP

DNS何时使用TCP协议,何时使用UDP协议?

  • DNS作为应用层协议,占用端口53,同时使用UDP和TCP

  • 当DNS进行区域传输的时候会使用TCP协议,其它时候会使用UDP协议

    • UDP是面向报文的,一般UDP长度不会大于512字节。TCP允许报文长度超过512字节,TCP协议能够对报文进行拆分和重组

    • 而且使用TCP保证了数据同步的准确性

    • DNS规范中有两种类型的DNS服务器:主DNS服务器和辅助DNS服务器

    • 在一个区中,主DNS服务器从自己本机的数据文件中读取该区的DNS数据信息,而辅助DNS服务器则从区的主DNS服务器中读取该区的DNS数据信息。

    • 当一个辅助DNS服务器启动时,它需要与主DNS服务器通信,并加载数据信息,这就叫做区传送(zone transfer)

    • 辅助DNS服务器会定时向主DNS服务器同步数据,如果数据变动,则进行区域传送,此时使用TCP,因为传送的数据量较多

  • 客户端在进行域名解析的时候会使用UDP协议

    • 查询域名的返回结果一般不大于512字节

    • 使用UDP能够减轻DNS服务器的负担,提高响应速度

    • 很多DNS服务器仅支持UDP查询包

为什么UDP报文大小要小于512字节?

  • 在局域网通信的时候,数据帧长度需要在46-1500字节之间,去出到IP头部和UDP报文首部,大概剩下1472字节

  • 但是在进行Internet编程的时候,由于Internet上不同路由器的MTU值可能有所不同,如果我们假定MTU为1500来发送数据的,而途经的某个网络的MTU值小于1500字节,那么系统将会使用一系列的机制来调整MTU值,使数据报能够顺利到达目的地,这样就会做许多不必要的操作.

  • 鉴于Internet上的标准MTU值为576字节,所以我建议在进行Internet的UDP编程时.最好将UDP的数据长度控件在548字节(576-8-20)以内.

  • 因此为了适应网络环境,一般UDP报文的长度不会大于512字节

当然,这个没有唯一答案,相对于不同的系统,不同的要求,其得到的答案是不一样的。

我这里仅对像ICQ一类的发送聊天消息的情况作分析,对于其他情况,你或许也能得到一点帮助:首先,我们知道,TCP/IP通常被认为是一个四层协议系统,包括链路层,网络层,运输层,应用层.UDP属于运输层,

下面我们由下至上一步一步来看:以太网(Ethernet)数据帧的长度必须在46-1500字节之间,这是由以太网的物理特性决定的.这个1500字节被称为链路层的MTU(最大传输单元).但这并不是指链路层的长度被限制在1500字节,其实这这个MTU指的是链路层的数据区.并不包括链路层的首部和尾部的18个字节.

所以,事实上,这个1500字节就是网络层IP数据报的长度限制。因为IP数据报的首部为20字节,所以IP数据报的数据区长度最大为1480字节.而这个1480字节就是用来放TCP传来的TCP报文段或UDP传来的UDP数据报的.又因为UDP数据报的首部8字节,所以UDP数据报的数据区最大长度为1472字节.这个1472字节就是我们可以使用的字节数。

当我们发送的UDP数据大于1472的时候会怎样呢?这也就是说IP数据报大于1500字节,大于MTU.这个时候发送方IP层就需要分片(fragmentation). 把数据报分成若干片,使每一片都小于MTU.而接收方IP层则需要进行数据报的重组. 这样就会多做许多事情,而更严重的是,由于UDP的特性,当某一片数据传送中丢失时,接收方便 无法重组数据报.将导致丢弃整个UDP数据报。

因此,在普通的局域网环境下,我建议将UDP的数据控制在1472字节以下为好.

进行Internet编程时则不同,因为Internet上的路由器可能会将MTU设为不同的值. 如果我们假定MTU为1500来发送数据的,而途经的某个网络的MTU值小于1500字节,那么系统将会使用一系列的机 制来调整MTU值,使数据报能够顺利到达目的地,这样就会做许多不必要的操作.

鉴于Internet上的标准MTU值为576字节,所以我建议在进行Internet的UDP编程时. 最好将UDP的数据长度控件在548字节(576-8-20)以内

域名分类

  • 根域名用”.“表示?

根域名服务器的个数?

  • IPV4:13

  • IPV6:25

DNS缓存是怎么实现的?

  • 进行域名解析的时候,会查看浏览器缓存、系统缓存、hosts文件,都没有才去LDNS查询

  • 对于一个域名123.abc.qq.com.cn

    • LDNS也会先看一下有没有123.abc.qq.com.cn的缓存,有就直接返回,没有,就继续看一下abc.qq.com.cn,qq.com.cn,com.cn,.cn的权威域名服务器的地址

    • 如果有,则直接去对应的服务器那里进行解析

    • 没有才去问根服务器

ICMP协议介绍一下?

  • ICMP协议,全称Internet Control Message Protocol,也叫互联网控制报文协议

  • 常用类型:

    • 查询报文:用来主动查询网络数据包是否可以到达目标主机

    • 差错报文:用来通报源主机,发送数据包出错,到不了指定的网络、主机或者端口

  • 查询报文:

    • 典型应用为ping程序,一种主动请求,并且获得主动应答的ICMP协议

    • 主动请求的报文,称为ICMP请求数据包(ICMP echo request),主动请求的回复,ICMP响应数据包(ICMP echo reply)

  • 差错报文

    • 当互联网协议在传输过程中发生错误,会给源主机发送ICMP差错报文,分类如下:

    • 终点不可达

    • 源站抑制:源主机发送速度过快,目标主机处理不过来,告诉源主机放慢速度

    • 超时:IP数据报有TTL存活时间,每经过一个路由节点减一,到零还没有送到目标主机,就会发送超时的差错报文

    • 路由重定向:通知下次发送给另外一个路由

  • 典型应用:

  • PING,见下面

  • Traceroute

    • W10下命令:tracert www.baidu.com

    • 使用的是ICMP差错报文的原理,可以用来跟踪网络数据包的路由途径

”`

traceroute to blog.chenishr.com (120.77.213.253), 30 hops max, 60 byte packets
 1  172.17.0.1 (172.17.0.1)  1.168 ms  1.091 ms  1.033 ms
 2  192.168.5.1 (192.168.5.1)  15.025 ms  15.352 ms  15.948 ms
 3  192.168.0.2 (192.168.0.2)  14.760 ms  14.683 ms  14.621 ms
 4  * * *
 5  113.106.40.50 (113.106.40.50)  208.131 ms 202.105.155.205 (202.105.155.205)  208.044 ms 61.146.241.233 (61.146.241.233)  208.000 ms
 6  183.56.65.6 (183.56.65.6)  208.619 ms 183.56.65.14 (183.56.65.14)  91.293 ms 183.56.65.86 (183.56.65.86)  91.100 ms
 7  119.147.223.110 (119.147.223.110)  90.981 ms  92.300 ms *
 8  183.2.182.130 (183.2.182.130)  92.160 ms 58.61.162.134 (58.61.162.134)  92.146 ms  92.076 ms
 9  183.61.45.10 (183.61.45.10)  92.041 ms 183.2.184.134 (183.2.184.134)  91.943 ms 183.61.45.10 (183.61.45.10)  91.948 ms
10  116.251.113.142 (116.251.113.142)  91.867 ms * 42.120.242.218 (42.120.242.218)  91.772 ms
11  42.120.253.2 (42.120.253.2)  91.730 ms 42.120.253.6 (42.120.253.6)  91.687 ms 116.251.117.153 (116.251.117.153)  103.880 ms
12  * * *
13  * * *
14  120.77.213.253 (120.77.213.253)  103.499 ms  103.443 ms  103.401 ms
```
  • 序号表示经过的第几个路由,后接的IP地址即为该路由的IP

  • Traceroute默认向每个网关发送三个探测数据包,那三个值是网关响应后返回的时间。星号表示网关没有返回数据

  • 底层原理:

    • 利用了IP数据报的TTL字段和ICMP的差错类型报文

    • 首先TTL设置为1,得到第一个路由返回的ICMP超时差错报文,以此类推,一直到到达目标主机为止

    • Traceroute 给目标主机发送一份UDP数据,但是端口号为不可能的值(大于30000),目标主机的任何程序都不会使用该端口。这样,目标主机会给程序返回端口不可达的差错报文,程序就会知道数据包已经到达目标主机,不再发送探测报文

    • 通过以上信息,就可以将网络数据包的路由途径绘制出来

PING协议介绍一下?

  • ICMP协议

  • 互联网报文控制协议,工作在网络层

  • 作用

    • 确认IP包是否成功送达目标地址,报告发送过程中IP包被废弃的原因、改善网络设置等

    • 检测目标地址是否可达

  • PING过程

    • 源主机构建一个ICMP请求数据包,里面包含了类型字段(8,代表请求数据包),顺序号(用于区分连续ping时发出去的多个数据包,每发一个,增加1),发送时间(用于计算RTT)

    • 然后该数据包再加上目标IP地、本机MAC地址和目标MAC地址等信息后,会发送出去

    • 目标主机收到数据包后,会构建一个ICMP响应数据包,类型字段为0,序号为接收到的请求报文的序号,然后发送给源主机

    • 在规定时间内,如果源主机没有收到ICMP的应答包,说明目标主机不可达,否则,说明主机可达

    • 在收到响应后,源主机用当前时刻减去发送时刻,就能得到数据包的延迟时间

长连接和短连接介绍一下?

  • 短连接

    • 并发量大,但是每个用户不需要频繁操作的情况

    • 应用场合

      • 用户登录场景

      • 客户端与服务端每进行一次HTTP请求,都会建立一次连接

  • 长连接

    • 报文首部:Connection:keep-alive

    • 当客户端和服务端建立连接后,在后续的请求中会复用这个连接,直到某一方主动断开连接

    • 服务端会通过探测报文的方法来检测连接中的另外一方是否已经断开

  • 如何选择

  • 长连接适合操作频繁,点对点通讯,且连接数不会太多的情况

    • 数据库连接
  • 由于长连接会占用服务端的连接资源,短连接用完就会断开,因此对于大并发,大量连接的场景比较适用

    • WEB网站的http服务一般用短连接

并行连接是什么?

  • 针对短连接的优化方案

  • 允许客户端打开多条连接,并行执行多个事务,每个事务都有自己的TCP连接

  • 有效利用带宽资源(但是由于带宽有限,因此带宽不足的情况下,性能提升很小)

  • 利用浏览器多线程的能力,能够进行并发下载

  • 长连接相比于并行连接的优势

    • 避免了每个事务都会打开/关闭一条新的连接,造成时间和带宽的耗费

    • 避免了 TCP 慢启动特性的存在导致的每条新连接的性能降低

    • 可打开的并行连接数量实际上是有限的,持久连接则可以减少建立的连接的数量

原文阅读