基础面试题 — 计算机网络

3,840 阅读16分钟

有感而发

作为计算机相关专业的必修课之一,计算机网络在编程中的地位从未被超越。整个社会都在高度依赖网络所提供的各种服务,我们一边享受网络所提供的便利,一边也在通过网络成为被贩卖的对象,网络安全的重要性,日益凸显。此外,如果太依赖于网络,天天生活在网络中,在一定程度上也会削弱我们在实际生活中的各项技能,因此,审时度势,合理利用网络,方能让我们活得更出彩。如果我们告别各种电子设备,回归到煤油灯的生活,想想每天都有大把的时间,可以做……也可以做……(反正不会熬夜)。

计算机网络体系结构

计算机网络的体系结构也就是我们熟知的七层网络模型或五层网络模型,接下来从下往上大致介绍下各个层级及其作用:

  • 物理层:其任务是规范物理传输媒介相关接口的特性(可理解为把光电信号转换成0或1比特数据,根据0或1比特数据转换成对应的光电信号);
  • 数据链路层:在相邻节点之间透明传送被封装成帧的IP数据报(封装成帧,透明传输);
  • 网络层(IP层):通过选择合适的路由,为网络系统中的主机提供通信服务(路由选择,地址解析等);
  • 传输层:通过传输层的协议,为不同主机间的应用进程提供通信服务(通过端口区分进程);
  • 会话层:负责处理不同主机进程间会话的建立、控制和关闭(会话管理,数据流同步);
  • 表示层:负责根据数据格式、编码、压缩和解压缩、加密和解密,转换计算机和网络通信间的数据格式;
  • 应用层:为用户应用程序提供通信服务,保证通信双方的可用性和数据的完整性;

一些关键词

  • ping:ICMP网际控制报文协议,一般使用ICMP协议的回送请求和回送应答测试两个主机之间的连通性;
  • MTU:Maximum Transfer Unit最大传送单元,即IP数据报,链路层所允许的最大传输单元;
  • MSS:Maximum Segment Size最大报文段长度,即传送到IP层不需要分片的长度;

差错检验

  • 传输层:对数据段和头部整体校验(IP头部长度:TCP20字节,UDP8字节);
  • 网络层:只校验首部(IP层首部20字节);
  • 链路层:比特差错校验(校验0和1);

TCP和UDP的区别?

协议 面向连接? 可靠交付? 点对点? 拥塞避免? 流量控制? 面向字节or报文?
UDP 无连接 尽最大努力但不保证可靠 一对一、多对多、多对一、多对多 不支持 不支持 面向报文
TCP 面向连接 保证可靠 只支持一对一 支持 支持 面向字节流(会对字节进行编号)

TCP 为何要三次握手和四次挥手?

TCP是面向连接的,所以在数据传输之前要先建立连接,3次握手是为了建立连接,数据传输完毕后必须要释放连接,4次挥手就是为了释放连接。

  • 三次握手(简化版)
    1. 服务端创建传输控制块TCB,准备接收客户端的连接请求并进入LISTEN(收听)状态,客户端创建TCB后向服务端发送连接请求报文;
    2. 服务端收到后随即发出确认报文(此时还没建立连接);
    3. 客户端收到服务器端的确认后再发出一次确认报文,服务端收到后建立连接,握手结束。

PS:客户端最后发送的那次确认主要是为了防止已失效的连接请求报文突然又传送到服务端,因而产生错误和导致服务端资源浪费。

  • 四次挥手(简化版)
    1. 客户端应用进程先向其TCP发出连接释放报文并停止数据的发送,进入终止等待状态
    2. 服务端收到后随即发出去确认并进入关闭等待状态,客户端收到确认后进入终止等待状态2,若服务端仍有数据需要发送给客户端,客户端仍然需要接收。
    3. 服务端确认没有需要向客户端发送的数据后,服务端应用进程通知TCP释放连接,并进入最后确认状态,等待客户端的确认
    4. 客户端收到服务端的连接释放的报文后,向服务端发出确认,服务端收到确认后关闭连接,客户端在等待最长报文寿命结束后也关闭连接(若服务端超时没有收到确认,则需要重发释放连接的请求,此时客户端会继续发出确认并重新开启一个等待计时器;防止已失效的连接请求报文出现在本连接中)

