首页|傲世皇朝注册|平台
首页|傲世皇朝注册|平台
全站搜索
 
 
新闻详情
 
当前位置
大摩注册-首页
作者:管理员    发布于:2023-12-02 06:39    文字:【】【】【
       

  大摩注册-首页为了制定一个统一的计算机网络体系,国际标准化组织ISO提出了一个试图使各种计算机可以在世界范围内互联成网的标准框架:OSI/RM(Open System Interconnection Reference Model 开放系统互连基本参考模型),该模型如下:

  媒介层:第一到第三层称为媒体层,它们主要与硬件相关,例如路由,交换和电缆规格;

  大致介绍一下各层(注意:看看就好,这不是重点,重点是后面的TCP/IP协议):

  物理层:物理层负责在设备和物理传输介质直接传输和接收非结构化原始数据。这一层中,把数字位转换为电,无线电或光信号。可以发现这一层往往跟各种材质啊信号呀什么的打交道,所以称为物理层;

  数据链路层:在通过物理层连接的两个节点之间进行传输数据帧,检测并且纠正物理层中可能发生的错误。它定义了在两个物理连接的设备之间建立和终止连接的协议,还定义了他们之间的流控制协议;

  网络层:构建和管理多节点网络,包括寻址、路由、流量控制。网络层是一种可以连接许多节点的介质,每个节点都在其上有一个地址,通过目标地址就可以在节点之间传输数据到目标地址。网络层消息传输不一定要保证可靠,网络层系可以可以提供可靠的消息传递,但不必这样做;

  传输层:传输层提供了将可变长度数据序列从源传输到目标主机的功能和过程方法,同时又保持了服务功能的质量。一些协议是面向状态和面向连接的,这意味着传输层可以进行分段传输、支持失败重传;

  会话层:控制计算机之间的连接,负责建立、管理和终止本地和远程引用程序之间的连接,提供全双工、半双工或者单工操作;

  表示层:网络服务和应用程序之间的数据转换,如字符编码、数据压缩、加密解密;

  应用层:最接近最终用户的OSI层,该层直接与实现通信组件的软件应用程序进行交互。

  可以发现,这个模型还真有点复杂。但很可惜,这个模型似乎不怎么流行,原因如下:

  在1980年代末和1990年代初的一段时间内,工程师、组织和国家对哪种标准(OSI模型或Internet协议套件)将更能塑造最佳和最强大的计算机网络存在争端,导致两极分化。尽管OSI在1980年代后期开发了其网络标准,后来更多的供应商网络上更多采用的却是TCP / IP标准,最终TCP/IP成为了实时的国际标准。

  可以发现,TCP/IP体系少了表示层和会话层,数据链路层和物理层用链路层取代。

  应用层:最高层,应用层的任务是通过应用进程间交互来实现特定网络应用。主要负责把应用程序中的用户数据传达给另一台主机或同一主机上的其他应用程序。这是所有应用程序协议的运行层,如SMTP、FTP、SSH、HTTP等;

  传输层:负责向两个主机中的进程之间的通信提供通用的数据传输服务。UDP是基本的传输层协议,提供了不可靠的无连接数据报传输服务;

  网络层:负责为分组交换网上的不同主机提供通信服务。该层定义了寻址和路由功能,主要协议是IP协议(Internet Protocol),它定义了IP地址,它在路由中的功能是将数据报传输到充当IP路由器的下一个主机,该主机更接近最终数据目的地;

  链路层:也称为数据链路层或者网络接口层,通常包括操作系统中设备驱动程序和计算机对应的网络接口卡。它们负责处理与传输媒介的物理接口细节。

  而后面我们除了讲到各种协议之外,还会顺便提及一些底层硬件,为了更好的进行阐述,我们将把链路层再分为物理层和数据链路层,采用以下这种五层模型:

  运行于内核:传输层、网络层、链路层,在内核中执行,主要处理所有的通信细节。

  介绍了这么多概念,是不是比较难懂呢,没关系,我们列一下每一层主要的协议,接下来我们会详细的讲解各种协议的原理。

  这里我们通过一个FTP客户端的通信流程,来说明下两台主机是如何工作在TCP/IP分层模型上的:

  端对端协议(End-to-end):包括应用层和传输层,端系统直接直接进行交互;

  逐跳(Hop-by-hop)协议:网络层,需要经过端系统中所有的中间系统。

  对于应用层来说,他们好像是直接与端系统进行交互的,应用层根本不知道底层通信用了多少个路由器,是在以太网上还是在令牌环网上的。

  如上图,路由器工作在网络层,属于第三层,所以经常有人称他为三层设备;而后面我们会讲到交换机,他是工作在第二层-数据链路层,所以也成为二层设备。

  正如上面的例子,分层之后是的顶层屏蔽了底层的物理和通信细节。底层的通信原理是在较低的协议层中实现的,因此每个进程都将隐藏大多数通信细节。以此类推,在传输层,通信表现为主机到主机,而无需了解应用程序数据结构和连接的路由器。而在互联网络层,则在每个路由器上遍历各个网络边界。

  就像我们搞软件开发划分层次一样,分层之后,提高了软件的复用度,封装每层细节,使用者只需要关注使用的API就可以了,不用关注实现细节。你不要告诉我你的一个功能涉及的一万行代码是写在一个函数里面的,那太可怕了。

  想要了解底层细节的人,就只能拆开TCP/IP协议潘多拉之盒,逐个协议去了解了,这也是本文后边会继续探讨的内容。高度的封装,使得顶层开发人员能更快速的通过API开发应用程序。作为一个API工程师,你知道怎么调用API发送HTTP请求就够了,但是作为一个有追求的工程师,你了解了这些细节之后,就能够胜任程序调优以及更加底层的开发工作了。这也是为什么我坚持写IT宅博客的原因:我想探索技术的本质,而不是生活在API构造的童话世界里面,这样即使童话世界谎言被拆穿的那天,也不至于失掉技术的信仰,因为我仍然有能力构建一个新的童话世界。

  假设我们现在直接通过两个网线把两台电脑连起来进行通信,需要做哪些工作呢。多亏我大学学的是网络工程,也是拉过网线的,所以多少还知道一点:

  所谓封装,就是每一层都会根据用到的协议,把数据封装成最终的一个数据单元(不同分层有不同的叫法,参考上图最左边每层的描述),每一层拿到的上一层的内容,把上一层封装好的内容作为当前层的数据,然后加上自己的协议头或者尾,接着执行该层协议的相关处理逻辑。

  有没有发现,这有点像装饰者模式,每一层拿到上一层的内容之后都添加额外的处理逻辑,但是不改变上层传过来的内容。

  A主机请求B主机的HTTP服务,应用层使用HTTP协议对请求内容进行封装,加上HTTP请求头,然后传给下一层;

  传输层拿到HTTP数据,使用TCP协议进行处理,加上自己的TCP头,封装成数据段,通过TCP协议传输给下一层。这一层通过TCP协议保证了可靠的传输;

  网络层拿到TCP传输段之后,使用IP协议进行处理,加上IP头,封装成包进一步传给下一层。这个IP决定了什么路由或者主机需要接收处理这个包;

  数据链路层拿到网络层的包之后,进一步封装成数据帧,最终通过数据链路层进行发送处理,最终数据帧通过物理层传输给接收端。

  所谓分用,指的是主机或者中间设备接收到一个物理层传输过来的数据帧时,数据开始从协议栈中由底向上升,逐层处理,每层去掉对应的协议的报文首部。每层协议盒都要检查报文首部的协议标识进行对应的协议处理。

  主机B接收到物理层传过来的数据帧之后,首先从首部找到MAC地址,判断是否发送给自己的,如果不是则进行丢弃;

  如果发送包是自己的,则从数据帧确定数据协议类型,再传给对应的协议模块,如IP、ARP等;

  IP模块接收到数据后获取IP首部,判断首部接收的IPIP地址匹配,如果匹配则根据首部协议类型转发给对应的模块,如TCP、UDP等;

  传到TCP模块之后,首先TCP模块会计算校验和,判断数据的完整性,然后处理数据包的顺序接收相关逻辑;最后检查端口号,确定具体应该要转发给应用层的哪个应用程序。

  应用层接收到数据之后,解析数据进行展示,这里是HTTP数据包,所以按照HTTP协议的约定进行解析展示。

  可以发现,以上流程中,大家感知最深刻的就是传输层的TCP或者UDP协议,以及应用层的HTTP协议了,因为做网站开发,或者网络通信编程经常会用到它们的API。

  上面介绍的并不是很详细,不过没关系,后面我们会把主要的协议都拿出来详细的讲解。

  当然,正常的网站请求中,中间肯定会涉及到很多路由器,交换机,光纤等底层的物理设备,中间会产生很多的逐跳(Hop-by-hop),每个中间系统都会对数据帧进行分用和封装的过程。

  TCP/IP协议簇内容非常多,这里列出的是本文可能会介绍到的相关协议,以及他们之间的交互关系:

  【文章福利】:小编整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!~点击832218493加入(需要自取)

  我们的数据帧究竟是怎么传给不同的主机呢。前面我们了解到每一个上层都依赖于下层的API,而物理层是最底层的了,它是真的要把数据传出去了。而数据最终都会变为0和1,物理层依赖于各种不同硬件技术,通过网络的电子传输技术,把0和1在传输介质中进行传输。

  很久以前,有些同学家里都是用的电话线进行上网的,这种网络传输模型类似如下这样:

  传输媒介的种类非常多:双绞线、对称电缆、同轴电缆、光缆、无线信道等,导致物理层的协议种类较多。

  物理层的主要作用是屏蔽掉这些传输媒介和通信手段的差异,使物理层上面的数据链路层感觉不到这些差异。为此,物理层需要处理以下事情:

  最后我列几个物理层常见的面试题,一般的开发人员都是工作在传输层以上,所以考一些TCP,UDP,HTTP,HTTPS等协议我觉得更贴近开发人员真实的工作场景。当然,如果是通信领域的工程师,物理层都是家常便饭,这些可是通信的基础知识。即使知识应用开发工程师,了解这些也不会吃亏,说不定哪天亲戚还需要叫你帮忙拉网线呢。

  所谓信道复用技术,指的是大家共享一个信道进行通信,在接收端在使用分用器,把合起来传输的信息分别送到相应的终点;

  将时间划分为一段段等长时分复用帧,每一个时分复用的用户周期性的占用帧位;

  时分复用,如果用户没有任何数据要传输,也会周期性的给他分配时隙,这就导致了信道利用率不高。

  统计时分复用使用STDM帧来传送复用的数据,把所有用户数据按时间顺序组成STDM帧,放入一个队列中,依次发送出去,这样就能够更合理的共享信道。STDM帧中的数据需要添加用户地址首部信息,以便能够正确的分发给目标用户:

  除了以上三种,还有波分复用和码分复用,感兴趣的朋友可以自行搜寻资料了解,这里就不继续展开来讲了。

  如果我们只是想用几台电脑搭建一个局域网,那么可以通过集线器(Hub)进行搭建,这个硬件工作在物理层,会把自己收到的字节都复制到其他端口,如下图:

  如上图,其中一台电脑发送信息之后,Hub以广播的方式发给其他三台机器,但是究竟哪台电脑才会把消息接收下来呢?这里我们就要讲到数据链路层了,在这一层判断数据包是不是自己的。

  所有的以太网(802.3)帧都基于一个共同的格式。在原有规范的基础上,帧格式已被改进以支持额外功能。

  长度或类型:0800时,表示IP数据报,0806表示ARP请求/应答,0835表示RARP请求/应答;

  这是一个物理地址,叫做链路层地址,因为链路层主要解决媒体接入控制问题,所以称为MAC地址(Media Access Control Address)。实际上,MAC地址就是适配器地址或适配器标识符,当适配器插入到某台计算机之后,适配器上的标识符就成为这台计算机的MAC地址了。

  FCS是帧校验序列,也就是循环冗余检测,收到数据报之后,会通过一个检验计算规则,把计算结果与FCS字段匹配,如果匹配补上,则帧可能在传输过程中受损,通常会丢弃该帧。

  我们知道,在数据链路层,是通过MAC地址判断某一个接收到的包是不是要进一步处理的。但是如果我们不知道对方的MAC地址的时候,如何发送数据链路层的帧呢?这就需要用到数据链路层的ARP协议了。

  ARP协议:ARP为IP地址到硬件地址之间提供了动态映射,我们通过ARP可以把32位的Internet地址转换为48位的MAC地址。另外,我们可以使用RARP,把48位的MAC地址转换为32位的Internet地址。

  另外,为了保证ARP的高效运行,ARP会维护每个主机和路由器上的ARP缓存,把Internet地址和MAC地址的映射关系保存起来,缓存正常到期时间是20分钟。

  下面是这个过程的演示,其中ARP数据帧只把关键信息描述出来了,想要了解完整的帧格式可以用参考 TCP/IP协议详解卷1[3]

  主机A想知道192.168.1.4这个IP地址的MAC地址是什么,发现本地缓存中找不到,于是广播了一个ARP请求,主机B和主机D收到之后,发现自己不是192.168.1.4于是忽略这个消息,主机C发现自己就是192.168.1.4,于是响应了一个ARP数据帧。最终主机A收到主机C响应的数据帧,拿到了MAC地址,并把IP地址和MAC地址映射关系保存下来。

  前面我们用了集线器组件网络,这个时候所有消息都会广播到其他端口,可以发现集线器转发了很多不必要的消息,能不能只发给需要的端口呢?这个时候就需要用到交换机了。

  当一台电脑A向交换机发送数据时,交换机会把电脑A的IP和MAC地址记住,保存到一个转发表中,如果转发表中暂时找不到目标IP地址的MAC地址,那么首先还是会广播消息,最终转发表会记录所有请求过交换机的电脑IP和MAC。当然,转发表也是有过期时间的。

  如上图,看到交换机的奸笑没有,与集线器不同,交换机是有灵魂的的,你告诉他你的身份证号和住址了,他就会偷偷记下来。

  MAC地址是工作在数据链路层的,也就是交换机这一层,交换机之间的主机进行通信,都是用的MAC地址,但是一旦走出了局域网,我们就得用大家都公认的IP地址了。

  MAC地址就好像是我们的身份证,IP就像是我们的住址,可以根据住址寄送快递,但是不能根据身份证号码寄快递,别人不知道怎么走呢。

  一个局域网,用身份证没有问题呀,因为要找某个人,ARP会大喊一声名字,那个人就会告诉你他的身份证号码了,这个时候直接以身份证作为标识传消息,别人听到不是自己的身份证就不管了。最终交换机这个小管家记住了所有人的名字跟身份证号码,就会使用悄悄话的方式传达消息了。这也就是用到了交换机的转发表。

  如上图,主机准备发送一个消息出去,结果交换机B收到后,复制数据帧,发送给了交换机A、C、D,此时交换机B认为主机是在左边。但是不妙的事情发生了,交换机D收到消息后,由于转发表还是空的,又是也复制数据帧,转发到了交换机A、B、C,这个时候交换机B发现怎么主机的数据又从右边传过来了,这些彻底晕了,不知道主机究竟在哪里。就这样数据一致在这个网络里面打转,这就拓扑环路导致的问题。

  STP通过在每个交换机禁用某些端口工作,来避免拓扑环路,保证不会出现重复路径。

  STP会找到拓扑结构的一个生成树,通过生成树避免环路。生成树的形成和维护有多个网桥完成,在每个网桥上运行一个分布式算法。

  网桥会发送一种称为网桥协议数据单元(BPDU)的帧来辅助形成和维护生成树。

  STP首先会尝试选举根网桥,根网桥是在网络中标识符最小的网桥(也就是说优先级与MAC地址结合),网桥初始化的时候,假设自己是最小的网桥,然后用自己的网桥ID作为根ID字段的值发送配置BPDU消息,如果发现ID更小的网桥,那么会停止发送自己的帧,并基于接收到的ID更小的帧构造下一步发送的BPDU消息。发出根ID更小的BPDU端口被标记为根端口,剩余端口被设置为阻塞或者转发状态。

  前面我们将的数据链路层,其实只能在局域网内进行通信,因为都是通过MAC地址进行传达信息的,要想跨局域网,那么就得用到IP地址了,这就是网络层要做的事情了。

  IP协议本身不支持发现发往目的地地址失败的IP数据包,也没有提供直接的方式获取诊断信息,比如在发送途中,经过了哪些路由器,以及往返时间。

  ICMP并不为IP网络提供可靠性,它只是用于反馈各种故障和配置信息。丢包不会触发ICMP。

  ICMP是RFC 792中定义的Internet协议套件的一部分。ICMP消息通常用于诊断网络或探测网络目的,或者是为了响应IP操作中的错误而生成(如RFC 1122中所指定),ICMP错误响应给原始数据包的源IP地址。

  但是黑客经常用ICMP来做坏事,于是网络管理员可能会用防火墙阻止掉ICMP报文,这样的话,很多ping、traceroute之类的诊断程序就无法正常工作了。

  其中,最常用的类型是8:回显请求(ping),以及0:回显应答(ping应答)。

  ping程序会发送一份ICMP回显请求给主机,并等待返回ICMP回显应答。

  我们知道,网络管理员可能会用防火墙阻止掉ICMP报文的,这样我们可能就ping不通了,但是主机的可达性不能只取决于IP层是否可达,还与端口号和协议有关,而ping是运行在网络层的,用于测试网络连接状态和信息包发送接收状况,即使ping不通,我们也可能用telnet远程登录到主机的其他端口,如25号端口。

  Unix系统实现ping程序时,把ICMP报文的标识符设置为进程ID,在进程内,序号从0开始,每发送一次新的 回显请求就加1,这样就可以同时运行多个ping进程了。

  端口号是传输层的东西,ping程序是使用ICMP协议,直接跳过了传输层,所以呢,ping程序是没有所谓的端口号的。

  IP协议拿到数据后进一步加上IP头,加上自己的IP和目标IP,传输给数据链路层;

  数据链路层拿到IP数据包,准备封装成帧,这个时候会去寻找目标IP的MAC地址,如果在A主机的ARP映射表找到了IP的MAC地址,那就直接拿来用了,否则会发起一个ARP广播请求,获取到MAC地址。至于跨网关这种ping,会多了转发的功能更,后面会进行介绍。最终数据链路层封装成数据帧,从网络接口发出去;

  服务器拿到数据帧之后,拿到MAC头,判断MAC地址是自己的,就基于拿到Frame data,按首部协议传给对应的模块,即IP模块;

  IP模块拿到数据,判断到IP跟自己对上了,与是继续拿到IP data,传输给ICMP协议,ICMP协议收到消息,准备应答:

  提示:为了能处理ICMP网络报文,我们需要用到原始套接字(SOCK_RAW),而不是SOCK_STREAM或者SOCK_DGRAM套接字。

  更多提示:Homework 6: A raw socket ping tool,思路都在这里了,大家动手做一做,然后就可以有直接操作网络层的工作经验了。??

  差错报文是有关IP数据报传递的ICMP报文。要是发送IP数据报中途产生了异常,那么就会响应ICMP差错报文。

  源地址不是单个主机(源地址为零地址、环回地址、广播地址或者多波地址)的数据报不会产生差错报文;

  为什么要这些规则呢?假如允许ICMP差错报文对广播分组响应,那么就会导致广播风暴了。

  上面的表格我们了解到,如果类型为三则表示目标不可达,而根据具体的代码可以进一步划分:

  这里我们演示通过tftp访问一个不存在的端口号,查看其返回的ICMP响应差错报文。tftp应用在传输层是通过UDP来进行传输数据的

  可以发现这里执行了五次UDP请求,每次请求都响应了一个ICMP包,为udp port 8090 unreachable端口不可达,产生了ICMP不可达报文,该报文一般格式如下:

  为什么需要返回IP首部:因为IP首部包含了协议字段,使得ICMP知道如何解释后面的8个字节;

  为什么需要原始IP数据报中数据的前8个字节:因为这里面包含了源端口和目的端口。

  注意:ICMP报文是在主机之间交换的,网络层的协议,不需要端口号,而以上20个字节的UDP数据报是包含了源端口号和目标端口号信息的。

  因为网络编程中,BSD系统不把从socket接收到的ICMP报文中的UDP数据通知用户进程,除非该进程以及发送了一个connect命令给该接口。标准的BSDTFTP客户程序并不发送connect命令,所以它永远也不会受到ICMP差错报文的通知。

  traceroute主要是通过故意设置特殊的TTL,来达到追踪目的地路径上的路由器的功能。

  TTL:是 Time To Live的缩写,该字段指定IP包被路由器丢弃之前允许通过的最大网段数量。每经过一个路由器,TTL就会减一,然后再把IP包转发出去,如果TTL减到0了,路由器就会丢弃收到的TTL=0的IP包,并向IP包的发送者发送一个ICMP差错报文,类型为11,代码为0:传输期间生存时间为0。

  第一轮,traceroute设置TTL值为1,那么遇到第一个路由就返回ICMP容错报文了,下一轮,TTL设置为2…这样依次增加。最终就把整个链路的路由器都试出来了。

  当然,有点路由器不会回整个ICMP,这也是为什么你去traceroute一个公网地址,看不到中间路由的原因。

  除此之外,traceroute也可以通过不设置分片,来确定传输链路的MTU(Maximum Transmission Unit, 最大传输单元):首先发送一个分组的长度正好与出口MTU相等,如果中间遇到窄点的关口,就被卡主了,这个时候会接收到一个ICMP差错报文,然后调小分组长度重试…

  在讲数据链路层的时候,我们用一个交换机,就构建了一个局域网。但是现在我们局域网里面的一台机器,想要访问另一个局域网的机器,怎么办呢。这就是本节讨论的内容。

  IP是TCP/IP协议簇中最核心的协议,所有TCP、UDP、ICMP等数据都已IP数据报格式进行传输。

  IP协议是不可靠的传输协议,上一级我们讲到到了ICMP协议,每当传输出现异常,IP层都会丢弃数据包,并且可能会响应一个ICMP差错消息给发送端,而任何要求的可靠性必须由上层如TCP协议来提供;

  IP协议是无连接的,也就是说IP不维护任何关于后续数据报的状态信息,每个数据报相互独立。具体表现在:可以不按发送顺序接收,不用维护连接状态,免去了维护复制的链接状态信息(后面讲传输层的TCP协议的时候会介绍到)。

  服务类型:包含3bit优先权子字段(已被忽略),4bit TOS子字段(分别代表最小时延、最大吞吐量、最高可靠性和最小费用)和1bit未用位但必须置0;

  路由器一般充当一个网关,属于三层设备。会把MAC和IP头取下来根据内容进行处理。路由器有五个网口,分别可以连接5个局域网,每个网口和局域网的IP地址相同的网段,每个网口都是对应的局域网的网关。

  路由器除了有交换机的功能外,更拥有路由表作为发送数据包时的依据,在有多种选择的路径中选择最佳的路径。

  第二层的产品功能在于,将网络上各个电脑的MAC地址记在MAC地址表中,当局域网中的电脑要经过交换机去交换传递数据时,就查询交换机上的MAC地址表中的信息,将数据包发送给指定的电脑,而不会像第一层的产品(如集线器)每台在网络中的电脑都发送。

  而路由器除了有交换机的功能外,更拥有路由表作为发送数据包时的依据,在有多种选择的路径中选择最佳的路径。此外,并可以连接两个以上不同网段的网络,而交换机只能连接两个。路由表存储了(向前往)某一网络的最佳路径、该路径的“路由度量值”以及下一个(跳路由器)

  在不同的局域网中,私有IP地址是会重复的,而我们要访问公网的时候,一定要分配一个共有IP地址,所以,我们在访问公网的时候,需要路由器帮忙把私有IP变为共有IP,这种叫做NAT网关,普通内网之间的通信用到的称为转发网关。

  主机A发现要访问的主机B不是在同一个网段,准备先找到网关,把消息发给网关,网关地址是192.168.1.1,主机A通过ARP获取到了网关的MAC地址,然后发送如下数据包:

  路由器A的192.168.1.1网口接收包之后,准备把包转发出去。而路由器A中的路由表中匹配到了,要想发送给192.168.3.4/24,需要从192.168.2.1这个网口出去,下一跳地址为192.168.2.2/24。路由器通过ARP拿到了下一跳192.168.2.2/24d的MAC地址,然后发送如下数据包:

  路由器B的192.168.2.2网口接收包之后,准备把包转发出去。路由器B中判断到目标IP在192.168.3.1这个网口所在的局域网,于是通过ARP拿到了192.168.3.4的MAC地址,然后发送如下数据包:

  可以发现在转发网关中,源IP和目的IP地址都是不会变的,因为整个内网不可能有冲突的IP。

  但是,假如我们要访问外网,情况就不一样了,最终可能会请到到另一个局域网,另一个局域网的私有IP是可能跟我们所在的局域网一样的,为了避免冲突,于是就有了NAT网关。专门在把数据包发送出去之前,把IP改为公网IP。

  现在假设主机A要访问另一个城市的主机B,这里为了演示NAT,我们把模型简化一下,假设路由器出去之后就是公网IP了,如下:

  由于是不同的局域网,主机A不会知道主机B的IP的,而主机B接入互联网的之后,领取到了一个互联网的IP,就是上图路由器WAN口的IP:203.0.113.103,所以主机B会把这个IP作为主机B的IP,最终发出如下IP数据包:

  最终路由器B接收到消息,通过NAPT得到最终接收数据报的IP为当前局域网的192.168.1.3/24,最终把消息转发给了这个IP所在的主机B。

  传统的NAT(traditional NAT)包括基本NAT(basic NAT)和网络地址端口转换(Network Address Port Translation, NAPT)。基本NAT只执行IP地址的重写,本质上是将私有地址改写为一个公共地址,这往往取自于一个由ISP提供的地址池或共有地址范围,这种NAT不是最流行的,因为无助于减少需要使用的IP地址数量。

  比较流行的做法是使用NAPT,NAPT使用传输层标识符如TCP或者UDP端口,或者ICMP查询标识符来确定一个特定的数据报到底和NAT内部哪台私有主机相关联。

  如果局域网两个端口号一样,那么NAPT会重写端口号,保证不一致。如下图,三个局域网的IP需要转换为公网IP,由于有两个的端口重复了,于是NAPT进行了端口重写:

  我们通过route命令和iproute命令都可以进行路由策略的配置和查询。

  同一个路由,也可以配多个运营商的网络,针对不同的IP,采用不同的运营商网络

  配置时非常灵活的,但是在复杂的网络环境下手动配置路由成本太大了,并且网络结构也是经常发生改版的。

  所以,我们可以使用动态路由路由器,这种路由器会根据路由协议算法生成动态路由表,动态的随着网络运行状况调整路由表。

  网络是复杂的,为了生成动态的路由表,需要配合特定的算法,主流的动态路由主流有两种算法。

  基于距离矢量算法实现的BGP协议(Border Gateway Protocol,外网路由协议):距离矢量,就是每个路由器都保存一个路由表,路由表每行保存了下一跳的路由器,以及距离下一跳路由器的距离。也成为边界网关协议。

  在BGP的世界中,每个路由域都称为自治系统或AS。BGP所做的工作通常是通过选择遍历最少自治系统的路由:最短的AS路径来帮助选择通过Internet的路径。

  我们会把重点放在传输层以上,所以动态路由协议这部分我们暂时不做不深入研究。

  传输层涉及到两个重要的协议:UDP和TCP,本节我们重点介绍这两个协议。

  UDP基本没干啥事,继承了IP包的特性:数据可能丢失,顺序传输无法保证。UDP与后边介绍的TCP不一样,是无状态的。我们来看看UDP数据报的格式:

  UDP校验和:UDP校验和覆盖UDP头部和UDP数据和一个伪头部(区别:IP头部校验和只覆盖IP头部),伪头部衍生子IPv4头部字段的12个字节,或者衍生子IPv6头部字段的一个40字节的伪头部;

  类似ICMP协议回显请求的标识符,UDP的端口用于区分是哪个进程的数据包,如果没有端口号,那么就不知道应该把数据包最终交给哪个进程来处理了。

  TCP端口号由TCP来查看,UDP端口号由UDP来查看,TCP端口号和UDP端口号是相互独立的,所以是可以相同的。每个请求都有源IP、目标IP、源端口号、目标端口、协议五个元素来标识的,每个协议的端口池是完全独立的。

  在UDP/TCP协议中源端口和目的端口都只有16位,也就是说端口的取值范围为0~65535。

  需要资源少,在网络情况比较好的内网,或者对对包不敏感的场合。如DHCP和TFTP就是基于UDP的;

  需要时延低,允许丢包,不关注网络拥塞的场景,如视频直播这种流媒体,实时游戏,通信,物联网等领域。

  TCP是我们平时用到最多的协议,特别是做web开发的时候,或者互联网后端开发,真的是时时刻刻都会用到,这里我会展开来讲。《TCP/IP详解-卷1:协议》一书中花了6章来讲解TCP的各种功能,单单是从TCP/IP协议栈的名称就可以看出,TCP协议的分量有多重了。为此,面试官张口就聊TCP咋的咋的。

  TCP是面向连接的可靠的服务:面向连接指TCP的两个应用程序必须在它们可交换数据之前,通过相互联系来建立一个TCP连接;

  TCP提供了一种字节流抽象概念给应用程序:TCP不会自动插入记录标志或者消息边界,这意味着TCP没有限制应用程序的写范围。发送端分两次发10字节和30字节,接收端可能会以两个20字节的方式读入。

  我们还是先来看看TCP数据报的格式吧,这个可比UDP复杂多了,但是也是设计的恰到好处的。

  如上图,头部深黄色部分为TCP特有的重点字段,后面TCP相关功能基本都是靠这些特有的字段来实现的。

  源端口号和目的端口号:同UDP一样,主要用于区分数据应该转发给哪个应用;

  序号:这个序号是为了解决乱序问题,32位无符号数,到达2^32-1后再重新从0开始;

  确认号:确认已经接收到了哪里,该确认序号表示该确认号的发送方期望接收的下一个序列号。该字段只有在ACK位字段被启用的情况下才有效,所以也成为ACK号或者ACK段;

  状态位:该状态位会让TCP连接双方的状态发生流转,常见的状态为,后面讲建立连接和断开连接的时候会用到:

  ACK:回复状态,启用该状态的情况下,确认号有效,连接建立之后一般都是启用状态;

  PSH:推送,表示接收方应该尽快给应用程序传送这个数据——没有被可靠的实现或用到;

  窗口大小:流量的窗口大小,用于流量控制,通信双方各声明一个窗口,这个大小表明了自己当前的处理能力;

  校验和:覆盖了TCP的头部和数据,以及伪头部数据(与UDP使用的相似的伪头部进行计算);

  第一次握手:主动连接方发送一个SYN报文段指明自己想要连接的端口号,以及客户端消息的初始化序列化ISN(c);

  第二次握手:服务器接收到消息后,也发送自己的SYN报文,包含了服务端的初始化序列号ISN(s),并设置确认号ack=客户端序列号+1;

  第三次握手:客户端应答服务器的SYN,将服务端的序列号+1作为ack返回给服务端。

  总结一下:客户端与服务端利用SYN报文交换彼此的初始化序列号。在我们熟悉的Socket编程中,三次握手在执行connect的时候触发。

  客户端请求建立连接,服务端收到了请求,并且做出了响应,很明显,服务器没法知道这个响应究竟有没有被接收,也许可能客户端迟迟收不到SYN响应,于是结束了请求。这个时候再传消息网络层就会收到一个ICMP目的不可达的差错报文。

  同理:客户端的SYN请求如果迟迟没有服务器的响应,那么也会重发SYN,最终如果服务端可能收到两个SYN,客户端想要建立一个连接,但是服务器收到两个SYN之后,建立了两个连接(当然,实际上的三次握手服务端是会判断客户端的请求序列号的,发现是同一个序列号,并不会建立多个连接,这也说明序列号的重要性)。

  为什么不需要四次呢?因为如果服务端和客户端双方都发起SYN,并且收到ACK之后,就都知道对方接受了自己的请求了,已经没有必要再继续确认下去了。

  接下来我们看看连接关闭的流程,连接的任何一方都可以发起关闭操作,此外,也支持双方同时关闭连接。在传统的情况下,负责发起关闭连接请求的通常是客户端。

  连接的主动关闭者发送一个FIN段请求关闭连接,携带了Seq=K,指明接收方希望看到的自己的当前序列号;携带了ack=L,指明自己想要接受到的下一个消息的序号。这个时候,连接主动关闭者表明了自己已经没有数据要发送了,但是仍然可以接受被动关闭者发送的数据;

  连接的被动关闭者进行了ACK回应,ack为K+1,表明自己已经成功接收到了主动关闭者发送的FIN。但是自己还未准备好关闭,所以主动关闭者会进入FIN_WAIT_2等待状态;

  紧接着被动关闭者也发送了一个FIN端请求关闭连接,携带了Seq=L。告诉主动关闭者自己也准备好了关闭;

  最后连接的主动关闭者接收到了对方的FIN关闭请求,也回应了一个ACK,同样的ack=L+1,表明自己已经成功接收到了被动关闭者发送的FIN;

  可以发现,因为TCP是全双工的,双方都要单独发起关闭请求,只有当连接双方都发起FIN关闭请求操作,并且得到确认之后,才完成一个完整的关闭操作,这也是被称为四次握手的原因。

  信息发送期间的状态流转如上图所示。其中主动关闭者在CLOSED状态之前,有一个TIME_WAIT状态,那么问题来了:

  我们知道主动关闭者在应道对方的FIN请求,有可能对方是收不到的,如果收不到的情况下,那么对方就可能认为自己的FIN请求丢失了,需要重新发起FIN请求,所以主动关闭者需要有一个足够长的等待时间,让对方有重试的机会。

  等待时间是2MSL(Maximum Segment Lifetime,报文最大生存时间),这也是报文在网络上最大的生存时间,超过了这个时间就会被丢弃。RFC 793中规定MSL为2分钟,实际应用中常用的是30秒,1分钟和2分钟等。如果超过了这个时间,那么主动关闭者就会发送一个RST状态位的包,表示重置连接,这个时候被动关闭者就知道对方已经关闭了连接:

  可以发现,由于端口复用,主动关闭者已经开启了另一个连接,这个时候被动关闭者还在重试发起FIN请求,导致新主动关闭者新的连接收到了很多没用的包。因为包是有序列号的,所以可以判断到不是本次连接该接收的包。为此,我们需要让主动关闭者进行等待,确保被动关闭者不会再发FIN请求了,再进行端口复用。

  可以发现,每个TCP连接在正常的建立和关闭的基本开销是7个报文段,如果只是需要交换很少量的数据时,有写程序更愿意选择使用UDP协议。但是UDP会面临数据丢失,拥塞管理,流量控制等问题。

  介绍了三次握手和四次挥手,我们再看看看以下这个TCP状态机就清晰多了。(如果没有看过三次握手和四次挥手流程,不建议直接看这个状态机,线、如何保证可靠传输:ACK+序列号

  假设主机A通过TCP向主机B发送数据,当主机A的数据到达主机B时,主机B会发送一个确认应答消息ACK。主机A收到ACK之后,就知道自己的数据已经被对方接收了:

  在TCP头部中,为了实现流量控制,包括顺序问题与丢包问题,我们重点关注TCP头部的这三个字段:序列号,序列号与确认号:

  其中TCP头部的窗口字段表明自己的处理能力,代表着可用缓存空间的大小,以字节为单位。

  TCP连接的每个端都可以收发数据,每个端的收发数据量是通过一组窗口结构来维护的。每个端都会包含一个发送窗口结构和接收窗口结构。

  SND.WND:提供窗口大小是由接收返回的ACK中的窗口大小字段控制的;

  关闭:窗口左边界右移,当已发送的数据得到ACK的时候,就会进行关闭,提供窗口大小减小;

  打开:窗口左边界右移,当已确认的数据得到处理后,那么接收端可用缓存就会变大,这个时候通过打开操作让提供窗口大小变大;

  为了避免接收重复数据:接收到的数据包小于左边界,说明是已经确认过的,将把数据报丢弃;如果接收到的数据报序列号大于右边界,说明暂时超出了处理能力范围,也将会被丢弃。

  为了保证已确认数据包的连续性,接收到的数据包的序列号与已确认 已接受部分连续的时候,才表示真正的已确认,左边界才可以右移。

  TCP在发送数据的时候会设置一个重传计时器,如果计时器超时仍然没有收到ACK确认信息,那么会进行重传操作。

  ACK丢失的场景:过了一段时间,3的计时器发现超时了,于是会触发超时重传。但是这个时候接收方发现3是在已接受已确认区域,于是会丢弃3,并反馈一个ACK;

  数据丢失的场景:4和5的数据传输丢失了,计数器发现超时,也会进行超时重传,保证4和5可以传给接收方,并拿到ACK反馈。

  在ICMP端口不可达案例中,采用UDP的TFTP客户端使用简单且低效的超时重传策略:设置足够大的超时间隔,每5秒进行一次重传;

  TCP通过采样RTT的时间,进行加权平均,算出一个值,最终得到一个估计的重传时间。

  快速重传机制是这样的:当接收方接收到一个序列号大于下一个所期望的报文段的时候,就会检测到数据流中间丢失的间隔,然后发送冗余的ACK,向发送者索要确实的间隔。当发送者收到一定数量的冗余的ACK(称为重复ACK的阈值或dupthresh)之后,就不等定时器过期了,直接重传丢失的 报文。

  重复ACK的阈值通常为3,一些非标准化的实现可基于当前的失序程度动态调整。

  如下例所示:发送方的4、5、6、7都已经发送出去了,但是接收方接收到了5、6、7,少了4,会在分别收到5、6、7的时候都发一个3的ACK,向发送方索要下一个数据4。这样发送方就收到到3个3的ACK了,于是就主动发起了4的重传,不等待重传计时器超时了:

  虽然重传保证了数据的到达,但是重传应该尽可能保证不重传以正确接收到的数据,而SACK信息能更快速的实现空缺填补并且减少不必要的重传。

  随着选择确认选项的标准化[RFC2018],TCP接收端可以提供SACK的功能了,通过TCP头部的累计ACK号字段来描述其接收到的数据。

  每当缓存存在失序数据时,接收端就可以生成SACK,代表着缓存接收状态地图,这样通过将缓存的接收状态地图发给发送方,发送方就很快可以知道是什么数据丢失并发起重传了。

  这种重传机制下,窗口内的其他报文段也可以被接收确认,但只有在接收到等于窗口的左边界的序列号时,窗口才会前移。这样就减少了窗口内的不必要的重传。

  流量控制指的是通过控制发送方和接收方的窗口大小,以使得接收方缓存中已接受的数据处理不过来时,通过减小发送方的窗口大小,让接收方能有足够的时间来接收数据包;或者是接收方比较空闲时,尝试让发送方调大窗口大小,以加快传输,合理利用空闲的网络资源。

  流量控制主要是通过TCP头的窗口大小来调节的。发送端收到接收端的通告窗口之后,得知接收端可接收的数据量。

  正常情况下,发送方左边界每关闭一格,右边界就打开一个,多一个可发送的单元:

  为了避免由于窗口更新通知ACK丢失,到时双方陷入等待的僵局,在发送方停止发送数据之后,会采用一个持续计时器间歇性的查询接收端,给接收端发送窗口探测(TCP ZeroWindowProbe)请求,要求接收端返回TCP ZeroWindowProbeAck,看看是否窗口是否已经增加了:

  前面我们讲到,可以通过滑动窗口大小来控制流量,从而为接收方缓解压力,避免不必要的丢包。

  可以理解为滑动窗口是为接收方服务的,而拥塞窗口是为整个网络通道服务的,拥塞窗口大小又会受制于接收方滑动窗口大小,并且会因为网络原因进行调整。因为网络通道中的任何一个环节都有可能影响整体的传输效率。

  那么我们发送端实际可用窗口应该是多少了,这里我们记实际可用窗口大小为W,那么W为接收端通知窗口awnd和拥塞窗口cwnd的较小者:

  虽然可以通过接收方的ACK得到对方的接收窗口大小,但是因为刚开始并不知道拥塞窗口是多少,所以只能以越来越快的速率不断发送数据,直到出现数据包丢失为止。

  通常TCP在建立新连接的时候会执行慢启动,直到有包丢失,然执行拥塞避免算法进入稳定状态。

  先发送初始窗口大小的数据,没有出现丢包,并且每收到一个ACK,慢启动算法就会以min(N,SMSS)来增加cwnd的值。可见这是指数性的增长。

  直到出现了网络拥塞,出现丢包、超时重传,说明已经到达了慢启动的阈值ssthresh(slow start threshold),这个时候cwnd减少一半,并作为新的ssthresh。

  一旦达到慢启动的阈值之后,为了得到更多的传输资源而不影响其他连接的传输,TCP实现了拥塞避免算法。一旦确定慢启动阈值,TCP会进入拥塞避免阶段,这个时候cwnd每次的增长值近似于成功传输的数据段大小。也就是说由原来慢启动的指数增长,变为了线、Socket编程

  Socket是一个抽象层,主要是把TCP/IP层复杂的操作抽象为几个简单的接口提供给应用层调用,进而实现应用进程在网络中通信。Socket主要是端到端之间的传输协议(网络层之上的协议)。因为Socket是一种高层的抽象网络API,是一种端到端的通信,只能访问到端到端协议之上的网络层和传输层。

  Socket起源于Unix,在Unix中,一切皆文件,Socket也不例外,是一种打开-读/写-关闭的模式实现的。在服务器和客户端各自维护了一个文件。

  UDP不用维护连接状态,所以不需要针对每个连接建立一组Socket,只需要一个就可以了。

  到目前为止,我们把物理层、数据链路层、网络层、传输层主要的协议和功能都介绍了一遍。基于这些底层的协议栈支撑,我们可以很快的构建出应用层的程序,接下来我们简单讲一下应用层。

  HTTP:Hypertext Transfer Protocol,超文本传输协议,是一个基于请求与响应,无状态的,应用层的协议,常基于TCP/IP协议传输数据,互联网上应用最为广泛的一种网络协议,所有的件都必须遵守这个标准。设计HTTP的初衷是为了提供一种发布和接收HTML页面的方法;

相关推荐
  • 优游优游注册平台官网
  • 大摩注册-首页
  • 主页%『德信天下注册』%主页
  • 星球娱乐-注册主页
  • 利澳注册利澳平台-未来享受生活
  • 首页-傲世皇朝注册「首页」
  • 百威注册平台-首选网站
  • 给大家科普一下-玄武iphone注册
  • 万和城-万和城平台-娱乐「诚邀代理加盟」
  • 优博娱乐-安全吗
  • 脚注信息
    友情链接: