小明学c++系列之第一篇:网络篇

来源:互联网 发布:淘宝不自动打开淘口令 编辑:程序博客网 时间:2024/06/05 09:22

胡适先生曾说:“发表是吸收的利器”,于是我写下这篇文章。我看了一些计算机基础,我感觉里面的知识都没有活力,于是,我希望用我的方式将计算机的工作机制正确地、简洁地、有趣地讲明白。这篇文章适用于没有计算机基础但希望了解计算机的人以及有一些计算机基础但是对计算机缺乏一个整体认知的人。

背景

小明学c++其实讲得是一个故事。话说有一天小明心血来潮,突然自学计算机。他听说C++是一个不错的计算机语言,于是他打开了电脑的浏览器,输入了一个网址:http://www.runoob.com/cplusplus/cpp-tutorial.html,然后浏览器出现了以下界面:这里写图片描述
其实这跟我们平时用浏览器浏览资讯、看看视频没有什么区别;那么,从小明在浏览器上输入网址,到最终显示菜鸟网站的C++教程,这期间到底发生了什么?

基础知识

我们先来看看这个网址吧:
这里写图片描述
原来网址的专业术语叫做统一资源定位符URL(Uniform Resource Locator),在互联网上就是通过这个东西来区分不同的文档和资源的。
那么这个URL就包含了三个部分:
(1)所使用的协议:这里使用超文本传送协议(Hypertext Transfer Protocol),它是一种应用层协议。

这里有两个名词(协议和应用层)可能需要解释一下,协议就是规则的集合,因为如果计算机跟计算机之间通信时用不同的语言是不能交流的,所以必须统一用一种语言,那么这个语言就是HTTP协议。这个协议大家都知道怎么用,于是就能够交流了。而应用层是计算机网络分层思想产生的一个层次。分层思想是一个很重要的思想,因为互联网非常庞大,因此通信时很复杂的一件事情,为了使得分工更加明确,人们把网络网络组网分为好几个层次,其中应用层是最高层,这一层主要负责实现具体的网络应用的,包括邮箱啊、上网啊、看视频啊、传文件啊等等。

(2)主机域名:www.runoob.com,这是菜鸟网站的域名。通俗地说就是网站的名字,让人更加容易记住。比如说www.baidu.com就是百度网站的名字一样。跟域名相对的,是IP地址,我们知道IP地址是标识某一台具体的计算机的。一般情况下,IP地址跟域名具有对应关系,我们在windows系统的cmd用ping命令就可以查到某域名对应的IP地址:
这里写图片描述
上图表明,www.runoob.com 对应的ip地址是183.61.180.185。
那么为什么要域名呢?因为域名好记啊。
为什么要有IP地址呢?因为域名往往是不规则的,长短不一的。有的域名很长,有的域名很短,到了计算机网络的路由部分,很不容易找到通往目的计算机的一条道路,于是就用IP来找路。后面我们还会详细地讲解如何根据IP来找路。
(3)路径名:cplusplus/cpp-tutorial.html,这里表示在菜鸟网站主机的哪个地方获取资源。这里的资源就是cpp-tutorial.html。通过后缀名html就可以知道这是一个用超文本标记语言html写出来的文件。html是一种标记语言,比如说我要显示一篇文章,就直接用不同的标记直接说明我要在这里显示标题,在标题下面显示主体,然后在主体的某个部分插入图片。
这里写图片描述
上图的左边是html代码,右边是浏览器的现实结果;这里头的head/p/img都是不同的标记。

第一步:找到这个域名对应的IP地址

