一文搞懂计算机网络

什么是计算机网络呢?有人说:计算机网络就是百度!还有人说:计算机网络就是 HTTP!如果你也是这么想的,那么希望本篇你能仔细看下去。

那么,计算机网络到底是什么呢?

客观地说,计算机网络就是:一些相互连接的、自治的计算机的集合。

说人话就是:全世界所有通过互联网互相连接的计算机所组成的一个大网。

如果你的电脑联网了,那就是计算机网络的一部分,如果你的电脑没联网,那就不是计算机网络的一部分。

计算机网络简介

计算机网络可以分为:广域网、城域网、局域网和个人区域网。

  • 广域网:覆盖几十到几千公里,比如我们日常说的上网,用百度、谷歌等,用的就是广域网。

  • 城域网:一般覆盖一个城市,大约 5 到 50 公里,比如有的城市的宽带。

  • 局域网:一般覆盖一公里左右,比如一个公司的内网。

  • 个人区域网:覆盖范围只有几米,比如自己给自己开热点。

有人说,这些我都不在乎,我就在乎它快不快,看视频卡不卡,这就要提到网络的质量参数了。

判断一个网络的质量有 7 个性能指标。

  • 速率:这个很好理解,就是传输速度。直接受到材料的影响,比如,相同条件下有线网肯定比无线网快;而同是有线网络,光纤就比电缆快。

  • 带宽:可以简单理解为传输的横切面大小。我们把计算机网络的传输看作是一个水管,速率就是传输的速度,带宽就是水管横切面大小,越大传输得肯定越快。

  • 吞吐量:单位时间内通过网络的数据量。这才是真正的传输速度,如果水管生锈了,那么速率是 100,也达不到 100,因为水锈会阻碍一部分传输,而吞吐量就是把所有因素都考虑进去之后,得出的最终的传输速度。

  • 时延:把数据从 A 端发送到 B 端所需要的时间。

  • 时延带宽积:发送数据时的延迟 x 带宽得出的结果。

  • 往返时间:从 A 发数据到 B,到 A 收到 B 的响应所需要的时间。

  • 利用率:网络在传输时信道的利用百分比。

我们把传输信号的介质统一称为:信道。

我们知道,网络传输的其实就是信号,或者说是电磁波,电磁波可以直接在空气中传播,可以在电缆中传播,也可以在光纤中传播。所以,空气、电缆、光纤等,都是信道。

我们传输的数据,也就是电磁波,从微观看就是一个一个的电子,电子在金属中的传输速度大于在空气中的传输速度,并且空气中有其他信号干扰,所以有线网就比 Wi-Fi 快。

那么,这些电子就这样传送,它传送的什么玩意儿呢?

只要我们制定一个规则,然后大家都按照这个规则来传递,就都明白了。

这个规则就叫做:计算机网络协议。

计算机网络协议

计算机网络协议是指:为计算机网络中进行数据交换而建立的规则、标准或约定的集合。

比如,我们前面说过的 HTTP,也是计算机网络协议中的一部分,属于应用层协议。

应用层?还分层吗?

分,就像我们的代码也分内核层、Native 层、Framework 层、应用层一样,网络协议也是分层的。

分层就是为了简便,各司其职,分工明确,容易维护。

我们一般把网络协议分为 5 层:物理层、数据链路层、网络层、传输层、应用层。当然,也有其他分法,我们这里就不叨叨了。

这里要理清一个流程,比如 A 向 B 发数据,数据是从 A 的应用层开始,沿着传输层、网络层、链路层、物理层向下,再依次到达 B 的物理层、链路层、网络层、传输层,最后到达 B 的应用层。

就像 A 给 B 送信,A 住在 5 楼,B 住在对面的 5 楼,A 先要下到 1 楼,然后再从 B 住的楼上去,才能送达。

物理层

物理层规定传输媒介的一些特性。

