网络是怎样连接的学习笔记(一)

第一章、浏览器生成消息-探索浏览器内部
1.1生成Http请求消息
1.1.1输入网址
通过浏览器访问Web服务器是从在浏览器地址栏中输入网址或者点击一个链接开始的。
网址就是URL,根据访问目标的不同,URL的写法也不同,比如在访问Web服务器和FTP服务器时,URL中会包含服务器的域名和要访问的文件的路径名等。
而发邮件的URL则包括收件人的邮箱地址,此外根据需要,URL中还会包含用户名、密码、端口号等信息。
尽管URL有各种不同的写法,但它们有一个共同点,那就是URL开头的文字,即“http:”“ftp:”“file:”“mailto:”这部分文字都表示浏览器应当使用的访问方法。比如当访问Web服务器时应该使用HTTPA协议,而访问FTP服务器时则应该使用FTP协议。因此,我们可以把这部分理解为访问时使用的协议类型。
输入URL之后,浏览器要做的第一步工作就是解析URL,从而生成发送给Web服务器的请求消息。
1.1.2URL的解析
1)http://www.lab.glasscom.com/dir/file1.html这个地址其中包含Web服务器名称www.lab.glasscom.com,以及文件的路径名/dir1/file1.html。
因此我们就能够明白这个URL表示要访问www.lab.glasscom.com 这个Web服务器上路径名为/dir/file1.html 的文件,也就是位于/dir/目录下的file1.html这个文件。
2)也会有http://www.lab.glasscom.com/dir/这种以"/"结尾的URL,这表示/dir/后面本来应该有的文件名被省略了。没有文件名,服务器怎么样知道要访问的是什么文件呢。
这种情况下,我们会在服务器上事先设置好文件名省略时要访问的默认文件名。大多数情况下是index.html或者default.htm之类的文件名。因此,像前面这样省略文件名时,服务器就会访问/dir/index.html或者/dir/default.htm。
3)还有一些URL是像http://www.lab.glasscom.com/这种形式的URL。以"/"结尾表示访问一个叫做"/"的目录(根目录),并且它也省略了文件名。所以它表示访问/index.html或者/default.htm这样的文件。
4)还有URL是http://www.lab.glasscom.com这种连目录名都省略了的形式。当没有路径名时,就代表访问根目录下事先设置的默认文件。这就和第二种情况相同。
5)还会有http://www.lab.glasscom.com/whatisthis这种形式的URL,前面这个例子中,由于末尾没有"/",所以whatisthis应该理解为文件名才对。
但实际上,很多人并没有正确理解省略文件名的规则,经常会把目录末尾的"/"也给省略了。因此,或许我们不应该总是将whatisthis作为文件名来处理。
一般来说,这种情况会按照下面的惯例进行处理:如果Web 服务器上存在名为whatisthis的文件,则将whatisthis作为文件名来处理;如果存在名为whatisthis的目录,则将whatisthis作为目录名来处理。(不会存在同一目录下文件和文件夹名字相同的情况)
1.1.3HTTP的基本思路
HTTP协议定义了客户端和服务器之间交互的消息内容和步骤。请求消息的内容是“对什么”和“进行怎样的操作”两部分。其中对什么的部分称为“URI”,URI的内容是存放网页数据的文件名或者cgi程序的文件名。例如“/dir1/file1.html”
“/dir1/program1.cgi”等。也可以是“http:”开头的URL来作为URI。相当于进行怎样的操作的部分称为方法。方法表示需要让Web服务器完成怎样的工作,其中典型的例子包括读取URI表示的数据、将客户端输入的数据发送给URI表示的程序等。
HTTP的主要方法包括
GET:获取URI 指定的信息。如果URI指定的是文件,则返回文件的内容;如果URI指定的是CGI程序,则返回该程序的输出数据
POST:从客户端向服务器发送数据。一般用于发送表单中填写的数据等情况下
PUT:替换URI指定的服务器上的文件。如果URI指定的文件不存在,则创建该文件
DELETE:删除URI指定的服务器上的文件
收到请求消息之后,Web服务器会对其中的内容进行解析,通过URI和方法来判断“对什么”“进行怎样的操作”,并根据这些要求来完成自己的工作,然后将结果存放在响应消息中。在响应消息的开头有一个状态码,
它用来表示操作的执行结果是成功还是发生了错误。当我们访问Web服务器时,遇到找不到的文件就会显示出404 Not Found 的错误信息,其实这就是状态码。状态码后面就是头字段和网页数据。响应消息会被发送回客户端,客户端收到之后,浏览器会从消息中读出所需的数据并显示在屏幕上。到这里,HTTP的整个工作就完成了。
1.1.3生成HTTP请求消息
对URL 进行解析之后,浏览器确定了Web服务器和文件名,接下来就是根据这些信息来生成HTTP请求消息了。
首先,请求消息的第一行称为请求行。这里的重点是最开头的方法,方法可以告诉Web 服务器它应该进行怎样的操作。不过这里必须先解决一个问题,那就是方法有很多种,我们必须先判断应该选用其中的哪一种。解决这个问题的关键在于浏览器的工作状态。这次的请求是从在浏览器顶部的地址栏中输入网址开始的,但浏览器并非只有在这一种场景下才会向Web服务器发送请求消息。比如点击网页中的超级链接B,或者在表单中填写信息后点击“提交”按钮,这些场景都会触发浏览器的工作,而选用哪种方法也是根据场景来确定的。我们的场景是在地址栏中输入网址并显示网页,因此这里应该使用GET方法。点击超级链接的场景中也是使用GET方法。如果是表单,在HTML源代码中会在表单的属性中指定使用哪种方法来发送请求,可能是GET也可能是POST。写好方法之后,加一个空格,然后写URI。URI部分的格式如下,一般是文件和程序的路径名。
第一行是请求行,第二行是请求头,包含一些附加信息,加个空行,后面是消息体。
(a)请求消息
<方法><空格><URI><空格><HTTP版本>
<字段名>:<字段值>
...
...
...
<空行>
<消息体>
(b)响应消息
<HTTP协议><空格><状态码><空格><状态短语>
<字段名>:<字段值>
...
...
...
<空行>
<消息体>
响应消息的格式和请求消息基本相似,差别就在第一行,在响应消息中,第一行的内容为状态码和响应短语,用来表示请求的执行结果是成功还是出错。状态码和响应短语表示的内容一致,但它们的用途不同。状态码是一个数字,它主要用来向程序告知执行的结果。相对地,响应短语则是一段文字,用来向人们告知执行的结果。
状态码 含义
1xx 告知请求的处理进度和情况
2xx 成功
3xx 表示需要进一步操作
4xx 客户端错误
5xx 服务器错误
返回响应消息之后,浏览器会将数据提取出来并显示在屏幕上,我们就能够看到网页的样子了。如果网页的内容只有文字,那么到这里就全部处理完毕了,但如果网页中还包括图片等资源,则还有下文。
当网页中包含图片时,会在网页中的相应位置嵌入表示图片文件的标签的控制信息。浏览器会在显示文字时搜索相应的标签,当遇到图片相关的标签时,会在屏幕上留出用来显示图片的空间,然后再次访问Web服务器,按照标签中指定的文件名向Web服务器请求获取相应的图片并显示在预留的空间中。这个步骤和获取网页文件时一样,只要在URI 部分写上图片的文件名并生成和发送请求消息就可以了。
由于每条请求消息中只能写1个URI,所以每次只能获取1个文件,如果需要获取多个文件,必须对每个文件单独发送1条请求。比如1个网页中包含3张图片,那么获取网页加上获取图片,一共需要向Web服务器发送4条请求。

1.2向DNS服务器查询Web服务器的IP地址
1.2.1IP地址的基本知识
生成HTTP消息之后,接下来我们需要委托操作系统将消息发送给Web服务器。尽管浏览器能够解析网址并生成HTTP 消息,但它本身并不具备将消息发送到网络中的功能,因此这一功能需要委托操作系统来实现。在进行这一操作时,我们还有一个工作需要完成,那就是查询网址中服务器域名对应的IP地址。
IP地址是一串32比特的数字,按照8比特(1 字节)为一组分成4组,分别用十进制表示然后再用圆点隔开。这就是我们平常经常见到的IP地址格式,但仅凭这一串数字我们无法区分哪部分是网络号,哪部分是主机号。在IP地址的规则中,网络号和主机号连起来总共是32 比特,但这两部分的具体结构是不固定的。在组建网络时,用户可以自行决定它们之间的分配关系,因此,我们还需要另外的附加信息来表示IP地址的内部结构。这一附加信息称为子网掩码。子网掩码是一串与IP地址长度相同的32比特数字,其左边一半都是1,右边一半都是0。其中,子网掩码为1的部分表示网络号,子网掩码为0的部分表示主机号。
(a)IP地址主体的表示方法
10.11.12.13
(b)采用与IP地址主体相同格式的子网掩码的方法
IP地址/子网掩码
10.11.12.13/255.255.255.0
(c)采用网络号比特数来表示子网掩码的方法
IP地址/子网掩码网络号比特位数
10.11.12.13/24
(d)表示子网的地址
10.11.12.0/24
主机号部分的比特全部为0,这个地址表示的不是单独一台计算机,而是代表整个子网。
(e)表示子网内广播的地址
10.11.12.255/24
主机号部分的比特数全部为1,这个地址表示向整个子网内广播。
1.2.2Socket库提供查询IP地址的功能
查询IP地址的方法非常简单,只要询问最近的DNS服务器“www.lab.glasscom.com 的IP 地址是什么”就可以了,DNS服务器会回答说“该服务器的IP地址为xxx.xxx.xxx.xxx”。
向DNS服务器发出查询,也就是向DNS服务器发送查询消息,并接收服务器返回的响应消息。
换句话说,对于DNS服务器,我们的计算机上一定有相应的DNS客户端,而相当于DNS客户端的部分称为DNS解析器,或者简称解析器。
通过DNS查询IP地址的操作称为域名解析,因此负责执行解析(resolution)这一操作的就叫解析器(resolver)了。
解析器实际上是一段程序,它包含在操作系统的Socket库中,Socket库中包含的程序组件可以让其他的应用程序调用操作系统的网络功能,而解析器就是这个库中的其中一种程序组件。
1.2.3通过解析器向DNS服务器发出查询
Socket库中的程序都是标准组件,只要从应用程序中进行调用就可以了。
格式如下:
<内存地址>= gethostbyname("www.lab.glasscom.com");
写上解析器的程序名称“gethostbyname”以及Web服务器的域名“www.lab.glasscom.com”,这样就完成了对解析器的调用。
调用解析器后,解析器会向DNS服务器发送查询消息,然后DNS服务器会返回响应消息。响应消息中包含查询到的IP地址,解析器会取出IP地址,并将其写入浏览器指定的内存地址中。
我们也就完成了IP地址的查询。接下来,浏览器在向Web服务器发送消息时,只要从该内存地址取出IP地址,并将它与HTTP请求消息一起交给操作系统就可以了。
解析器本身也不具备使用网络收发数据的功能。解析器会调用操作系统的协议栈,由协议栈执行发送消息的操作,然后通过网卡将消息发送给DNS服务器。
如果要访问的Web服务器已经在DNS服务器上注册,那么这条记录就能够被找到,然后其IP地址会被写入响应消息并返回给客户端消息经过网络到达客户端,再经过协议栈被传递给解析器然后解析器读取出消息取出IP地址,并将将取出的IP地址写入应用程序指定的内存地址中。
向DNS服务器发送消息时,我们当然也需要知道DNS服务器的IP地址。只不过这个IP地址是作为TCP/IP的一个设置项目事先设置好的,不需要再去查询了。