小明很懒,只留下了一个网址,剩下的所有工作都交给计算机自己去搞了。
计算机也不辞劳苦,尽心尽力。它知道,要想联系菜鸟网站,让它把c++的教程发过来,就首先需要找到它的IP地址。那么怎么找呢?
在网络上有一个专门的部门(由计算机组成)来做这件事,那就是域名系统DNS。
DNS专门负责从域名到IP地址的转换。在这个系统里面,是由服务器组成的。服务器可以理解为专门提供某种服务的计算机,比如DNS服务器就是提供域名到IP转换服务的计算机。
在这个系统里面分为不同的角色,根域名服务器是最高级的域名服务器,它知道所有顶级域名服务器的域名和IP;顶级域名服务器负责管理该顶级域名服务器下的所有二级域名;权限域名服务器掌管一个区。此外,还有一个本地域名服务器,它是默认的域名服务器,它可能在一个学院的里面、一个大学或者同一个网络提供商ISP里面。
这个系统流程大概是这样的:
(1):首先询问本地域名服务器:喂,你有没有www.runoob.com的ip地址啊?
在win7的IPV4选项里面,可以设置这个本地域名服务器:
这里写图片描述
上图可以看出,本地域名服务器的ip地址是202.96.134.133。如果你是第一次访问菜鸟网站,本地域名服务器就不知道菜鸟网站的地址。就会来到第二步。如果找到了对应IP地址那么寻找Ip地址的流程就愉快地结束了。
(2)本地域名服务器向根域名服务器求助:大佬,我这里有个域名解析请求,你知道它的IP吗?根域名服务器发现这个URL后面是.com结尾的,那么它就把分管.com的顶级域名服务器的地址告诉本地服务器。
(3)本地服务器问负责.com顶级域名服务器,菜鸟网站的IP是多少?顶级域名服务器发现二级域名是runnoob,然后就在系统里面查找分管runoob的权限服务器。然后将这个服务器地址交给本地域名服务器。
(4)于是本地服务器又问权限域名服务器;权限域名服务器说找到了,是183.61.180.185。
(5)本地域名服务器向主机报告DNS解析结果。
下图就是上述过程的流程图:
这里写图片描述
终于,找到了菜鸟网站的地址了。

如果小明在不久的将来继续访问菜鸟网站,就不用这么麻烦了。因为计算机也不傻,本地服务器会将这次的解析结果保存起来,下次再次查找IP地址的时候就直接通过本地域名服务器返回了。而不需要向根域名服务器查询。

第二步:浏览器向服务器发送http请求

前面讲到,http协议是处于应用层的超文本传送协议。顾名思义,这个协议是用来传输以.html为后缀名的超文本文件。
那么一个http请求报文包含哪些内容呢?
我们可以使用一个抓包工具wireshark来获取http请求报文:
这里写图片描述

上图说明:
(1)请求行使用了get方法,表示获取资源的意思。此外还有post(发送表单)、update(更新)、delete(删除)、put等方法。而get方法表示浏览器想要获取一个关于C++教程的html文档。
(2)Connection为keep-alive,为了简化服务器的设计,http协议是无状态的,就是服务器不知道谁访问过我,什么时候访问的,以及访问了多少次。并且http协议的下层是tcp协议,tcp协议是基于建立连接的可靠传输(下面会讲)。在http1.0版本,每传送一个资源就会自动断开连接。但是每次建立连接是非常消耗资源的,于是keep-alive就在一段时间内保持连接,节省资源。
(3)cookie的作用:由于http的无状态,使得服务器无法知道用户的访问信息。因此在用户和服务器之间必须使用一个类似令牌的东西,这样每当用户亮出令牌,服务器就知道是你,这样就可以记录访问信息了。cookie就是这样的一个东西。

这样,http请求报文构造好以后,就交给处于传输层的tcp协议。

第三步:浏览器与服务器建立TCP连接

TCP功能强大,责任也重大。

以我个人的理解,TCP就是建立一个端到端的可靠连接,并且为了避免网络的一些临时故障,增加了重传和拥塞控制的功能。
这里的端指的是什么?
这里的端是指套接字。我们将一个运行的程序成为进程(比如那打开的浏览器是一个进程),那么套接字就是为了表示一个唯一的进程的。 套接字包含了两个部分:IP和端口号。如果把一栋楼比作一个计算机,那么一个房间就是一个端口号。快递必须送到房间才算完成任务,单单送到楼房门口还不行。

利用IP地址和端口号组成的套接字了,就可以建立TCP连接了。
建立TCP连接是为了给应用层提供可靠无差错的通道,以便传送数据。