比如说,我们上面说到的电缆和光纤都是传输媒介,当然还有其他的,比如无线信道、同轴电缆等。物理层指的不是这些实实在在的物理上的传输媒介,而是规定它们的协议。那么,这些传输媒介有什么共同点呢?可以分为四个点。

  • 机械特性:指明接线器的形状和尺寸、引脚数目、排列方式等。

  • 电气特性:指明每条线上的电压范围。

  • 功能特性:指明每条线上每一个电平表示的意义。

  • 过程特性:指明各种事件的出现顺序。比如,哪根信号线先动,哪根信号线先出现什么电平等。

换句话说,物理层协议将具体的实实在在的传输媒介进行了高度总结,抽象成了四个特点,只要满足这四个特点,就可以作为传输媒介。这样一来,我们不必去死记硬背到底哪些属于传输媒介,而只需要理解这四个特性,只要满足这四个特性的,就可以作为传输媒介。这就是化记忆为理解的手段之一:找共性并总结抽象。

这跟面向对象中的将具体事物提炼成抽象类有异曲同工之妙。

一般来说,我们的信道有三种。

  • 单向通信:也叫单工通信,只能从一端到另一端通信,而不能反过来。比如我们收听的收音机广播。

  • 双向交替通信:也叫半双工通信,双端都可以收发,但不能同时进行。

  • 双向同时通信:也叫全双工通信,双方可以同时发送和接收。

现在,我们有了物理层,可以传输电子信号了,那么电子信号是怎么被识别成数据呢?又怎么保证可靠地到达另一端呢?

数据链路层

数据链路层负责将数据透明地、无差错地进行传输。

我们知道,传输媒介传输的是信号,或者说就是电子,那这怎么可能看得懂呢?很简单,我们传输的电子都会通过电路展示为高低电平,我们用高电平表示 1,低电平表示 0,这样就被翻译成了一串串二进制,而数据链路层就负责把这些二进制封装成帧,你可以将帧理解为一串二进制。

那么,在数据链路层我们的传输单位就从电子信号变成了帧。那么,要怎么保证这些帧正常地到达呢?这就是数据链路层的核心作用了。

首先,链路层会把我们的数据封装成一个一个的数据帧,说白了就是在数据前后添加标记,我们称之为首部和尾部,这样,当接收端收到数据后,就能首部和尾部的标记,来识别数据的开头和结束。

当然,我们传输数据的时候,不会感知到首部和尾部的存在,这是链路层自己添加的,在发送的时候自己添加,在接受的时候自己删除,这对我们来说是透明的。

其次,链路层会对发送的数据进行差错检测,比如最常见的循环冗余检测,如果遇到错误的数据帧,就会丢掉并重传,以此来提供可靠的、无差错的数据传输。

所以,链路层的功能就是:将数据封装成帧,进行透明地、无差错地传递。

那么,我们怎么知道要传输到哪里呢?数据怎么知道对方的地址呢?

网络层

网络层负责编址、寻址和转发。

网络层的核心功能就是寻址,也就是寻找地址。

我们知道,我们发送数据都需要一个 IP 地址。IP 就是一个协议,全称叫做 Internet Protoco,叫做国际互联协议,它可以表示一个地址,比如常见的 192.168.xxx.xx,叫做 IP 地址。

那么,这样的地址怎么能找到对方的主机呢?

我们可以通过地址映射协议,也叫做 ARP(Address Resolution Protocol) 来根据 IP 地址找到对应的主机地址,从而将数据塞给这个主机,或者从这个主机上拿数据。

主机地址是不变的,IP 地址是随着网络变化的。

比如:我的主机叫大灰狼,我连了我家的 wifi123,那么,我连了网,就会自动给我的主机分配一个 IP 地址,假如地址是 192.168.1.1,那么,隔壁喜羊羊就可以顺着这个地址来找到我的主机大灰狼,从而给我发消息;如果我换了另一个网络,我的主机又被重新分配一个新 IP 地址,那么,喜羊羊用老的 IP 地址就找不到我了。

这也就是我们连接不同的网络时,执行ipconfig查看到的 IP 地址不同的原因。

IP 地址有了,我们可以买个域名,比如 https://www.yyds.nb,然后将这个域名绑定到自己的 IP 地址,那么别人访问这个域名的时,就会访问到你的 IP 地址,这个过程叫做域名映射,跟上面的地址映射是异曲同工的。

