原文链接:ghcljx72eo.feishu.cn/docx/Uylbdr…
前言
作为前端开发我们去了解这些知识的目的是什么?我想有两个典型的原因,一个是作为计算网络基础知识,会作为常识性的面试题。另一个是我比较在意的,为了理解网络框架设计原理便于提高日常开发的效率,与技术建设。
本篇我就从后一个角度出发,聊聊我的理解与实际应用。
数据链路层:网络连接的桥梁是什么
虽然网络是具有代表性的虚拟世界,但是这个虚拟世界却是完完全全由一个个真实的物理设备连接后组成的。那这些物理设备是如何连接的呢?既然是链接使用线吗?
答案是网线和无线信号,那么我们自己能实现一个局域网吗?当然可以而且非常简单。
记得在上大学的时候为了能在机房“摸鱼”联机打游戏,除了关闭监控软件以外我们还有另一个法子,就是用一根网线同时连接两台电脑,组成一个小型局域网完成联机。谁想到才刚学了一点计算机基础知识,就“学以致用(歪门邪道)”了。
这就是数据链路层(Link)的最基本形态,如果我们将上面例子中的其中一台换成路由器,就可以搭建出一个容纳多台设备的局域网,除此之外用交换机也可以。配合其他上层协议就可以实现端到端通信。
这是有线的,无线的则有WIFI与移动网络。在电脑与手机上都可以发送热点,让其他设备连接,这也组成了一个网域,热点设备将充当路由器。移动网络也叫蜂窝网络,手机根据通信协议连接到附近的基站,再由基站转发至运营商服务器,再访问公网网络,并将相应数据传回给手机终端。
最后回答一下网络连接的载体是什么?是以太网(网线),或无线终端网络通信桥。
到这里,算是确认了不同的物理终端设备有哪些方式可以完成连接,但仅仅是连上了还不能进行通信,在一个网域内总是连着多个设备,相互之间如何区分,做不到区分就做不到相互通信。
到这里这跟前端开发有什么关系呢?答案是确实没啥关系,数据链路层出现错误的直观表现就是设备提示没网络或没信号,远远到不了代码层面。
网络层:IP 像是被不断转接的客服电话
在前端开发中,更多的场景是根据域名或IP访问服务器。C端通常不会同时作为服务器对外开放访问。所以这里我就只做单方向的拆解。
IP的作用非常好理解,他们是所在路由器分配的在当前网域下唯一的ID,像是一个客服电话号码,用于呼出与被呼。不过与客服电话类似的是,IP与IP直接往往不是点对点直接通信,就行拨打客服电话需要转接一样,由中间服务在做转接与调配。
需要注意的是IP协议属于网络层,用于网络通信。在端与端之间可能隔着局域网路由器,运营商等中间路由器,目标服务器所属路由,最后才到目标服务器。下面的图会更形象一些:
在获取的目标主机的IP后(后文有如何获取的解释),设备将IP+数据包传递到路由器,路由器根据路由表查找通往目标设备的路径,根据查询到的路由器地址一层层像击鼓传花一样传递到目标主机。
这个环节内容对于前端日常开发有何意义?
我自己有一些理解,但我知道肯定有局限性,于是我问了ChatGPT。整体总结出与IP紧密相关的是下面两个方向:
- 网络调试:碰到请求延迟高,请求失败,使用ping,traceroute等工具来测试查找网络问题的根源
- 跨域问题:使用IP代理,CORS,JSONP等技术来实现跨域请求。我对于跨域并不熟悉,以后碰到了也许我会深入研究其实现。
传输层:为什么需要TCP,TCP具体做了那些事
网络链路建立好了,只要通上电,根据IP协议就能传输数据包了。但是网络是有速率的,收发需要读写时间,同时网络并不是稳定态相反是极其不稳定的,这就会造成数据传输不可靠。TCP便能在这种场景下提供可靠的网络传输。
通常大家都会“背诵”的三次挥手与四次挥手,这指的是连接建立于连接释放两个阶段。除此之外还包含:
- 数据传输:TCP协议会将数据包分成适当的报文段,逐个发送并逐个确认已被正确接收。这样做的好处是在传输失败时,缩小重传的范围,提高成功率。越大的包传输时间越长,传输失败的风险越高。
- 拥塞控制:每个TCP连接都有一个拥塞窗口大小(cwnd),它代表了网络中还未确认的数据量大小,也就是当前可用的网络带宽。TCP发送方在发送数据之前会先检查拥塞窗口的大小,如果拥塞窗口的大小小于要发送的数据量,则不能一次性发送所有数据,而是需要分批发送,并根据网络状况适当调整拥塞窗口的大小。更具体的则是对应的四个算法,这我段时间并不打算深入(慢启动,拥塞避免,快重传,快恢复)
- 流量控制:每个TCP连接都有一个发送方和一个接收方。发送方在发送数据时,需要知道接收方可以接收多少数据。接收方发送一个大小为接收窗口的信息告诉发送方自己的可用接收窗口大小。发送方会根据接收方的接收窗口大小来发送数据。如果接收方的接收窗口变小了,发送方会减少发送的数据量以避免数据拥塞。如果接收方的接收窗口变大了,发送方会增加发送的数据量以提高传输效率。
以上后两点copy自chatGPT🐶
主机建立TCP连接需要IP+端口,即TCP将切分好的包将通过IP协议发送到目标主机,并根据端口被对应目标应用服务接收。
日常开发中对TCP的理解与应用有两个点:
- 权衡减少冗余字段或使用Gzip等压缩技术,减少数据包体积,提高传输层效率与质量,提高网络服务稳定性。
- 网络连接问题排查,确认链路层,网络层连接是否正常,最后确认端口是否正确开放。当然也包括检查防火墙设置。
关于UDP(用户数据协议)则应用于游戏数据同步等不需要可靠传输的场景。游戏里看到队友瞬移就是最常见的延迟与丢包情况。服务器连发了10个有关用户移动距离的包,客户端只收到了1,2,10三个包,那就只会展示1,2,10三个状态给用户。
应用层:相对陌生的DNS(Domain Name System)
谈到应用层网络协议相对熟悉的是HTTP或者FTP,对于DNS似乎离日常开发比较远。
通常我们不是直接访问一个IP,而是访问一个域名。DNS则是用于解析域名并返回IP的服务。
这里引用一张团队小伙伴整理的DNS解析流程图:
值得注意的是根域名服务器,它并不负责解析IP。根域名服务器IP通常在硬件设备里由系统配置,目前全球根域名服务器一共13个,分布于全球各地。通过根域名服务器可以查询到对应的顶级域名服务器。顶级域名服务器在其收到请求后,会递归其下所有子域名服务器(权威域名解析服务器)解析一级域名,并将查找到的IP远路返回。通常这都很快,但是在国内访问一些国外网站会很慢,通过调整DNS服务器顺序可以轻度改善的原因就在这里。
掰扯完了DNS解析流程,再谈谈了解它对前端开发有何意义。
了解网络安全问题与应对策略
-
DNS劫持:当用户打开一个页面时,展示的并不是我们自己的页面而是另一个广告页面或其他网站页面。应对避免DNS劫持的办法:
- 可以避免使用运营商的DNS服务器(针对运营商劫持),比如使用Google的。
- 配置DNSSEC,DNSSEC是一种用于保护DNS数据完整性的技术,它可以防止DNS查询结果被篡改。如果域名的DNS服务器支持DNSSEC,可以通过在域名注册商处配置DNSSEC,从而提高域名的安全性。
- 使用HTTPS协议:HTTPS协议使用SSL/TLS技术对数据进行加密,可以防止DNS查询过程中的数据被窃听或篡改。在HTTPS中会校验证书+域名,即使DNS响应被篡改,用户被重定向到了恶意服务器,数据不会被泄漏,同时HTTPS连接建立失败,客户端也不会解析被恶意返回的数据包内容展示给用户。
-
优化网络性能:
- 理解CDN(Content Delivery Network)加速技术原理,用于提升网络服务治理
- 理解DNS容灾场景,优化网络服务质量。(当远程DNS解析失败时,为了让用户能正常访问,直接访问固定IP获取服务)
- 在Android中可以借助OKHttp很方便的实现这一机制。下面是代码示范:
-
class CustomDns : Dns { override fun lookup(hostname: String): List<InetAddress> { try { // 尝试使用默认的DNS解析 return Dns.SYSTEM.lookup(hostname) } catch (e: UnknownHostException) { // 解析失败时,使用备用IP return listOf(InetAddress.getByName("备用IP")) } } } val client = OkHttpClient.Builder() .dns(CustomDns()) .build()
我发现,作为前端开发按四层来理解要比OSI七层理解要容易得多,我能很好的将物理场景到网络场景到构建联系起来,并应用到实际场景中进行解释。
在日常开发中,碰到复杂的网络问题我们就需要借助对DNS/TCP/IP/Link层的理解来确认问题根源与解决手段,或者无法解决,是的存在无法解决的场景。比如我碰到过一次,部分上海移动用户无法访问公司的服务,复现后通过电脑连接手机热点(手机连移动),当时服务器很稳定,最后推测是运营商问题导致HTTPS建立连接失败,这促进了后续我们推进客户端网络容灾服务落地。
下面是当时的日志,未能确认其确切原因:
MacPro3@MacPro3deMacBook-Pro ~ % curl -v https://www.xxxx/xx.json
* Trying x0.x5.6x.x7:443...
* Connected to www.xx.com.cn (x0.x5.6x.x7) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
* CApath: none
* (304) (OUT), TLS handshake, Client hello (1):
* error:02FFF036:system library:func(4095):Connection reset by peer
* Closing connection 0
curl: (35) error:02FFF036:system library:func(4095):Connection reset by peer
最后我用扔物线(朱凯)老师课程中的截图来收尾:
浏览橘子树其他文章:橘子树的个人写作记录