TCP的连接的建立与释放
这里写图片描述
上图看起来好复杂,似乎有一些没有必要的操作,为何不直接发送数据就得了?其实不然,所有的步骤都是为了保证达到可靠传输的标准。
(1)TCP三次握手:上图的前三步就是三次握手。为什么要三次握手?两次不就行了吗?不可以。举一个极端的例子,小明的电脑在发送完第一个报文就关机了。也就是说,虽然这个报文是小明的电脑发送的,但是过了一段时间,这个报文已经失效。如果服务器收到这个报文以为是小明的电脑刚刚发的,就发送一个答复给它,并建立了连接。但是服务器等啊等,迟迟等不到对方发消息过来。因此白白浪费了服务器资源。
(2)数据传送:建立连接以后,双方(应该是指浏览器进程跟服务器的服务进程)就可以互相发送数据了。浏览器会在本地电脑上随机选择一个端口,结合本地IP地址组成套接字,而服务器则一直监听着80(web服务默认端口)端口,等待着客户端的请求。当80端口收到数据以后,就知道有请求过来了,然后就根据报文内容发送一个响应报文回去。
(3)四次挥手:最后大家都发送完了,就双方依次发送一个信息发送完毕的信号,收到这个信号时,必须返回确认信号。这样连接就会断开。为什么需要四次?因为对方可能还有数据发给我,因此需要互相发送一个结束信号。为什么需要确认呢?为了避免对方一直保持连接。

下图是通过wireshark抓取的TCP报文:
这里写图片描述

其中包括了自己的端口:33224以及对方的端口:80;
此外还有序列号,确认号,窗口大小、检验和等字段,这些都是为了重传以及拥塞控制的,感兴趣的你可以查找资料深入学习TCP协议,这里就不细说了。

第四步:IP路由

传输层的下一层是网络层。网络层负责的工作是 找到一条通往目的计算机的道路。
在寻路 期间,IP包将会遇到很多“指路人”,这些“指路人”就是路由器。
路由器的作用就是用来路由的。

TCP报文会变成网络层的数据,网络层给TCP报文首部加上本地的IP地址和目的主机的IP地址,就变成了IP报文了。

下面的图可以看出IP报文的格式:
这里写图片描述
其中版本号为4表示用了IPv4,此外有头部长度、整个包的长度、校验和、本地和目的的IP地址等。

一个校园网的拓扑结构
这里写图片描述

现在我们假设小明是在一个学生宿舍里面,他用的电脑使用的网络是校园网。

那么小明使用的电脑首先会连接到宿舍的交换机。交换机就是一个属于数据链路层的设备,它有点类似于插座,就是可以插很多条网线。连接同一台交换机的电脑可以同时上网。

然后交换机会连接到校园网络中心的核心路由器,一般我们会叫做网关。

那么小明的电脑希望跟其他电脑通行,会首先到达网关,网关会查找目的IP地址是在内部网里面还是外网。如果内部网就可以直接通信了。如果是外部网就需要转发到其他的路由器,这样一直转发,直到最后一个与目的主机直连的路由器。然后这个路由器才将数据包发给目的主机。(其实这也说明了一个现象就是局域网内部电脑通信比较快,外部通信就慢一点,因为外部需要受到路由器和带宽的限制,于是上网速度就慢下来了)

上述过程是大致过程。下面在具体讲讲细节。
主要有三个重点吧,包括NAT、ARP以及路由选择。
(1)NAT
我们知道IP地址是由32位二进制表示的。按道理来说是可以表示很多电脑的了。但是呢,随着互联网的发展,这些IP地址居然快用完了,于是有人想了一个办法。就是IP地址复用呗。
规定一下范围的地址是私有地址,这些地址是不用在公网上流通的。
10.0.0.0~10.255.255.255(A类)
172.16.0.0~172.31.255.255(B类)
192.168.0.0~192.168.255.255(C类)