所以,网络层的两个核心协议就是 IP 和 ARP,IP 协议负责给不同的主机分配 IP 地址,ARP 协议负责根据 IP 地址找到实际的主机地址。

那么,地址知道了,我们怎么传输数据呢?直接硬塞吗?非也!

传输层

传输层的核心作用就是进行数据的传输。

上面说到,我们通过网络层拿到了具体的主机地址,那么,数据传给谁呢?直接塞到系统的 C 盘吗?

肯定不会,应该是传给这个主机中的一个进程,这个进程收到数据后就进行数据的解析处理。那么,数据是怎么被进程获取到呢?

通过端口号!

每个主机上都有不同的端口号,我们可以开启一个进程去监听这个端口,只要监听到这个端口有数据,我们就立刻处理,这样就拿到了数据。

也就是说,数据传输不仅仅需要 IP 地址,还需要有端口号,比如常见的192.168.0.1:8080,其中 8080 就是端口号。最常见的就是 Socket 了。

数据传输有两种传输方式,一种是面向连接的,可靠的 TCP 协议;另一种就是无连接的,不可靠的 UDP 协议。

  • TCP(Transmission Control Protocol):面向连接的,可靠的。

  • UDP(User Datagram Protocol):无连接的,不可靠的。

照这么说,UDP 不就没啥用了吗,不可靠要你干啥?

存在就是道理!

TCP 虽然可靠,但是每次通信都要先建立连接,完事之后还要断开连接,就像打电话一样。

累不累?这个美其名曰三次握手四次挥手。因为提前都互相问了对方能听到吗,所以是可靠的。但是我们也看到了,太费劲!

A:喂,能听到么? B:嗯,可以,And you? A:me too,好,现在我们开始吹。 1 个小时之后要结束了。 A:今天就到这里吧。 B:好的,就到这里吧。 A:好的,挂了。 B:好的,我也挂了。

如果是 UDP 呢?就是如下这样。

A:大风车啊咿呀咿呀转,xxxxxxxx,收到回复! B:嗯!

就这么简单,就跟发短信似的,但是可能 B 没收到,所以不可靠。

所以,我们可以总结一下:TCP 需要连接,费事,但是可靠;UDP 不需要连接,省事,但是不可靠。

应用层

应用层直接给用户提供傻瓜式的服务。

这一层最常见的就是 HTTP 了,也叫做超文本传输协议(Hyper Text Transfer Protocol),大部分都是用来传递 JSON,也可以传递 HTML 页面,所以叫做超文本。

HTTP 是基于 TCP 的,所以也是需要连接的。

一次完整的 HTTP 请求如下,比如我们请求https://xxx.cn。

  1. 通过域名映射找到https://xxx.cn对应的 IP 地址。

  2. 通过这个 IP 地址加上默认端口 80 进行 TCP 连接。

  3. 发送数据请求。

  4. 等待对方主机返回响应结果,也就是一个 HTML 页面。

  5. 得到结果,放在浏览器里面进行渲染,我们就看到了具体的页面。

  6. 关闭 TCP 连接。

关于 HTTP 就不做过多讲解,应用层还有很多其他协议,比如文件传输协议 FTP、电子邮件协议 SMTP。

凡是 P 结尾的,大部分都是协议或原则。

比如,单一职责原则 SRP、开放闭合原则 OCP 等。

其实对于我们来说,我们真正需要了解的就两层,一个是应用层,一个是传输层。

应用层的 HTTP、传输层的 TCP,这俩协议是用得最多的,基本上霸占了“皇后”和“贵妃”的位置,而 HTTP 又是基于 TCP 实现的,那我们就追其根溯其源,剥其衣脱其裳,来彻底了解下这位面试官最喜欢打听的协议吧。

TCP 可靠传输的实现原理

之前我们说过:TCP 的传输是可靠的。那么,这个“可靠”是啥意思呢?其实它包含两个点:

  1. 传输的数据没有差错;

  2. 传输的数据不会丢失。

