Skip to content

常见的网络问题

TCP 拥塞控制和流量控制

区别

  • 流量控制

为了解决发送方和接收方速度不同而导致的数据丢失问题,当发送方发送的太快,接收方来不及接受就会导致数据丢失,流量控制用滑动窗口的形式解决问题

  • 拥塞控制

为了解决过多的数据注入到网络,导致网络奔溃,超过负荷.当发送方发送数据大量的数据会注入到网络,如果没有限制,网络就会超负荷变卡,拥塞控制的用的是拥塞窗口解决的问题的

拥塞控制

  • 慢开始

慢开始的含义就是讲窗口先设置为 1 ,每个传输轮次大小增长一倍,直到大道慢开始的门限(ssthresh),这时候慢开始阶段结束

  • 拥塞避免

慢开始结束后,接下来就是拥塞避免,这个阶段拥塞窗口在每个传输轮次数量加1,直到触发了网络拥塞,窗口大小和门限都变为拥塞时最大的值得一半,然后重新开始慢开始阶段

  • 快重传

快重传指的是当接受方收到顺序错误得数据时不接收数据,同时重复发起对于之前数据的确认,发动到第三次,发送方得知自己的一部分数据再发送中丢失,立即发起重传,不需要等待下一次发送信息时一起发送过去.,且重传时触发和拥塞一样得情况,进入快恢复阶段

  • 快恢复

快恢复就是再发生拥塞和重传时,窗口经历了拥塞避免阶段,然后进入快恢复阶段,和拥塞避免一样都是每次加一,这样能提高恢复速度,像老版本(Tahoe)中需要重新经历慢开始

流量控制

双方在通信的时候,发送方的速率与接收方的速率是不一定相等,如果发送方的发送速率太快,会导致接收方处理不过来,这时候接收方只能把处理不过来的数据存在缓存区里(失序的数据包也会被存放在缓存区里)。

如果缓存区满了发送方还在疯狂着发送数据,接收方只能把收到的数据包丢掉,大量的丢包会极大着浪费网络资源,因此,我们需要控制发送方的发送速率,让接收方与发送方处于一种动态平衡才好。

对发送方发送速率的控制,我们称之为流量控制。

img

如何控制

接收方每次收到数据包,可以在发送确定报文的时候,同时告诉发送方自己的缓存区还剩余多少是空闲的,我们也把缓存区的剩余大小称之为接收窗口大小,用变量win来表示接收窗口的大小。

发送方收到之后,便会调整自己的发送速率,也就是调整自己发送窗口的大小,当发送方收到接收窗口的大小为0时,发送方就会停止发送数据,防止出现大量丢包情况的发生。

发送方何时再继续发送数据

当发送方停止发送数据后,该怎样才能知道自己可以继续发送数据?

当发送方收到接受窗口 win = 0 时,这时发送方停止发送报文,并且同时开启一个定时器,每隔一段时间就发个测试报文去询问接收方,打听是否可以继续发送数据了,如果可以,接收方就告诉他此时接受窗口的大小;如果接受窗口大小还是为0,则发送方再次刷新启动定时器。

img

TCP 三次握手和四次挥手的全过程

  • SYN_SEND:同步已发送
  • SYN_RECV:同步已经收到
  • ESTABLISHED:已建立连接
  • FIN-WAIT-1:终止等待1
  • FIN-WAIT-2:终止等待2
  • CLOSE-WAIT:关闭等待
  • LAST-ACK:最后确认
  • TIME-WAIT:时间等待
  • CLOSED:关闭状态

三次握手

img

第一次握手:客户端发送 syn 包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认; 第二次握手:服务器收到 syn 包,必须确认客户的 SYN(ack=x+1),同时自己也发送一个 SYN 包(syn=y),即 SYN+ACK 包,此时服务器进入 SYN_RECV 状态; 第三次握手:客户端收到服务器的 SYN+ACK 包,向服务器发送确认包 ACK(ack=y+1),此包发送完毕,客户端和服务器进入 ESTABLISHED 状态,完成三次握手。 握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

四次挥手

img

第一次挥手:客户端 的应用进程先向其 TCP 发出连接释放报文段,并停止在发送数据,主动关闭 TCP 连接。客户端把连接释放报文段首部的终止控制位 FIN 置 1,其序号 seq = u ,它等于前面已传过的数据的最后一个字节的序号加 1 。这时 客户端 进入 FIN-WAIT-1 (终止等待1) 状态,等待 服务器 的确认。

