阿里架构师讲面试:计算机网络通信

2,440 阅读19分钟

应用层

为什么要引入应用层

定义进程间传输的内容规范。

对于不同的网络应用需要不同的应用层协议。在互联网中应用层协议很多,如域名系统 DNS,支持万维网应用的 HTTP 协议,支持电子邮件的 SMTP 协议等等。我们把应用层交互的数据单元称为报文。

HTTP协议

https

http长链接和短链接

对于HTTP 1.0的http标准而言,默认连接是短连接,啥叫短连接?就是服务器当发送完最后一个字节的数据之后将关闭连接,也就是回收tcp_sock结构,这样,如果客户端再发送数据给服务器,将直接丢弃。即使此时客户端还有这样的结构,但是我们说连接已经关闭或者已经断了。那客户端知不知道啥时候服务器的连接关闭?不知道,双方可以在任何时候来关闭自己的连接而没有必要通知对方。那短连接的弊端,大家可能都已经知道了,如果对一个服务器要连续发送多个请求,还需要为每次请求建立新的连接。

为了降低建立连接的时间,HTTP 1.1引入了长连接的概念,并把它搞成了默认的连接方式。啥叫长连接?就是当完成一个业务之后,socket结构并不回收。这样,只要在socket结构还存在的时候,客户端发送的任何数据,服务器都可以收到,这就是所谓的长连接。

相比短连接而言,长连接并没有什么特别的新的技术,只是维护socket结构时间长了。因为,说http长连接更不如说是tcp长连接。

http和tcp协议关系

TCP是底层通讯协议,定义的是进程间数据传输和连接方式的规范(进程间可靠传输);

HTTP是应用层协议,定义的是进程间传输数据的内容的规范(应用层内容规范约定);

传输层

为什么引入传输层

我们知道传输层位于网络层之上,网络层提供了主机之间的逻辑通道。那既然已经把一个数据包从一个主机发到另一个主机上面了,为什么还需要传输层呢?这是因为传输层提供了应用进程之间的端-端连接。我们知道一个电脑可能有多个进程同时在使用网络连接,那么网络包达到主机之后,怎么区分自己属于那个进程?这就需要靠传输层的作用了。(网络层只负责IP层面的数据传输,且对可靠性不负责)因此,引入传输层主要有两点:

  • 进程层面的数据通信协议。
  • 传输可靠性保障。

参考:blog.csdn.net/hanzhen7541…

TCP协议

什么是TCP链接?

这里先说结论,连接实际上是操作系统内核的一种数据结构,称为TCP控制块(TCB),对于linux而言是tcp_sock结构。用于保证可靠性和流控制机制的信息,包括 Socket、序列号以及窗口大小叫做连接。

建立 TCP 连接就是通信的双方需要对上述的三种信息达成共识,连接中的一对 Socket 是由互联网地址标志符(IP)和端口组成的,窗口大小主要用来做流控制,最后的序列号是用来追踪通信发起方发送的数据包序号,接收方可以通过序列号向发送方确认某个数据包的成功接收。

为什么需要TCB?

当应用希望发送数据时,并不是直接向网卡驱动发数据,而是先放入到一个内核缓冲区中,然后根据一定算法(达到一定数量或者调用flush之后),缓冲区中的数据就发送到网卡中了(这里说的不准确,实际上,是网卡主动从缓冲区中拷贝出来的,但并不影响我们理解)。

当网卡收到数据时,数据包要先经过如下几步:

  1. 数据包要先经过网卡校验正确与否。
  2. 数据链路层根据报头的类型传给不同的上层类型(IP层或者其他),并移除了数据链路层报头。
  3. IP层也需要先校验,然后根据IP报头选择不同的类型(TCP或者UDP),然后移除IP层报头,并将剩余的数据发送到相应的处理程序(tcp或udp)
  4. 到了tcp层,处理程序此时根据tcp首部中的端口号选择一个socket,并将其载荷数据拷贝进去。

所以到这里,我们就应该知道,每个socket必须要有自己独立的发送缓冲区和接收缓冲区,并且还应该还有其他控制结构或者标示结构,这就构成了TCB,没有TCB,接收到的数据根本就不知道要传递到哪里。