TCP 如何保证可靠的?

TCP协议主要是通过确认和重传机制来保证可靠交付,TCP会对报文数据进行编号,每个报文段都有起始编号和尾部编号,一个报文段可以理解为一个发送窗口的大小,也就是发送方来在未收到对方确认的情况下可以连续发送的报文大小。

  1. 对于发送方来说发送报文段数据时,同时开启超时计时器,在未收到确认之前都会暂时保留一份副本,发送方经过一段时间后(由超时计时器控制)就重传这部分数据(可能是发送方发送的报文超时,也可能是接收方发送的确认超时),并重新设置超时计时器,直到收到确认为止。
  2. 发送方接收到确认(已收到的报文号段和期待下次接收的报文号段)后,移除保留的对应的报文段副本(从发送缓存中移除),并移动发送窗口,发送新的数据。
  3. 接收方把接收到的报文段放到接收缓存中,包括按序到达的、但尚未被接收应用程序读取的数据和未按序到达的数据。接收方等到字节流中所缺少的字节收到后,再按序交付给上层的应用进程。
  4. 接收方如果收到的分组被检测出有差错或重复的数据就会丢弃,接收方可能会有缺失的报文段,对于接收方来说没有收到就不会发送确认,此时发送方会重传未被确认的报文段。

TCP 流量控制

流量控制就是让发送发的发送速率不要太快,要让接收方来得及接收。假设A向B发送数据,在连接建立时,B告诉A我的接收窗口rwnd="400"(rwnd表示receiver window,字节单位),假设一个报文段长度是100字节,A发送了序号1-100的报文段,还能发送300字节,A发送序号101-200报文段,还能发送200字节,此时B发送了序号1-100报文段和rwnd=300字节的确认报文,此时A可发送的大小就是300字节,如果A已发送了400字节,就不允许A再发送数据了,允许A超时重发旧的数据,但不允许A发送新数据,总的来说就是接收方控制发送方发送数据的速率,以便接收方来得及接收。

TCP 拥塞控制

TCP主要通过慢开始和拥塞避免进行拥塞控制。在不清楚网络负荷的情况下,若把大量数据发送到网络,那么就可能引起网络拥塞,所以会先探测一下,即从小到大逐渐增大拥塞窗口(等同于发送窗口),当网络出现拥塞再把拥塞窗口减小一些,以减少注入到网络中的分组数。

  1. TCP初始化时会把拥塞窗口设置为一个MSS大小,并初始化慢开始门限(调节发送窗口大小)的值,在每收到一个对新的报文段的确认后,把拥塞窗口增加至多一个MSS的数值(1变2,2变4,4变8),用这样的方法逐步增大发送方的拥塞窗口cwnd,使报文注入到网络的速率更加合理。
  2. 随着传输轮次的增加,拥塞窗口的大小曾指数级增长,当拥塞窗口的大小等于慢开始门限的值时,此时会从慢开始切换到拥塞避免,可以理解为把拥塞窗口之前的指数级增长改为线性缓慢增长(经过一个往返时间拥塞窗口仅增加一个MSS大小)。
  3. 随着拥塞窗口的逐渐增大,当检测到报文超时(在超时计时器内没有收到确认),就把慢开始门限的值更新为出现拥塞时拥塞窗口大小的一半,同时把拥塞窗口的大小重新置为一个MSS,并开始执行慢开始算法,如此往复。

HTTP的组成部分

