原文地址:http://www.pad2zero.top/index.php/archives/53/
问题
我们有时会浏览器用来打开一个本地的html文件,打开之后浏览器上的地址栏类似这种形式:
file:///users/a123123/Desktop/注册界面.html
有的时候我们使用Tomcat,访问的地址类似这种
http://localhost:8080/web_war_exploded/
这两种形式无疑访问的都是本地的资源,那么这两者之间的区别和背后的原理是什么呢?
注意:本文不会介绍协议栈基本工作原理,也不会介绍域名解析相关的知识
通信知识的简单介绍
两台计算机之间的通信从计算机网络的角度来讲实际上是两台计算机上进程之间的通信
两个进程之间通信的时候需要依赖协议栈,TCP/IP协议栈可以分为五层:应用层,传输层,网络层,数据链路层和物理层
在我们运行的电脑上,浏览器实际上就是一个进程(运行着的程序),当他要发送数据的时候,和普通的网络编程一样,需要将数据等相关信息交付给Socket(套接字),交付之后的数据就不再归浏览器管了,套接字然后再将数据交给传输层,传输层接着进行多路复用(封装数据),然后再继续往下传。最后由物理层传出去,经过链路和路由转发,到达另外一台计算机上,然后又从下向上多路分解,将数据交付给这台计算机上的进程。
在整个流程中,至少需要有两个标志,一个是目的IP地址,标志着另外一台主机所在的位置,还有一个是端口号,标志着另外一台主机上的进程(电脑上有多个进程在运行,需要彼此区分)。
浏览器解析URI
理解了通信的基本流程之后,我们来分析浏览器是怎样解析URI的,
在浏览器地址栏输入一个URI之后,一个基本的检查是包括在后面的步骤内的:
去hosts文件里面查找相关的IP
比如localhost,这个域名不需要浏览器发起DNS请求去解析,因为这个域名是系统默认的,在hosts文件里面:
这样我们也知道了,localhost其实就是对应一个IP地址:127.0.0.1
同时我们也知道Tomcat使用使用时后面还接上了一个端口号:8080
这是tomcat进程运行时的默认端口号
那么总的流程简单来说就是浏览器判断到了localhost是本地IP地址,就直接将请求发送给了进程
8080
,然后进程再返回一些信息回来
但是这样说还不够,结合协议栈,在这个过程中调用了哪些协议呢?
在这里浏览器使用到的应用层协议是HTTP,同样Tomcat也是,这样两者才能正确通信
应用层封装好数据后,经过套接字转给传输层,这里HTTP协议使用到的是TCP传输协议,Tomcat同样也是
接下来有趣的事就发生了(这里我还没有找到相关参考资料),我的猜测是在传输层会检验一次IP,判断属于本地地址(127.0.0.1即回环地址)之后,传输层不再继续封装,而是直接将数据发送给相应的(根据端口号)套接字(和目的进程绑定),套接字再将数据交付给进程。
实际上就是实现了本地进程之间的通信,我们可以用下面两个程序来模拟:
#UDPClient.py
from socket import *
servername = '127.0.0.1'
serverport = 12000
clinentSocket = socket(AF_INET,SOCK_DGRAM)
message = 'Hi there!'
clinentSocket.sendto(message.encode(),(servername,serverport))
print("send ok")
modifiedMessage, serverAddress = clinentSocket.recvfrom(2048)
print(modifiedMessage.decode())
clinentSocket.close()
我们利用套接字做了个客户端Client,将数据发送给传输层,实际上,大部分的网络编程都是这样做的,我们要发送的数据是“Hi there”,端口是12000
,IP地址是127.0.0.1
简单来讲流程就是这个进程要把数据发送给本地的另外一个进程,端口号是12000
这是接收端进程的代码:
from socket import *
serverPort = 12000
serverSocket = socket(AF_INET,SOCK_DGRAM)
serverSocket.bind(('',serverPort))
print ('the server is ready to receive:')
while 1:
message, clientAddress = serverSocket.recvfrom(2048)
print(clientAddress)
print("The message is %s"%(message))
serverSocket.sendto("This message is from 12000 process",clientAddress)
运行的效果
能看到一个58384号端口的进程给当前12000进程发送了一个数据
同样发送端也接收到了接收端的回应
File URI SCheme
百度上的很多教程将这种形式:
file:///
称之为File协议,根据维基百科:
The file URI scheme is a URI scheme defined in RFC 8089, typically used to retrieve files from within one's own computer.
Previously the file URI scheme was specified in RFC 1630 and RFC 1738. The Internet Engineering Task Force (IETF) published RFC 8089, obsoleting these RFCs, with "a syntax based on the generic syntax of RFC 3986 that is compatible with most existing usages."[1]
称之为协议是不准确的,因为协议至少是双方的,这应该是一种URI形式,就如同维基百科所讲。
浏览器解析到了这种形式的URI便相当于是一个本地进程去访问本地的文件资源一样
不会去调用协议栈
那么至此,这两者的区别就已经说完了。
参考资料:
1.https://www.cnblogs.com/xiaohuochai/p/9174471.html
2.http://www.360doc.com/content/19/0519/14/30647790_836709624.shtml