一:浏览器生成消息

学计算机网络听得最多的就是TCP/IP协议族,那什么是协议?
协议就是规则,共事的几方为达成统一的目标而制定的行动规则,大家都遵守规则目标才能达成。
就像插头和插座有国家标准一样,生产电器的厂家按标准制造出插头,生产插座的厂家按标准制作出插座,这样才能正常使用。
也可以把协议理解为日常的语言,两个人都说同样的语言才能沟通,一个说中文,一个说英语就没法沟通。
网络通信协议,或者可以扩大到计算机世界的所有协议,都是这样。
通信的各方按照提前约定的规则通信,“怎么说”,“怎么听”都有一定之规,这样才能达成统一的目标——通信。

1、解析URL+生成HTTP请求消息

浏览器先做两件事:解析URL,生成请求消息

URL:统一资源定位符,也就是网址

完整的URL包括:访问协议://用户名:密码@域名:端口/文件路径名
如:https://xxxxxx:123456@www.qq.com:80/wwwroot/index.html
域名之前的用户名密码叫做“HTTP身份验证”,属于一个单独的知识分支。
来自:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Authentication

这种 URL 已被弃用。在 Chrome 中,URL 中的 username:password@ 部分甚至会因为安全原因而被移除。Firefox 则会检查该站点是否真的需要身份验证,假如不是,则会弹出一个警告窗口:你即将使用用户名 username 登录 www.example.com 站点,但是该站点不需要进行身份验证。这可能是在试图进行欺诈。

“/”的理解:index/ 可以直接读作“index目录下”,root/ 可以直接读作“root目录下”,所以 / 就是目录下的意思。如果前面没有名字呢,那就是目录的最顶级——没有目录,或者叫顶级目录——/。

请求消息:对XX(URI)进行XX(HTTP方法)操作

URI:统一资源标识符,简单讲就是服务器上的某个文件
HTTP方法:GET、POST等
请求消息格式:请求行,消息头,消息体
响应消息格式:状态行,消息头,消息体

2、向DNS服务器查询IP地址

发送消息必须要知道对方的IP地址。

IP地址

互联网和局域网都是按照TCP/IP的理念来设计的。
大概的思路是:若干台计算机通过集线器组成子网,不同的子网通过路由器连接起来组成网络。
在网络中,所有的设备都会被分配一个IP地址,类似于现实中的“XX号XX室”。其中XX号对应的是某个子网,XX室对应的是某台主机。
相应地,在IP地址中分别叫子网号和主机号。
也就是说,IP地址=子网号+主机号。
发送消息的大概思路是:消息经集线器发送到最近的路由器,然后再发到下一个路由器,如此转发下去,到达目标主机所在的集线器,最后到达目标主机。
IP地址是一串 32bit 的数字,按 8bit 一组分为4组,用十进制表示再用圆点隔开。
用子网掩码来区分网络号和主机号。子网掩码有三种表示方式:十进制IP地址,单个数字,二进制展开。
主机地址全0代表整个子网,主机地址全1代表向整个子网广播。

为什么不直接使用IP地址作为访问方式?因为IP不好记。
为什么不直接使用域名作为访问方式?因为对网络硬件压力太大,处理效率太低。

查询IP地址的大概思路是:问一下最近的DNS服务器:XXX.COM的IP是多少,它就会回答:XXX.COM的IP地址是XXX.XXX.XXX.XXX。

DNS

域名系统,负责回答域名对应的IP地址,当然还有其他功能。
向DNS服务器查询也是请求-响应的模式,既如此,就要有相应的客户端。
相当于DNS客户端的部分叫DNS解析器。
这个客户端不是像浏览器这种显式存在的,DNS解析器只是一段程序,包含在操作系统的Socket库中。
操作系统的库是什么?就是可复用的程序组件仓库,对单个程序而言能减少代码编写量,对多个程序而言可以实现组件的标准化。
Socket库中的组件可以让其他程序调用操作系统的网络功能,解析器是其中一个组件。

浏览器虽然能生成请求消息,但是并不能直接发送消息,它需要委托操作系统来发送消息。
解析器也不能直接发送查询IP地址的请求消息,一样是要委托操作系统的协议栈。
协议栈:操作系统内的网络控制软件,也叫“协议驱动”,“TCP/IP驱动”。

“发送消息必须要知道对方的IP地址”,对DNS服务器也是一样。
不过DNS服务器的地址已经提前设置好了。

解析器的查询消息

来自解析器的查询消息包含3个信息:
a)域名:服务器、邮件服务器(邮箱地址@后面的部分)的名称
b)Class:网络类型
在最早的DNS设计中,互联网以外的其他网络类型也都被考虑到了,Class就是用来区分网络的信息。
不过目前除了互联网没有其他的网络,所以Class的值永远是IN。
c)记录类型:A(Address)记录对应IP地址;MX(Mail eXchange)记录对应邮件服务器
除了A和MX这两种,还有其他几种:
PTR:根据IP地址反查域名
CNAME:查询域名别名
NS:查询DNS服务器IP地址
SOA:查询域名属性信息

比如,解析器想要查询:
a)www.qq.com
b)Class:IN
c)A
DNS服务器查询同时满足这三个条件的记录然后返回。
如果查询的是MX记录,除了IP地址还会返回优先级。
当一个邮件地址对应多个邮件服务器时,需要根据优先级来判断,数值较小的更优先。

DNS服务器响应的信息被称为“资源记录”。

域名的层次结构