HTTP由三部分组成:开始行、Header和Body体;

  • 开始行:用于区分是请求行还是响应行
    • 请求行:Method URL HTTP/Version,如:GET /Joelixy/IVAlgorithms HTTP/1.1;
    • 状态行:HTTP/Version StatusCode StatusMsg,如:HTTP/1.1 404 Not Found;
  • Header
    • Request Header(一般有如图所示字段,也可自定义)

    • Response Header(具体请参考下图)

  • Body
    • 一般的POST请求只有相关参数,上传文件的POST请求会包含被编码的文件数据。

上传文件的HTTP请求

  • —$boundary:可以认为是Body的边界
  • Content-Disposition:用于指定呈现方式,如:from-data:name="txt"
  • Content-Type:
    • application/x-www-form-urlencoded:用来提交文本,不能上传文件,用$_POST[]接收
    • multipart/form-data:用来提交表单数据,用$_FILES接收
  • Content-Transfer-encoding:默认的编码行不通时可指定编码方式
  • 数据:被上传的编码后的数据

GET和POST的区别

  1. GET从服务端获取数据,POST向服务端传送数据
  2. GET参数添加到表单的ACTION属性所指的URL中;POST中将字段及其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址,用户看不到这个过程。
  3. 服务端用Request.QueryString获取变量的值,用Request.Form获取表单中的数据
  4. GET传送数据量较小,2KB以内,POST不受限,主要取决于服务端的限制
  5. 安全性不一样,GET请求直接暴露给用户了,且还可能被服务器记录,有泄漏风险;POST的所有操作对用户都是不可见的

网络安全

密码体制

主要有两种,一种是对称秘钥密码体制,另一种是公钥密码体制。

  • 对称密钥密码体制:即加密与解密都是相同的密码体制,加解密速度快,但是若不对密钥分发的过程加以保护,密钥泄漏的风险还是很高。
  • 公钥密码体制:使用不同的加密密钥和解密密钥,即公钥和私钥,公钥是公开的,私钥是保密的,公钥加密的数据只有私钥能解密,私钥加密的数据只有公钥能解密。

密钥分配

由于密码算法是公开的,所以网络的安全性就完全基于密钥的安全之上,因此密钥的管理和分发就显得无比重要了。目前通用的做法是:

  • 用公钥分发:通过公钥对临时会话产生的对称密钥进行加密,然后进行分发,私钥解密后,双方就按照约定的加密方式进行加解密,对称密钥对数据加解密,公私钥用于对报文鉴别。
  • 双向验证:通信的双方都有自己的公钥和私钥对,通信时通过证书的解析得到对方的公钥,然后双发都有了自己的私钥和对方的公钥,这样就可以互相解密对方的加密数据,只是这种方式效率太低,不适合频繁的网络请求场景。

数字签名

签合同的时候,我们都需要进行签名,就是为了保证其真实性,通过验证签名来表明这份合同的真实性。数字签名就是为了保证这个数据是某个发送者发送的,因为只有它才可以有这样的签名,所以数字签名必须保证三点功能:“报文鉴别、报文的完整性和不可否认”。

  1. 报文鉴别:接收者能够核实发送者对报文的签名,也就是说,接收者可以鉴别该报文的确是发送者发送的,其他人无法伪造对报文的签名。
  2. 报文的完整性:接收者确信所收到的数据和发送者发送的完全一样,没有被篡改过,这就叫报文的完整性。
  3. 不可否认:发送者事后不能抵赖对报文的签名。

HTTPS如何对报文鉴别来保证数据的完整性?

  1. 以客户端和服务端为例,服务端在发送报文时,会通过报文摘要算法对报文进行计算得到报文摘要,然后服务端用自己的私钥对报文摘要进行签名,最后把签名后的报文摘要追加到报文后一起发送给客户端。
  2. 客户端在收到服务端的报文后,把签过名的报文摘要和报文进行分离后,会做两件事,(1)客户端用服务端的公钥对签过名的报文摘要解密得到报文摘要,(2)对接收的报文进行报文摘要计算,也会得到一个报文摘要。客户端对比两个报文摘要,若相等则说明收到的报文就是服务端发送的,相等也说明数据没有被篡改过,否则说明数据被篡改或不是服务端发送的。
  3. 同理,客户端发送报文时也一样,用自己的公钥对报文摘要签名,然后追加到报文后发送给服务端,服务端用私钥解密,并做同样的事情,来对报文进行鉴别。