有人说,没有差错肯定就不会丢失啊,第 1 点包含了第 2 点啊。

未必。

我们举个例子,我一秒给你发 100 条信息,并且全发到了,这是没有差错;但是你来不及看完,也就是有漏看,这就是丢失了。

所以,我们可以定义得更精简一点:传输的数据没有差错并且全部被处理。

这怎么可能呢?网络信号肯定有不好的时候,比如隔壁有人下载东西下载得太猛把网下欠费了又不去缴费,那这不就丢失了?你怎么保证可靠?

可以的,我们只需要保证两点:

  1. 传输出错的数据进行重传;

  2. 控制传输速度,让接收方来得及处理。

这样,即使原来不可靠,也会变得可靠了。

要实现这两点,我们要了解以下几个协议。

超时重传协议

发送方在发送数据之后的规定时间内,如果没有收到接收方的确认,则重新传送数据。

现在假设 A 向 B 发消息,如果网络正常,就是 A 向 B 发送消息,B 正常收到。

但是如果网络异常呢?A 向 B 发送之后,A 怎么知道 B 是否收到了呢?

这就需要 B 在收到 A 的消息后,向 A 发送一个确认信号,A 收到了 B 的确认信号,才认为 B 正确地收到了 A 发送的消息。

所以,如果网络正常,那么流程就变为:

  1. A 向 B 发消息,并等待 B 的确认;

  2. B 收到 A 的消息,发送确认消息给 A;

  3. A 收到 B 的确认消息,认为 B 正常收到了,继续发送下一个消息。

那么,如果 A 迟迟没有收到 B 的确认消息呢?比如,双方约定了一个时间 T,A 在发送消息后,经过时间 T 还没有收到 B 的确认消息,那么 A 还继续等吗?

不等了!此时 A 就认为 B 没有收到自己发送的消息,于是就重新发送一次,直到收到 B 的确认消息为止。

这就叫做超时重传。那么这个时间又是多少呢?

我们假设,A 发送消息到 B 需要的时间为 t1,B 处理消息需要时间为 t2,B 发送确认消息到 A 需要的时间为 t3,那么这个超时时间 T 肯定要大于 t1+t2+t3。而且考虑到通用性,我们应该计算多次 t1+t2+t3,并且取它们的平均值,而 T 就要大于这个平均值。

当然,有人说了,你这也不准啊,网络可能有时候很差,有时候很好,你这怎么决定呢?

所以,我们要动态计算这个超时时间,我们在上一篇文章讲过自旋锁,自旋锁可以根据具体情形进化为自适应自旋锁。这里也是一样的道理,我们可以根据当前情形动态计算超时时间 T,比如:根据最近的 20 次传输时间取平均值。

其实,核心就是动态计算,而不是固定死的一个值。

当然,超时重传这个操作 TCP 已经帮我们做好了,我们在 API 层是无感知的,无脑调用 API 就行。所以也叫做自动重传递协议,术语就是 ARQ(Automatic Repeat reQuest)。

ARQ 保证我们的数据是无差错地传输到另一端,那么,怎么保证另一端能来得及处理呢?

这就要说到停止等待协议。

停止等待协议

发送方会调整发送速度,来等待接收方处理完之后,再进行发送。

其实,我们上文已经说过了,A 发送一条消息之后,直到收到 B 的确认消息,才会继续发送下一条消息,就好像是 A 等着 B 似的,这就叫停止等待协议。这样一来,B 总是在自己处理完一条消息之后才发送确认信号给 A,A 总是在 B 处理完一条消息之后才发送下一条消息给 B,说白了就像一条隧道,A 开过去,B 再开过来,A 再开过去,如此循环。

等等,这不对劲啊!我们的 TCP 是全双工啊,你这样活脱脱地把它完成了单工通信了,这岂不是白白浪费资源吗?

比如 B 在发送确认消息到 A 的过程中,A 就干等着?A 在发送消息给 B 的过程中,B 也干等着?如果网络不好,A 发送消息发了 10 分钟还没到,B 就等 10 分钟?