1.3DNS服务器工作方式
1.3.1DNS服务器的基本工作
DNS服务器的基本工作就是接收来自客户端的查询消息,然后根据消息的内容返回响应。其中,来自客户端的查询消息包含以下3种信息。
(a)域名:服务器、邮件服务器(邮件地址中@ 后面的部分)的名称
(b)Class:在最早设计DNS方案时,DNS在互联网以外的其他网络中的应用也被考虑到了,而Class就是用来识别网络的信息。不过,如今除了互联网并没有其他的网络了,因此Class的值永远是代表互联网的IN
(c)记录类型:表示域名对应何种类型的记录。例如,当类型为A时,表示域名对应的是IP地址;当类型为MX时,表示域名对应的是邮件服务器。
DNS服务器上事先保存有前面这3种信息对应的记录数据。DNS服务器就是根据这些记录查找符合查询请求的内容并对客户端作出响应的。
1.3.2域名的层次结构
DNS服务器中的所有信息都是按照域名以分层次的结构来保存的。在域名中,越靠右的位置表示其层级越高这种具有层次结构的域名信息会注册到DNS服务器中,而每个域都是作为一个整体来处理的。
换句话说就是,一个域的信息是作为一个整体存放在DNS服务器中的,不能将一个域拆开来存放在多台DNS服务器中。
不过,DNS服务器和域之间的关系也并不总是一对一的,一台DNS服务器中也可以存放多个域的信息。
1.3.3寻找相应的DNS服务器获取IP地址
这里的关键在于如何找到我们要访问的Web服务器的信息归哪一台DNS服务器管。
首先,将负责管理下级域的DNS服务器的IP地址注册到它们的上级DNS服务器中,然后上级DNS服务器的IP地址再注册到更上一级的DNS服务器中,以此类推。
也就是说,负责管理lab.glasscom.com这个域的DNS服务器的IP地址需要注册到glasscom.com域的DNS服务器中而glasscom.com域的DNS服务器的IP地址又需要注册到com域的DNS服务器中。
这样,我们就可以通过上级DNS服务器查询出下级DNS服务器的IP 地址,也就可以向下级DNS服务器发送查询请求了。
在com、jp这类域之上还有一层域是根域根域不像com、jp那样有自己的名字,因此在一般书写域名时经常被省略,如果要明确表示根域,应该像www.lab.glasscom.com.这样在域名的最后再加上一个句点,而这个最后的句点就代表根域。
不过,一般都不写最后那个句点,因此根域的存在往往被忽略,但根域毕竟是真实存在的,根域的DNS服务器中保管着com、jp 等的DNS服务器的信息。
由于上级DNS服务器保管着所有下级DNS服务器的信息,所以我们可以从根域开始一路往下顺藤摸瓜找到任意一个域的DNS 服务器。
除此之外还需要完成另一项工作,那就是将根域的DNS服务器信息保存在互联网中所有的DNS服务器中。这样一来,任何DNS服务器就都可以找到并访问根域DNS服务器了。
因此,客户端只要能够找到任意一台DNS服务器,就可以通过它找到根域DNS服务器,然后再一路顺藤摸瓜找到位于下层的某台目标DNS服务器。
分配给根域DNS服务器的IP地址在全世界仅有13个,而且这些地址几乎不发生变化,因此将这些地址保存在所有的DNS服务器中也并不是一件难事。
实际上,根域DNS服务器的相关信息已经包含在DNS服务器程序的配置文件中了,因此只要安装了DNS服务器程序,这些信息也就被自动配置好了。