于是问题来了,我内部网的电脑要上网怎么办?
办法是人想出来的。答案是NAT,就是地址转换技术。它的流程如下:
这里写图片描述
(1)内网主机192.168.1.3需要访问服务器1.1.1.2。首先它会发送一个本地IP:192.168.1.3,目的IP为1.1.1.2的IP包。
(2)到了网关以后,就是用NAT技术将本地IP变成该路由器端口的公网IP,即本地IP:20.1.1.2,目的IP:1.1.1.2;这时需要用NAT地址转换表记录下转换:
这里写图片描述
(3)到了服务器后,服务器返回响应报文,到了网关 ,就查找NAT转换表,发现对应的内部网IP地址。
(4)网关将这个响应报文转发给本地主机。

这样就完成了访问外部网的过程。此外,还有NAPT,就是用端口号和公网IP地址来访问外部网,这样好处在于当内部网拥有少数公网IP时也能够让足够多的主机上网。

(2)路由选择
在路由选择之前,路由器需要运行路由算法来获取路由信息。
关于路由算法可以参阅本人写的博客:路由算法
经过上述路由算法的运行后,路由器就会有对应的路由表了,本地主机也有,打开cmd输入命令route print,得到以下结果:
这里写图片描述
根据这个路由表,如果要访问菜鸟网站的IP地址,183.61.180.185,就去查找这个路由表,如果查找不到就按默认路由(上述路由表的第一条)到达了网关,然后网关根据自己的路由表进行查找,查找的时候是按最长匹配原则进行路由的,就是会选择最长的匹配路由项。就这样,IP包经过了若干个路由器,最终到达菜鸟网站的主机。
使用tracert命令可以查看到达经过了哪些路由器:
这里写图片描述

tracert命令的原理是这样的:它从源主机向目的主机发送一系列的IP数据报,数据报封装的是无法交付的UDP数据报。第一个数据报的TTL(生存时间设置为1),第二个设置为2,这样每次TTL变成0,就会想源主机发送一个ICMP时间超时差错报文。最后一个数据报到了目的主机,但TTL并不会减为0,但是内容格式无法交付的UDP数据报,于是目的主机会向源主机发送ICMP终点不可达报文。

(3)ARP地址转换
首先得了解一下什么是MAC地址(硬件地址),MAC地址是48位的全球统一的物理地址。尽管互联在一起的网络的硬件千差万别,但是他们都是使用同一种物理地址格式。于是在网络层的下一层数据链路层,就不是使用IP地址而是使用物理地址来进行传输。
可能有人会问,为什么不直接使用IP地址呢?因为使用IP地址更加方便。在这个世界上,存在着各种各样的网络,它们使用不同的硬件地址,要使它们能够互相通信,需要使用非常复杂的硬件地址转换工作。但是IP的出现就解决了这个问题。因为使用IP,就好像在同一个网络上通信一样。

在cmd使用命令arp -a可以查看本地计算机的地址转换表:
这里写图片描述
可以看出IP地址跟MAC地址有着一对一的映射关系。

那这个到底是怎么做的呢?
请看下图:
这里写图片描述
现在假设主机A需要知道IP地址为192.168.1.2的MAC地址,那么它会想局域网内部广播ARP请求。这样每个主机都收到了ARP请求。但是收到请求每个主机都会比对IP地址是不是自身的IP地址(A是不是要找我?),只有匹配的IP地址才会响应。刚好主机B匹配了,于是,他就将自己的MAC地址单独传播给主机A。
那么如果是不同局域网呢?不同局域网就不能用一次广播就完成ARP转换。这时它会先向本地网关发送ARP请求。找到了网关的硬件地址后就讲IP数据报传送到网关了,这时网关又发送一个ARP请求,去找下一个路由器的硬件地址,就这样,最后找到了不同局域网的目的主机的MAC地址。

第五步:封装成帧后变成比特流