我们可以这么干:我们直接在发送端 A 和接收端 B 都开一个缓冲区,A 每次发送的消息都放在自己的缓冲区中,B 每次收到的消息也先放在自己的缓冲区中,A 只要缓冲区不满就继续发消息,直到缓冲区满为止;B 就循环从缓冲区取出消息处理,处理完就发送确认信息给 A,同时继续处理下一条消息;A 收到 B 的确认信号就从缓冲区移除对应的消息,移除之后缓冲区就不满了,就继续发送下一条消息。如此循环,直到消息全部发送完毕为止。

这不正是生产者消费者模式吗,没错!

如此一来,我们的信道可能一直处于忙碌状态,大大提高了信道的利用率。假如,此刻,A 正在给 B 发送第 10 条消息,而 B 正好给 A 回复第 3 条消息的确认信息,此时信道中就同时出现 A 给 B 发、B 也给 A 发的情景,这正是全双工的表现。

那么,这样的话,就不是停止等待了,那这怎么保证对方来得及处理呢?

不,这仍然是停止等待,只不过等待的不是一条消息了,而是一堆消息了。换句话说,没有缓冲区的停止等待,就像有缓冲区但是缓冲区的大小是 1 的情况。有缓冲区的停止等待,等待的是什么呢?等待的是缓冲区不满,所以,当缓冲区满了,照样等待。

但是如果没有缓冲区的停止等待,如果消息错了,我就重新发送,对方再重新处理就行了,而有了缓冲区后,如果中间一条消息出错了怎么办呢?后面的是不是全乱了?

对!后面的肯定都乱了,所以,后面的全部重新传送一遍即可!

比如,缓冲区大小是 5,A 发送了 12345,B 收到了 1245,没收到 3,那么,A 迟迟收不到 B 对 3 的确认信号,A 就会将 345 全部重新发送一遍,直到收到 3 的确认为止。

那如果 B 没收到 3,直接对 4 进行了处理,然后给 A 发送了 4 的确认呢?A 会认为这个确认无效,因为没跟上一个确认连上,所以不做处理。

有人又说了,你这如果中间丢了一个消息,就要把后面全部传送一遍,这效率肯定低了啊。

对,效率肯定低了,但是你想啊,我们的网络大部分都是好的,只有很少一部分时间是异常的,所以,这个选择肯定是利大于弊的。如果真的网络差,那也没办法,效率低是低了,但是消息还是正确的。总而言之,我们权衡利弊之后,还是选择带有缓冲区的停止等待协议。

其实,这个就叫做连续 ARQ 协议,大大提高了信道利用率,提高了传输效率。

连续 ARQ 协议

我们上面说了,使用加缓冲区的停止等待协议,就是连续 ARQ 协议,这其实就是采用流水线方式进行连续发送,提高信道利用率,而不用依次等待上一条消息的确认,毕竟异常的情况是少数,我们就默认是正常的,如果发现错误,再从错误的地方重新发送即可。

其实,连续 ARQ 协议是采用滑动窗口实现的,我们的缓冲区就等于一个窗口,如下所示:

我们假设缓冲区的大小是 5,那么这个窗口的宽度也就是 5,我们把沿着滑动方向的两条边分别称为前沿和后沿,前沿后沿的差值就是缓冲区的大小;刚开始时,TCP 会将窗口内的消息全部发送,当收到一个确认信号时,窗口会整体向前移动,新进入窗口的消息就会被发送。

比如,图中所示,如果收到了消息 1 的确认,那么后沿就向前移动一个单位,如下:

此时,6 进入了窗口,6 就会被发送。如果此时收到了 2 的确认信号,窗口就继续向右边移动,直到全部确认为止。

窗口内的数据都是有缓存的,如果收不到确认,就会把窗口内的数据重新发送一遍。比如,如果超时没有收到 2 的确认,那么就会把窗口内的 23456 全部发送一遍(反正有缓存);如果收到了 2 的确认,那么窗口整体右移,2 就被移出窗口,因为 2 不需要发送了。

这个过程就像窗口,所以也叫做滑动窗口。换句话说,连续 ARQ 协议是基于滑动窗口的思想实现的。