为什么说四元组是连接的唯一标识?

我们可能已经多次看到这样一种说法:一个tcp连接由一个连接四元组唯一标识。连接四元组是指<\source ip,source port,target ip,target port>。

为什么需要四元组,我们就不多说了,如果四个元素缺少任何一个都会发生冲突。那在网络接收过程中是如何利用到这四元组呢?

看了上个部分,你可能会有种错觉,是网卡一步一步向上传递的。其实严格说并不是这样,当网卡收到数据之后,首先经过校验没有错误之后,通过DMA直接发送到内存缓冲区中,然后给CPU发送一个中断信号,通知操作系统一个数据包到了。

注意:这里面的内存缓冲区并不是socket的接收缓冲区,而是网卡驱动提前向操作系统申请的一块内存,并且驱动会提前告诉网卡这块内存的地址(注意是物理地址)和大小。如果没有这块内存缓冲区,那么网卡会直接将数据丢掉。

在操作系统获悉到一个数据包来到之后,就利用中断函数来一步步执行并解析数据包,直到TCP层,到了TCP层,TCP要决定将数据包发给哪个socket的接收缓冲区。

怎么找?这里TCP就是利用连接四元组,并以这个四元组为key,查找hash表找到对应的socket的socket结构指针,并利用该指针找到对应socket的接收缓冲区,并将载荷数据拷贝进去。

半连接队列和SYN攻击

服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列

当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。

SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用半连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。

TCP为什么要三次握手和四次挥手?

我们将原有的问题转换成:为什么需要通过三次握手才可以初始化 Sockets、窗口大小和初始序列号?

  1. 阻止历史的重复连接初始化造成的混乱问题,防止使用 TCP 协议通信的双方建立了错误的连接。(通过序列号即可判断)
  2. 对通信双方的初始序列号进行初始化。

如上图所示,通信双方的两个 TCP A/B 分别向对方发送 SYN 和 ACK 控制消息,等待通信双方都获取到了自己期望的初始化序列号之后就可以开始通信了,由于 TCP 消息头的设计,我们可以将中间的两次通信合成一个,TCP B 可以向 TCP A 同时发送 ACK 和 SYN 控制消息,这也就帮助我们将四次通信减少至三次。

  1. 客户端:我的序列号从100开始的。
  2. 服务端:知道了。我的序列号从300开始的。
  3. 客户端:知道了。

因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET**,**所以只能先回复一个ACK报文,告诉客户端,"你发的FIN报文我收到了"。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手:

  1. 客户端:关掉了。
  2. 服务端:知道了。
  3. 服务端:关掉了。
  4. 客户端:知道了。

参考:

draveness.me/whys-the-de…

juejin.cn/post/684490…

TCP如何保障可靠传输?

停止等待ARQ协议

  1. A向B每发送一个分组,都要停止发送,等待B的确认应答;A只有收到了B的确认应答后才能发送下一个分组。
  2. 当分组丢失或出现差错的情况下,A都会超时重传分组。

应答丢失****和应答迟到的情况

TCP会给每个字节都打上序号,用于判断该分组是否已经接收。

应答丢失:若B正确收到分组,并已经返回应答,但应答在返回途中丢失了。此时A也收不到应答,从而超时重传。紧接着B又收到了该分组。接收者根据序号来判断当前收到的分组是否已经接收,若已接收则直接丢弃,并补上一个确认应答。

应答迟到:若由于网络拥塞,A迟迟收不到B发送的应答,因此会超时重传。B收到该分组后,发现已经接收,便丢弃该分组,并向A补上确认应答。A收到应答后便继续发送下一个分组。但经过了很长时间后,那个失效的应答最终抵达了A,此时A可根据序号判断该分组已经接收,此时只需简单丢弃即可。

停止等待协议的注意点

  • 每发送完一个分组,该分组必须被保留,直到收到确认应答为止。
  • 必须给每个分组进行编号。以便按序接收,并判断该分组是否已被接收。
  • 必须设置超时计时器。每发送一个分组就要启动计时器,超时就要重发分组。
  • 计时器的超时时间要大于应答的平均返回时间,否则会出现很多不必要的重传,降低传输效率。但超时时间也不能太长。