数据在网络层被封装成IP包后,就是数据链路层和物理层的事情了。
数据链路层主要考虑的是一个局域网(校园网)内部一台主机跟另一台主机(小明的电脑跟网关路由器)如何传输数据。而不像IP协议那样着眼于整个互联网。
这里写图片描述
上图是一种以太网帧的内容,可以看出内容包含了源物理地址和目的物理地址,类型,数据等内容。
数据链路层主要需要解决三个问题:
(1)我如何去标识一个帧的开始和结束?
在以太网中,是按一个比特一个比特地传输的,这时必须知道哪里是开头,哪里是结尾,才能将一个比特流当成一个整体。如果传输的是ASCII码字符,可以用两个控制字符SOH(0x1)和EOT(0x4)来标识一个帧,当发送端读取到这个以后就知道,噢,原来这是一个帧。
(2)当我要传输的数据包含帧的开始符或者结束符怎么办?
可以使用转义字符。当数据包含SOH或者EOT就在前面插入一个转义字符(这个字符可以随便选),来标识这不是帧的边界。
(3)传输的数据有没有出现差错?出现差错怎么办?
当出现比特差错时,可以用CRC循环校验(这是一种校验方法)。如果出现差错可以将此帧丢弃。也可以向上层协议汇报。
那么,这样数据链路层的工作就完成了。

接下来交给物理层。
物理层需要规定使用光纤还是双绞线还是无线电来传输数据,电平的值、以及编码方式都是物理层需要做的事情。它尽可能地让数据链路层觉得物理层传的就是0跟1,而不管物理层是怎么做到的。
在这里必须要讲的就是以太网的编码方式,即用什么来表示0跟1:
曼切斯特编码:
这里写图片描述
(1)曼切斯特编码:规定每一个周期中心向下跳变(以上图为例)为1,向上跳变为0.
(2)差分曼切斯特编码:规定位开始边界有跳变的为0,没有跳变的代表1.
就这样,数据就变成了比特流,流向菜鸟网站的服务器。

第六步:服务器收到比特流解析数据

经过一段时间后,菜鸟网站收到了一系列比特流。它们不知道这是什么。
但是它们按照分层思想和各种协议将内容读取了出来。
(1)首先,它采用慢切斯特编码将数字信号转化为二进制信息。
(2)然后再根据EthernetII的帧格式读取每一个帧。
(3)然后在以IP协议的格式读取每一个数据包。
(4)将IP包,去掉首部以后,就变成了TCP报文了。如果IP包是有分段的,就由好几个IP包组成TCP报文。
(5)服务器早已经开放80端口等待请求过来,这是就按HTTP协议解析内容。原来有用户需要我的c++教程。

第七步:服务器返回响应

从用户角度来讲,服务器返回响应是一个C++教程。
但是对于一个请求,服务器可能需要进行请求分类、运算、生成html代码文件,再向小明的主机发送一个http响应报文。
这一步的流程也是按部就班的,具体可以参考一些web服务器开发的书籍了解一下。
我们就来看看响应报文吧:
这里写图片描述
里面包括了http协议的版本、状态码、响应时间、数据的编码、以及一个html数据等。
然后服务器将这个报文发给了小明的主机。

第八步:浏览器解析网页文件并显示网页

这样又经过了层层的封装与解封,这个响应报文终于到了小明的浏览器了。
这时如何将html代码转化成c++教程成为浏览器内核的事情。
这里写图片描述
浏览器内核完成的事情就是根据图片右边的代码翻译成左边的教程。
那它是怎么做的呢?
首先需要识别一个个html的元素,哪个是标题,哪个是链接,哪个是图片。
其次它会构造一个语法分析树,这个树的节点包含了元素的各种信息。
然后浏览器按照这颗树就计算每一个元素的大小高度颜色等信息。
最后浏览器就成功显示出C++教程了。
这是一个大概的流程。具体可以参照这篇文档深入了解:浏览器是如何工作的。

总结:化大为小,分工明确,遵守规则

终于我写完了这篇博客,给我点个赞?
好现在我们总结一些计算机网络为何能够在如此复杂庞大的系统里面有条不紊地运转起来。
我总结为上面十二个字。
化大为小,分工明确。计算机网络最明智的地方就在于将计算机通信分为好几个层次。每个层次只专注于自己负责的部分。下层给上层提供服务。
遵守规则。每一层都有一些协议来实现这一层的功能。于是每一层就好像跟同一层通信一样,这样就大大简化了通行的流程。它们用协议来交流,使得接收方能够使用同一种协议解析出发送方的数据,最终实现了网络互联。