要理解DNS的工作流程,需要先了解域名的层次结构。
为什么要有层次结构?因为全世界域名太多,无法将所有的域名信息都保存在同一台服务器中,只能分散保存,接力查询。
DNS服务器中的所有信息都是按照域名以分层的结构来保存的,域名像IP地址一样使用句点分隔,层次从左向右递增。
这里的“层次”,类似于现实中的上下级层次,比如mail.qq.com就可以说成com集团qq公司mail部门。
每一个层级称为一个域,如上面的com域,qq域,mail域。
每个域都作为一个整体来处理,一个域不会拆开存放到多态服务器上,但一台服务器可以存放多个域的信息。
为了更清楚地讲解DNS服务器的接力过程,这里假设一台服务器只存放一个域的信息。
在域名-服务器一对一关系的假设下,DNS服务器也有了像域名一样的层次结构,每个域的信息都存放在相应层级的服务器中。

DNS服务器的层次结构

a)层次结构的生成:向上注册
接力查询不是漫无目的的随意查询,是有先有后的顺序查询。
要实现这种查询,就要在开始的存储中按域名本身的层次结构存放。
也就是把下级域名所在的DNS服务器IP地址注册到上级域名所在的DNS服务器,这个服务器的IP地址再注册到更上一级,以此类推。
比如域名news.qq.com,先把news.qq.com所在的DNS服务器IP地址注册到qq.com的DNS服务器,再把qq.com的DNS服务器IP地址注册到com域的DNS服务器。
像com,cn这些叫顶级域名,上面还有一个负责管理它们的根域,对应的DNS服务器就是根域服务器。
根域服务器的IP地址全世界共有13个,也是提前配置在DNS服务器中。

b)层次结构的利用:接力查询

3、委托协议栈发送消息

准备好了请求信息,也查询到了IP地址,下面就要开始向服务器发送消息了。
发送消息的操作不仅限于浏览器,对于各种使用网络的应用程序来说都是通用的。
因此委托协议栈发送消息的过程不仅适用于Web,也适用于所有的网络应用程序。

向操作系统的协议栈发出委托时,需要按一定顺序调用Socket库中的程序组件。
发送数据是一系列操作结合起来实现的,如果不去理解整个过程的全貌,就无法理解每个操作的意义。

数据流通的大概样子

设想在客户端和服务器之间有一条管道,数据从这边被送入,然后在另一边被取出。

数据可以从任意一段进入管道,数据的流动是双向的。
管道并不是一开始就有,需要双方共同建立。
建立管道的关键在于两端的出入口,这些出入口称为套接字。
需要先创建套接字,然后将两个套接字连接起来,就形成了管道。

更具体一点的过程

服务器启动后会自动创建套接字,等待客户端连接。
客户端创建套接字,连接服务器的套接字,形成管道。
送入数据,进行通信。
通信完成后,随便哪一方都可以断开连接的管道。
断开连接后,套接字也被删除,通信结束。

总结起来

四个步骤:创建、连接、通信、断开。
创建:客户端创建套接字
连接:连接到服务器的套接字形成管道
通信:收发数据
断开:断开连接并删除套接字

这些操作不是浏览器做的,都是委托操作系统的协议栈做的。
如何“委托”?就是调用Socket库中的程序组件。
也就是说,过程是:浏览器→socket库程序→协议栈。
不光浏览器什么都没干,socket库程也什么都没干,所有的活都是协议栈干的。
Socket库中的程序组件只充当桥梁的角色,把浏览器的委托原原本本地给到协议栈。
只是为了方便说明,才在表述中把socket库程和协议栈当成一体,说是浏览器“委托协议栈”,实际上浏览器根本不能直接和协议栈打交道,要找socket库程当中间人。

三个词的区别:
Socket:库
socket:库中的程序组件
套接字:管道两端的接口

详细过程

创建:调用socket(注意:这个组件名字就叫socket),创建完成。协议栈返回该套接字的描述符,描述符可以理解为类似变量名的东西。
连接:调用connect,提供客户端套接字描述符、服务器IP地址、服务器端口号三个参数,连接完成。
为什么不直接用IP地址,还要有端口号?
IP地址只能定位到主机(确切说主机中的网网络硬件),而连接操作的对象是服务器上某个具体的套接字。
为什么不能用描述符而要用端口号?
描述符是本地程序和套接字交互用的号码牌,不是给对方看的,双方互相都不知道对方的描述符,也不需要知道。
端口号是专门用来让通信的另一方识别自己的套接字机制。
套接字一个身份两个称呼,在本地叫它描述符,对外叫它用端口号。
既然要用端口号,那如何确定用什么样的端口号呢?
端口号的规则是全球统一的,为了避免重复和冲突,端口号和IP一样都是由IANA统一管理。
也就是说,服务器上的端口号以及根据应用事先规定好了,无需操心。
比如Web的80端口,邮件的25端口。
通信:调用write指定描述符,发送数据(HTTP请求消息)。
在前面的连接操作后,通信管道已经建立,只需要将东西放进这边的管道出入口就大功告成了。
服务器收到信息后,解析并执行相应操作,然后返回响应消息。
应用程序调用名为read的Socket组件接收响应消息,并将消息放入接收缓冲区。
这一内存区域属于应用程序,当响应消息放入接收缓冲区就等于交给了应用程序。
断开:调用close断开连接,套接字被删除

posted @ 2023-02-26 22:36  GPL-技术沉思录  阅读(55)  评论(0编辑  收藏  举报