好,到这里,TCP 的数据传输我们都了解了。那么,TCP 是怎么建立连接的呢?

三次握手与四次挥手

TCP 是怎么建立连接的呢?

TCP 的连接采用 CS 模式,也叫做客户端-服务器模式,主动发起连接的叫客户端(C 端),被动等待连接的叫做服务端(S 端)。

刚开始客户端和服务端都处于关闭状态,当需要建立连接时,客户端会主动打开连接,然后请求服务器建立连接,这中间有三次握手的操作,如下。

  1. 客户端:服务器吗,我需要建立连接(第一次握手)。

  2. 服务器:我知道了(第二次握手)。

  3. 客户端:那还不赶紧打开(第三次握手)?

  4. 服务器打开连接(建立连接)。

那么,为什么要三次握手呢?首先,不握手肯定是不行的,一次握手也是不行的,因为都不能保证对方收到了自己的请求,要保证对方收到了自己的请求,至少要两次握手,一次是请求,一次是对请求的确认。

我们知道,TCP 是全双工的,如果只有两次握手,那么结果就是客户端发送、服务器确认,这只能保证服务器能接收到客户端的消息,而不能保证客户端能接收到服务器的消息,这是不可靠的。所以我们需要三次握手,直到第三次客户端的应答过来了,才能保证客户端确确实实收到了服务器的确认信号,也就是保证了双方都能互相接收到对方的消息,也就保证了可靠性。

还有一点,就是可以避免浪费资源。比如:第一次握手时由于网络问题,发出的信号迟迟没有到达服务器,于是,由于超时重传,客户端再次发送一个连接信号,此时,网络中有两个连接信号了;如果这时候网络突然好了,那么服务器就会一下收到两个连接信号,如果是两次握手,服务器此时就直接打开了两个连接。这两个连接中必定有一个是无用的,白白浪费资源。

如果是三次握手呢?服务器就不会打开连接,而是发送两个确认信号给客户端,客户端收到第一个确认信号时,就告诉服务器打开连接(第三次握手),收到第二个确认信号时,由于已经收到过相同的信号了,就认为是重复的,就直接丢掉。这样服务器就只会收到一个第三次握手的信号,只会建立一个连接,从而避免浪费资源。

有人说,你这不就是让客户端判断是否有重复信号呗?你放在服务器不是也可以吗?

不可以!你要知道,服务器是面向多个客户端的,如果要判断是否有重复信号,那就要保存每一个客户端的请求信息,如果有 10 亿个客户端,这要保存多少信息呢?这个代价太高了,不如分摊到客户端,让客户端自己记录,这样代价低一些。所以,服务器适合做广播,而不太适合做单播。

所以,我们可以归纳三次握手的两个点:保证可靠,避免浪费资源。

那么,TCP 是怎么断开连接呢?

流程大致如下:

  1. 客户端:我要断开了。

  2. 服务器:好的。

  3. 服务器:我也要断开了。

  4. 客户端:好的。

能不能干净利落点直接断了,就跟断开网线一样?

这肯定不行啊,如果直接断了,这到底是正常断开呢,还是网络异常了呢?所以我们需要通知对方,这就需要两次握手。而我们又知道,TCP 是全双工的,客户端到服务器断了就表示客户端不再向服务器发送消息了,但是,服务器依然可以向客户端发消息啊,因为你是全双工啊。所以,如果要彻底断开,还需要服务器发起一次握手,客户端进行一次应答,这样才能让这个全双工连接彻底断开。这就一共需要四次挥手。

有人说,那你连接的时候为啥三次就行?因为握手的时候,第二次握手的过程同时包含了服务器的应答和服务器的发送,等价于做了两件事,比如:

A:你能听到吗?

B:我能听到。(这句话说明 B 不但收到了 A 的消息,还表明了 B 能发送消息。)

A:好的。(这句话证明了 B 发消息成功,还证明了 A 能收到 B 的消息。)

UDP 协议

UDP 是无连接的、不可靠的,所以没什么可以扯的太多的东西,这里就不废话了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值