连续ARQ协议<滑动窗口协议>

停止等待ARQ(Automatic Repeat reQuest)协议发送者每次只能发送一个分组,在应答到来前必须等待。而连续ARQ协议的发送者拥有一个发送窗口发送者可以在没有得到应答的情况下连续发送窗口中的分组。

  • 累计确认

在连续ARQ协议中,接收者也有个接收窗口,接收者并不需要每收到一个分组就返回一个应答,可以连续收到分组之后统一返回一个应答。

  • 流量控制

如果发送者发送数据过快,接收者来不及接收,那么就会有分组丢失。为了避免分组丢失,控制发送者的发送速度,使得接收者来得及接收,这就是流量控制。流量控制根本目的是防止分组丢失,它是构成TCP可靠性的一方面。

滑动窗口协议既保证了分组无差错、有序接收,也实现了流量控制。主要的方式就是接收方返回的 ACK 中会包含自己的接收窗口的大小,并且利用接收窗口大小来控制发送方的数据发送。

流量控制引发的死锁?怎么避免死锁的发生?

当发送者收到了一个窗口为0的应答,发送者便停止发送,等待接收者的下一个应答。但是如果接下来窗口不为0的应答在传输过程丢失,发送者一直等待下去,而接收者以为发送者已经收到该应答,等待接收新数据,这样双方就相互等待,从而产生死锁。

**为了避免流量控制引发的死锁,TCP使用了持续计时器。**每当发送者收到一个零窗口的应答后就启动该计时器。时间一到便主动发送报文询问接收者的窗口大小。若接收者仍然返回零窗口,则重置该计时器继续等待;若窗口不为0,则表示应答报文丢失了,此时重置发送窗口后开始发送,这样就避免了死锁的产生。

  • 拥塞控制

拥塞控制就是防止过多的数据注入网络中,这样可以使网络中的路由器或链路不致过载。拥塞控制是一个全局性的过程,和流量控制不同,流量控制指点对点通信量的控制。

发送方维持一个叫做拥塞窗口cwnd**(congestion window)**的状态变量。拥塞窗口的大小取决于网络的拥塞程度,并且动态地在变化。发送方让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接收能力,发送窗口可能小于拥塞窗口。<如果接收窗口小于拥塞窗口,则发送窗口大小等于接收窗口>

慢开始算法的思路就是,不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。<从1指数增长>

为了防止cwnd增长过大引起网络拥塞,还需设置一个慢开始门限****ssthresh状态变量。ssthresh的用法如下:

cwnd<ssthresh时,使用慢开始算法。

cwnd>ssthresh时,改用拥塞避免算法。

cwnd=ssthresh时,慢开始与拥塞避免算法任意。

拥塞避免算法让拥塞窗口缓慢增长,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍。这样拥塞窗口按线性规律缓慢增长。

无论是在慢开始阶段还是在拥塞避免阶段,只要发送方判断网络出现拥塞(其根据就是没有收到确认,虽然没有收到确认可能是其他原因的分组丢失,但是因为无法判定,所以都当做拥塞来处理),就把慢开始门限设置为出现拥塞时的发送窗口大小的一半。然后把拥塞窗口设置为1,执行慢开始算法。如下图:

快重传要求接收方在收到一个失序的报文段后就立即发出重复确认(为的是使发送方及早知道有报文段没有到达对方)而不要等到自己发送数据时捎带确认。快重传算法规定,发送方只要一连收到三个重复确认就应当立即重传对方尚未收到的报文段,而不必继续等待设置的重传计时器时间到期。

快重传配合使用的还有快恢复算法,有以下两个要点:

①当发送方连续收到三个重复确认时,就执行“乘法减小”算法,把ssthresh门限减半。但是接下去并不执行慢开始算法。

②考虑到如果网络出现拥塞的话就不会收到好几个重复的确认,所以发送方现在认为网络可能没有出现拥塞。所以此时不执行慢开始算法,而是将cwnd设置为ssthresh的大小,然后执行拥塞避免算法。如下图:

连续ARQ的注意点:

  • 接收者收到的字节会存入接收窗口,接收者会对已经正确接收的有序字节进行累计确认,发送完确认应答后,接收窗口就可以向前移动指定字节。
  • 同一时刻发送窗口的大小并不一定和接收窗口一样大。虽然发送窗口的大小是根据接收窗口的大小来设定的,但应答在网络中传输是有时间的,有可能t1时间接收窗口大小为m,但当确认应答抵达发送者时,接收窗口的大小已经发生了变化。
  • 此外发送窗口的大小还随网络拥塞情况影响。当网络出现拥塞时,发送窗口将被调小。
  • TCP标准并未规定未按序到达的字节的处理方式。但TCP一般都会缓存这些字节,等缺少的字节到达后再交给应用层处理。这比直接丢弃乱序的字节要节约带宽。
  • TCP标准规定接收方必须要有累计确认功能。接收方可以对多个TCP报文段同时确认,但不能拖太长时间,一般是0.5S以内。

blog.csdn.net/sinat_21112…

TCP和UDP区别

TCP(Transmission Control Protocol,传输控制协议)

UDP(User Datagram Protocol,用户数据包协议)

当IP包通过路由将数据传输到目的地时,会根据TCP或UDP包头中的源端口和目的端口信息,请求和获取不同的应用。也就是说,不管TCP还是UDP,都含有网络服务必须的源端口和目的端口信息,以建立和实现网络传输服务。这时,你的疑问就来了:既然都用于传输,为何要搞两个不同的协议呢?这就需要从网络中不同服务的需求来谈起。

在网络中,有些服务,如HTTP、FTP等,对数据的可靠性要求较高,在使用这些服务时,必须保证数据包能够完整无误的送达;**而另外一些服务,如DNS、即时聊天工具等,并不需要这么高的可靠性,高效率和实时性才是它们所关心的。**根据这两种服务不同的需求,也就诞生了面向连接的TCP协议,以及面向无连接的UDP协议。

TCP报文是交由IP网络来负责运输,IP网络并不能保证TCP报文到达目的地,既然IP网络是指望不上了,那TCP就自力更生吧,TCP必须依赖自身的努力来保证数据传输的可靠。TCP和UDP协议的区别如下:

  • 可靠性

  • 基于连接与无连接;

  • TCP保证数据可达性和顺序性,UDP可能丢包和乱序;

  • 资源占用

  • 对系统资源的要求(TCP较多,UDP少);UDP报文头8个字节,TCP报文头字节20个字节**。**

  • 速度

  • UDP协议吞吐量不受流量控制和拥塞控制的调节,只受应用软件生成数据的速率、传输带宽、 源端和终端主机性能的限制。因此UDP协议吞吐量更大。

  • 简单

  • UDP程序结构较简单;

  • 流模式(字节)与数据报(http报文)模式 ;

网络层

为什么引入网络层

负责计算机节点之间的通信链路,不可靠。

互联网是由大量的异构(heterogeneous)网络通过路由器(router)相互连接起来的。互联网使用的网络层协议是无连接的网际协议(Intert Prococol)<IP报文解析>和许多路由选择协议<网络寻址>,因此互联网的网络层也叫做IP层。

数据链路层

数据链路层(data link layer)通常简称为链路层。两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层的协议。 在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装程帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。

在接收数据时,控制信息使接收端能够知道一个帧从哪个比特开始和到哪个比特结束。这样,数据链路层在收到一个帧后,就可从中提出数据部分,上交给网络层。 控制信息还使接收端能够检测到所收到的帧中有误差错。如果发现差错,数据链路层就简单地丢弃这个出了差错的帧,以避免继续在网络中传送下去白白浪费网络资源。

物理层

在物理层上所传送的数据单位是比特。 物理层(physical layer)的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。

觉得有收获的话帮忙点个赞吧,让有用的知识分享给更多的人

## 欢迎关注掘金号:五点半社

## 关注微信公众号:五点半社(工薪族的财商启蒙)##