1.4委托协议栈发送消息
1.4.1数据收发操作概览
知道了IP地址之后,就可以委托操作系统内部的协议栈向这个目标IP地址,也就是我们要访问的Web服务器发送消息了。
要发送给Web服务器的HTTP消息是一种数字信息(digital data),因此也可以说是委托协议栈来发送数字信息。收发数字信息这一操作不仅限于浏览器,
对于各种使用网络的应用程序来说都是共通的。因此,这一操作的过程也不仅适用于Web,而是适用于任何网络应用程序。
向操作系统内部的协议栈发出委托时,需要按照指定的顺序来调用Socket 库中的程序组件。
使用Socket库来收发数据的操作过程,就是收发数据的两台计算机之间连接了一条数据通道,数据沿着这条通道流动,最终到达目的地。
我们可以把数据通道想象成一条管道,将数据从一端送入管道,数据就会到达管道的另一端然后被取出。数据可以从任何一端被送入管道,数据的流动是双向的。
在进行收发数据操作之前,双方需要先建立起这条管道才行。建立管道的关键在于管道两端的数据出入口,这些出入口称为套接字。我们需要先创建套接字,然后再将套接字连接起来形成管道。
首先,服务器一方先创建套接字,然后等待客户端向该套接字连接管道。当服务器进入等待状态时,客户端就可以连接管道了。
具体来说,客户端也会先创建一个套接字,然后从该套接字延伸出管道,最后管道连接到服务器端的套接字上。
当双方的套接字连接起来之后,通信准备就完成了。接下来,就像我们刚刚讲过的一样,只要将数据送入套接字就可以收发数据了。
当数据全部发送完毕之后,连接的管道将会被断开。管道在连接时是由客户端发起的,但在断开时可以由客户端或服务器任意一方发起。
其中一方断开后,另一方也会随之断开,当管道断开后,套接字也会被删除。到此为止,通信操作就结束了。
综上所述,收发数据的操作分为若干个阶段,可以大致总结为以下4个。
(1)创建套接字(创建套接字阶段)
(2)将管道连接到服务器端的套接字上(连接阶段)
(3)收发数据(通信阶段)
(4)断开管道并删除套接字(断开阶段)
前面这4个操作都是由操作系统中的协议栈来执行的,浏览器等应用程序并不会自己去做连接管道、放入数据这些工作,而是委托协议栈来代劳。
这些委托的操作都是通过调用Socket 库中的程序组件来执行的,但这些数据通信用的程序组件其实仅仅充当了一个桥梁的角色,并不执行任何实质性的操作,应用程序的委托内容最终会被原原本本地传递给协议栈。
1.4.2创建套接字阶段
应用程序(浏览器)委托收发数据过程的关键点就像对DNS服务器发送查询一样,调用Socket库中的特定程序组件。
访问DNS 服务器时我们调用的是一个叫作gethostbyname的程序组件(也就是解析器),而这一次则需要按照一定的顺序调用若干个程序组件。
首先是套接字创建阶段。客户端创建套接字的操作非常简单,只要调用Socket库中的socket程序组件就可以了。
套接字创建完成后,协议栈会返回一个描述符,应用程序会将收到的描述符存放在内存中。描述符是用来识别不同的套接字的。
因为计算机中会同时进行多个数据的通信操作,比如可以打开两个浏览器窗口,同时访问两台Web 服务器。这时,有两个数据收发操作在同时进行,也就需要创建两个不同的套接字。
在这样的情况下,我们就需要一种方法来识别出某个特定的套接字,这种方法就是描述符。
1.4.3连接阶段:把管道接上去
接下来,我们需要委托协议栈将客户端创建的套接字与服务器那边的套接字连接起来。应用程序通过调用Socket库中的名为connect的程序组件来完成这一操作。这里的要点是当调用connect 时,需要指定描述符、服务器IP地址和端口号这3个参数。
第1 个参数,即描述符,就是在创建套接字的时候由协议栈返回的那个描述符。connect会将应用程序指定的描述符告知协议栈,然后协议栈根据这个描述符来判断到底使用哪一个套接字去和服务器端的套接字进行连接,并执行连接的操作。
第2 个参数,即服务器IP地址,就是通过DNS服务器查询得到的我们要访问的服务器的IP地址。在DNS服务器的部分已经讲过,在进行数据收发操作时,双方必须知道对方的IP地址并告知协议栈。这个参数就是那个IP地址了。
第3 个参数,即端口号,连接操作的对象是某个具体的套接字,因此必须要识别到具体的套接字才行,端口就是用来识别计算机上某个具体的套接字。
确定连接对象的套接字需要使用端口号,服务器也得知道客户端的套接字号码才能给客户端发送数据。
因此,客户端在创建套接字时,协议栈会为这个套接字随便分配一个端口号。接下来,当协议栈执行连接操作时,会将这个随便分配的端口号通知给服务器。
1.4.4通信阶段:传递消息
当套接字连接起来之后,只要将数据送入套接字,数据就会被发送到对方的套接字中。当然,应用程序无法直接控制套接字,因此还是要通过Socket库委托协议栈来完成这个操作。这个操作需要使用write这个程序组件。
首先,应用程序需要在内存中准备好要发送的数据。根据用户输入的网址生成的HTTP 请求消息就是我们要发送的数据。接下来,当调用write时,需要指定描述符和发送数据,
然后协议栈就会将数据发送到服务器。由于套接字中已经保存了已连接的通信对象的相关信息,所以只要通过描述符指定套接字,就可以识别出通信对象,并向其发送数据。
接下来,服务器执行接收操作,解析收到的数据内容并执行相应的操作,向客户端返回响应消息。
当消息返回后,需要执行的是接收消息的操作。接收消息的操作是通过Socket库中的read程序组件委托协议栈来完成的。
调用read时需要指定用于存放接收到的响应消息的内存地址,这一内存地址称为接收缓冲区。于是,当服务器返回响应消息时,read就会负责将接收到的响应消息存放到接收缓冲区中。
由于接收缓冲区是一块位于应用程序内部的内存空间,因此当消息被存放到接收缓冲区中时,就相当于已经转交给了应用程序。
1.4.5:断开阶段:收发数据结束
当浏览器收到数据之后,收发数据的过程就结束了。接下来,我们需要调用Socket库的close程序组件进入断开阶段。最终,连接在套接字之间的管道会被断开,套接字本身也会被删除。
断开的过程如下。Web 使用的HTTP协议规定,当Web服务器发送完响应消息之后,应该主动执行断开操作,因此Web服务器会首先调用close来断开连接。断开操作传达到客户端之后,客户端的套接字也会进入
断开阶段。接下来,当浏览器调用read执行接收数据操作时,read会告知浏览器收发数据操作已结束,连接已经断开。浏览器得知后,也会调用close进入断开阶段。

(1)准备:<描述符> = socket(<使用IPv4>,<流模式> ...);
...
(2)连接:connect(<描述符>,<服务器的IP地址和端口号>,...);
...
(3)发送:write(<描述符>,<发送数据>,<发送数据长度>);
...
(4)接收:<接收数据长度>=read(<描述符>,<接收数据缓冲区>,...);
...
(5)断开:close(<描述符>);
...
这就是HTTP的工作过程。HTTP 协议将HTML文档和图片都作为单独的对象来处理,每获取一次数据,就要执行一次连接、发送请求消息、接收响应消息、断开的过程。
因此,如果一个网页中包含很多张图片,就必须重复进行很多次连接、收发数据、断开的操作。对于同一台服务器来说,重复连接和断开显然是效率很低的,因此后来人们又设计出了能够在一次连接中收发多个请求和响应的方法。
在HTTP 版本1.1 中就可以使用这种方法,在这种情况下,当所有数据都请求完成后,浏览器会主动触发断开连接的操作。
  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值