第二次挥手:服务器收到连接释放报文段后即发出确认,确认号 ack = u + 1 ,而这个报文段自己的序号是 v ,等于服务器前面已经传过的数据的最后一个字节的序号加 1。服务器就进入 CLOSEWAIT(关闭等待)状态。TCP 服务器进程这时应通知高层应用进程(不确定自己是否还有数据要发送给 客户端(所以是四次不是三次))。因而从 客户端 到 服务器 这个方向的连接就释放了,这时的 TCP 连接处于 半关闭(Half-close)状态。

第三次挥手:若服务器已经没有要向客户端发送的数据,其应用进程就通知 TCP 释放连接。这时服务器发出的连接使用报文段必须使用 FIN = 1。现假设服务器的序号为 w(在半关闭状态服务器可能又发送了一些数据)。服务器还必须重复上次已发送过的确认号 ack = u + 1。这时服务器就进入 LAST-ACK (最后确认) 状态,等待客户端的确认。

第四次挥手:客户端在收到服务器的连接释放报文段后,必须对此发出确认。在确认报文段中把 ACK 置 1,确认号 ack = w + 1,而自己的序号是 seq = u + 1(根据 TCP 标准,前面发送过的 FIN 报文段要消耗一个序号)。然后进入到 TIME-WAIT(时间等待) 状态。请注意,TCP 连接现在还没有释放掉。必须经过时间等待计时器(TIME-WAIT)设置的时间 2MSL 后,客户端 才进入到 CLOSED 状态。时间 MSL 叫做 最长报文段寿命,RFC 793 建议设为 2 分钟。

为什么要三次握手

主要是为了防止已失效的连接请求报文段突然又传到了服务器,因而产生错误。

现假定出现一种异常情况,即客户端发出的第一个连接请求报文段并没有丢失,而是在某些网络结点长时间滞留了,以致延误到连接释放以后的某个时间才到达服务器。本来这是一个早已失效的报文段。但 服务器 收到此失效的连接请求报文段后,就误认为是客户端又发出一次新的连接请求。于是就向客户端发出确认报文段,同意建立连接。假定不采用报文握手,那么只要服务器发出确认,新的连接就建立了。由于现在 客户端 并没有发出建立连接的请求,因此不会理睬 服务器 的确认,也不会向 服务器 发送数据。但是 服务器 却以为新的运输连接已经建立了,并一直等待 客户端 发来数据。服务器 的许多资源就这样白白浪费了。 采用三次报文握手的办法,可以防止上述现象的发生。假如在刚才的异常情况下,客户端 不会像 服务器 的确认发出确认。服务器 由于收不到确认,就知道 客户端 并没有要求建立连接。

为什么要四次握手

第一,为了保证客户端发送的最后一个 ACK 报文段能够到达服务器。这个 ACK 报文段有可能丢失,因而使服务器收不到确认。服务器会超时重传这个 FIN + ACK 报文段,而 客户端 就能在 2MSL 时间内收到这个重传的报文段。接着 客户端 重传一次确认,重新启动 2MSL 计时器。最后,客户端 和 服务器 都能正常进入到 CLOSED 状态。如果 客户端 在 TIME-WAIT 状态不等待一段时间,而是在发送完 ACK 报文段后立即释放连接,那么就无法收到 服务器 重传 的 FIN+ACK 报文段,因而也不会再发送一次确认报文段。这样,服务器 就无法按照正常步骤进入 CLOSED 状态。第二,防止“已失效的连接请求报文段” 出现在本次连接中。客户端 在发送完最后一个 ACK 报文段后,再经过 2MSL,就可以使本次连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个新的连接中不会出现这种旧的连接请求报文段。

TCP 的 三次握手 四次握手

TCP协议中的计时器

TCP中有四种计时器(Timer),分别为:

  1. 重传计时器:Retransmission Timer
  2. 坚持计时器:Persistent Timer
  3. 保活计时器:Keeplive Timer
  4. 时间等待计时器:Timer_Wait Timer

DNS

DNS为什么用TCP和UDP

DNS区域传输的时候使用TCP协议

辅域名服务器会定时(一般3小时)向主域名服务器进行查询以便了解数据是否有变动。如有变动,会执行一次区域传送,进行数据同步。区域传送使用TCP而不是UDP,因为数据同步传送的数据量比一个请求应答的数据量要多得多。

域名解析时使用UDP协议

客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。不用经过三次握手,这样DNS服务器负载更低,响应更快。理论上说,客户端也可以指定向DNS服务器查询时用TCP,但事实上,很多DNS服务器进行配置的时候,仅支持UDP查询包。

http

keep-alive模式

http1.0没有keep-alive,每次传输后都要重新连接,导致握手时间的资源浪费。http1.1增加了 keep-alive 之后,握手之后可以保持连接,保持连接之后管道被复用。达到性能优化的效果。