HTTPS的握手流程

  • 客户端:向服务端发送clientHello以及支持的SSL版本号和加密套件、随机字符串(后面协商对称秘钥的时候需要)
  • 服务端:向客户端发送ServerHello以及支持的SSL版本号和选择的加密套件、随机字符串和服务器的证书(被CA机构的私钥签名过的证书,有RSA公钥、域名、版本、签名使用的加密算法等,若需要双向验证,则需要客户端也提供相应的证书,此处只考虑单向验证)
  • 客户端:支持HTTPS的一般都有个可信的CA表(内置客户端或系统中),表中有CA的公钥(CA认证中心的公钥都是公开的)。客户端收到服务端的证书时就检查此证书的发行者是否在自己的可信CA表中。若不在则无法建立加密连接,若在就使用CA表中相应的公钥对证书校验并解密得到服务端的公钥。然后用公钥加密客户端产生的随机字符串并发送给服务端,服务端收到后用私钥解密得到随机字符串,此时双方都有三个字符串,会用约定的加密方式加密随机字符串生成对称密钥。
  • 客户端:向服务器发送一个报文,说明以后客户端将使用此会话对称密钥进行加解密。然后客户端再向服务器发送一个单独的加密报文,表明客户端的握手过程已完成。
  • 服务端:也向客户端发送一个报文,说明以后服务端将使用此会话密钥进行加解密,然后服务端再向客户端发送一个单独的加密报文,表明服务端的握手过程已完成。
  • 此时SSL握手已经完成,下面就可以开始SSL的会话;

淘宝页面发送HTTP请求的过程

  • DNS解析(查找过程:本地域名Server(DNS高速缓存)->根域名Server->COM顶级域名Server
    • 先查找浏览器的DNS缓存->系统的DNS缓存->DNS服务器(先查找自己的DNS缓存->根域名Server->COM顶级域名->域名注册商
    • 如:www.google.com域名,真实的域名是www.google.com.多一个点,浏览器向DNS请求时会自动添加该点,表示根域名服务器。
    • DNS缓存:浏览器缓存-系统缓存-路由器缓存-IPS服务器缓存-跟域名Server-顶级域名Server-主域名Server
  • TCP建立连接
    • 拿到域名IP地址后,客户端以一个随机端口向服务器的Web程序80端口发起连接请求,连接请求经过下面4层网络模型层层封装到达服务端,进入到服务端的内核的TCP/IP协议栈,识别连接请求层层解开数据报,找到80端口到达Web程序,
  • 发送HTTP请求(三部分)
    • GET:要求服务器将URL定位的资源放在响应报文的数据部分发送给客户端,URL和参数通过“?”分割
    • POST:将数据封装在HTTP请求数据中(HTML HEAD)
  • 服务器处理HTTP请求并返回HTTP报文
    • 处理后就把数据返回给客户端HTML数据
  • 客户端解析数据并渲染页面
    • 客户端解析HTML拿到数据,然后开始渲染页面
  • 断开TCP连接
    • 服务端在一段时间后若没有收到客户端的请求,连接就会关闭;

总结

网络是那么的重要,几乎每个项目都避不开它,而且还可能会出现很多奇奇怪怪的网络问题,所以有关网络的相关基础理论是编写和调试网络模块的必备知识,最好通过调试来了解请求是如何建立和相互通信的。


PS:以上描述的相关知识点,由个人总结并进行了简化,难免会有不足和错误的地方,欢迎大家指正!