Keep-Alive: timeout=20,表示这个TCP通道可以保持20秒 max=XXX,表示这个长连接最多接收XXX次请求就断开

tcp keep-alive

tcp中的keep-alive是用来做 心跳监测 ,定时发送一个空的 TCP Segment,来监测连接是否存活

长链接和短链接

  1. 长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。

2.而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源。如加载图片资源等。

长连接:连接→数据传输→保持连接(心跳)→数据传输→保持连接(心跳)→……→关闭连接(一个TCP连接通道多个读写通信);

短连接:连接→数据传输→关闭连接;

报文长度

  • 使用Content-Length表示body报文体的长度。

  • 报文头中的Transfer-Encoding: chunked ,表示报文体body是使用chunked分块方式拼接成的,不需要Content-Length指明长度,chunk编码由一个标明长度为0的chunk标示结束。

socket套接字

  • 流格式套接字(SOCK_STREAM)

流格式套接字(Stream Sockets)也叫“面向连接的套接字”,在代码中使用 SOCK_STREAM 表示。SOCK_STREAM 是一种可靠的、双向的通信数据流,数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送。

SOCK_STREAM 有以下几个特征:

  1. 数据在传输过程中不会消失;
  2. 数据是按照顺序传输的;
  3. 数据的发送和接收不是同步的(有的教程也称“不存在数据边界”)。

为什么流格式套接字可以达到高质量的数据传输呢?因为它使用了 TCP 协议,TCP 协议会控制你的数据按照顺序到达并且没有错误。

  • 数据报格式套接字(UDP)

数据报格式套接字(Datagram Sockets)也叫“无连接的套接字”,在代码中使用 SOCK_DGRAM 表示。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。

因为数据报套接字所做的校验工作少,所以在传输效率方面比流格式套接字要高。它有以下特征:

  • 强调快速传输而非传输顺序;
  • 传输的数据可能丢失也可能损毁;
  • 限制每次传输的数据大小;
  • 数据的发送和接收是同步的(有的教程也称“存在数据边界”)。

数据报套接字也使用 IP 协议作路由,使用 UDP 协议(User Datagram Protocol,用户数据报协议)。

nginx配置的问题

配置http1。1

之前是http1.0,然后出现一些504,经过排查是在4次挥手的过程中出现的,于是想使用长连接。

遇到一个api。开启1.1和keep-alive之后,nginx连接的keep-alive-request(复用次数)和keep-alive-timeout(连接可用时长)时长和次数不对称,导致偶发性的503,当时是出现在流量低谷的时候出现这样的情况,发生的原因是在keep-alive-timeout时间内,nginx还认为这个连接次数还在,就会导致这个问题。

解决方案:在日志平台上面发现问题,最后无奈把1.1关了。 四次挥手的慢:服务端出现大量的close_wait。2个MSL。状态等待导致很多的连接具柄。 升级1.1之后出现503问题,time_wait降低。

配置http2.0

nginx里面是有一个复用次数和,时间。 在chrome上,不同的window,不同的tab使用的是一个tcp,多路复用。 nginx里面加ssl和http2。 http2内部系统有加解密吗?

http2.0还是会存在一些问题的 比如:

哈夫曼编码,哈夫曼树来确认他的编码。

http1.1 的长连接和 http2.0 的多路复用的本质区别?

长连接

同一个域名访问同一个文件的多个请求都可以复用一个 tcp 连接(不用像 1.0 一样 每次请求都需要重新建立连接) 1.多个请求只能被串行处理(数据基于文本,只能按顺序传输); 2.访问多个不同的文件依然会建立多个请求。

多路复用

同一个域名访问多个文件的请求也可以复用一个 tcp 连接,且多个请求可以被并行处理。

nginx 缓存配置

强缓存配置

nginx
server {
  location ~* \.(html)$ {
    access_log off;
    add_header  Cache-Control  max-age=no-cache;
  }

  location ~* \.(css|js|png|jpg|jpeg|gif|gz|svg|mp4|ogg|ogv|webm|htc|xml|woff)$ {
    # 同上,通配所有以.css/.js/...结尾的请求
    access_log off;
    add_header    Cache-Control  max-age=360000;
  }
}

协商缓存配置

nginx
server {
  location ~* \.(html)$ {
    access_log off;
    add_header  Cache-Control  max-age=no-cache;
  }

  location ~* \.(css|js|png|jpg|jpeg|gif|gz|svg|mp4|ogg|ogv|webm|htc|xml|woff)$ {
    # 同上,通配所有以.css/.js/...结尾的请求
    access_log off;
    add_header Cache-Control max-age=360000;
  }
}

Released under the MIT License.