原文: https://github.com/skyline75489/what-happens-when-zh_CNcss
这个仓库试图回答一个古老的面试问题:当你在浏览器中输入google.com而且按下回车以后发生了什么?html
不过咱们再也不局限于日常的回答,而是想办法回答地尽量具体,不遗漏任何细节。node
这将是一个协做的过程,因此深刻挖掘吧,而且帮助咱们一块儿完善它。仍然有大量的细节等待着你来添加,欢迎向咱们发送Pull Requset!linux
这些内容使用 Creative Commons Zero 协议发布。git
为了从头开始,咱们选择键盘上的回车键被按到最低处做为起点。在这个时刻,一个专用于回车键的电流回路被直接或者经过电容器闭合了,使得少许的电流进入了键盘的逻辑电路系统。这个系统会扫描每一个键的状态,对于按键开关的电位弹跳变化进行噪音消除(debounce),并将其转化为键盘码值。在这里,回车的码值是13。键盘控制器在获得码值以后,将其编码,用于以后的传输。如今这个传输过程几乎都是经过通用串行总线(USB)或者蓝牙(Bluetooth)来进行的,之前是经过PS/2或者ADB链接进行。github
USB键盘:面试
虚拟键盘(触屏设备):算法
键盘在它的中断请求线(IRQ)上发送信号,信号会被中断控制器映射到一个中断向量,实际上就是一个整型数 。CPU使用中断描述符表(IDT)把中断向量映射到对应函数,这些函数被称为中断处理器,它们由操做系统内核提供。当一个中断到达时,CPU根据IDT和中断向量索引到对应的中端处理器,而后操做系统内核出场了。windows
HID把键盘按下的事件传送给 KBDHID.sys 驱动,把HID的信号转换成一个扫描码(Scancode),这里回车的扫描码是 VK_RETURN(0x0d)。 KBDHID.sys 驱动和 KBDCLASS.sys (键盘类驱动,keyboard class driver)进行交互,这个驱动负责安全地处理全部键盘和小键盘的输入事件。以后它又去调用 Win32K.sys ,在这以前有可能把消息传递给安装的第三方键盘过滤器。这些都是发生在内核模式。浏览器
Win32K.sys 经过 GetForegroundWindow() API函数找到当前哪一个窗口是活跃的。这个API函数提供了当前浏览器的地址栏的句柄。Windows系统的"message pump"机制调用 SendMessage(hWnd, WM_KEYDOWN, VK_RETURN, lParam) 函数, lParam 是一个用来指示这个按键的更多信息的掩码,这些信息包括按键重复次数(这里是0),实际扫描码(可能依赖于OEM厂商,不过一般不会是 VK_RETURN ),功能键(alt, shift, ctrl)是否被按下(在这里没有),以及一些其余状态。
Windows的 SendMessage API直接将消息添加到特定窗口句柄 hWnd 的消息队列中,以后赋给 hWnd 的主要消息处理函数 WindowProc 将会被调用,用于处理队列中的消息。
当前活跃的句柄 hWnd 其实是一个edit control控件,这种状况下,WindowProc 有一个用于处理WM_KEYDOWN 消息的处理器,这段代码会查看 SendMessage 传入的第三个参数 wParam ,由于这个参数是 VK_RETURN ,因而它知道用户按下了回车键。
中断信号引起了I/O Kit Kext键盘驱动的中断处理事件,驱动把信号翻译成键码值,而后传给OS X的WindowServer 进程。而后, WindowServer 将这个事件经过Mach端口分发给合适的(活跃的,或者正在监听的)应用程序,这个信号会被放到应用程序的消息队列里。队列中的消息能够被拥有足够高权限的线程使用 mach_ipc_dispatch 函数读取到。这个过程一般是由 NSApplication 主事件循环产生而且处理的,经过 NSEventType 为 KeyDown 的 NSEvent 。
当使用图形化的 X Server 时,X Server会按照特定的规则把键码值再一次映射,映射成扫描码。当这个映射过程完成以后, X Server 把这个按键字符发送给窗口管理器(DWM,metacity, i3等等),窗口管理器再把字符发送给当前窗口。当前窗口使用有关图形API把文字打印在输入框内。
浏览器经过URL可以知道下面的信息:
- Protocol "http"
使用HTTP协议
- Resource "/"
请求的资源是主页(index)
当协议或主机名不合法时,浏览器会将地址栏中输入的文字传给默认的搜索引擎。大部分状况下,在把文字传递给搜索引擎的时候,URL会带有特定的一串字符,用来告诉搜索引擎此次搜索来自这个特定浏览器。
要想发送ARP广播,咱们须要有一个目标IP地址,同时还须要知道用于发送ARP广播的接口的Mac地址。
若是缓存没有命中:
ARP Request:
Sender MAC: interface:mac:address:here Sender IP: interface.ip.goes.here Target MAC: FF:FF:FF:FF:FF:FF (Broadcast) Target IP: target.ip.goes.here
根据链接主机和路由器的硬件类型不一样,能够分为如下几种状况:
直连:
集线器:
交换机:
ARP Reply:
Sender MAC: target:mac:address:here Sender IP: target.ip.goes.here Target MAC: interface:mac:address:here Target IP: interface.ip.goes.here
如今咱们有了DNS服务器或者默认网关的IP地址,咱们能够继续DNS请求了:
当浏览器获得了目标服务器的IP地址,以及URL中给出来端口号(http协议默认端口号是80, https默认端口号是443),它会调用系统库函数 socket ,请求一个 TCP流套接字,对应的参数是 AF_INET 和SOCK_STREAM 。
到了如今,TCP封包已经准备好了,但是使用下面的方式进行传输:
对于大部分家庭网络和小型企业网络来讲,封包会从本地计算机出发,通过本地网络,再经过调制解调器把数字信号转换成模拟信号,使其适于在电话线路,有线电视光缆和无线电话线路上传输。在传输线路的另外一端,是另一个调制解调器,它把模拟信号转换回数字信号,交由下一个 网络节点 处理。节点的目标地址和源地址将在后面讨论。
大型企业和比较新的住宅一般使用光纤或直接以太网链接,这种状况下信号一直是数字的,会被直接传到下一个 网络节点 进行处理。
最终封包会到达管理本地子网的路由器。在那里出发,它会继续通过自治区域的边界路由器,其余自治区域,最终到达目标服务器。一路上通过的这些路由器会从IP数据报头部里提取出目标地址,并将封包正确地路由到下一个目的地。IP数据报头部TTL域的值每通过一个路由器就减1,若是封包的TTL变为0,或者路由器因为网络拥堵等缘由封包队列满了,那么这个包会被路由器丢弃。
上面的发送和接受过程在TCP链接期间会发生不少次:
客户端选择一个初始序列号(ISN),将设置了SYN位的封包发送给服务器端,代表本身要创建链接并设置了初始序列号
若是浏览器是Google出品的,它不会使用HTTP协议来获取页面信息,而是会与服务器端发送请求,商讨使用SPDY协议。
若是浏览器使用HTTP协议,它会向服务器发送这样的一个请求:
GET / HTTP/1.1 Host: google.com [其余头部]
“其余头部”包含了一系列的由冒号分割开的键值对,它们的格式符合HTTP协议标准,它们之间由一个换行符分割开来。这里咱们假设浏览器没有违反HTTP协议标准的bug,同时浏览器使用 HTTP/1.1 协议,否则的话头部可能不包含 Host 字段,同时 GET 请求中的版本号会变成 HTTP/1.0 或者 HTTP/0.9 。
在发送完这些请求和头部以后,浏览器发送一个换行符,表示要发送的内容已经结束了。
服务器端返回一个响应码,指示此次请求的状态,响应的形式是这样的:
200 OK [response headers]
而后是一个换行,接下来有效载荷(payload),也就是 www.google.com 的HTML内容。服务器下面可能会关闭链接,若是客户端请求保持链接的话,服务器端会保持链接打开,以供之后的请求重用。
若是浏览器发送的HTTP头部包含了足够多的信息(例如包含了 Etag 头部,以致于服务器能够判断出,浏览器缓存的文件版本自从上次获取以后没有再更改过,服务器可能会返回这样的响应:
304 Not Modified [response headers]
这个响应没有有效载荷,浏览器会从本身的缓存中取出想要的内容。
在解析完HTML以后,浏览器和客户端会重复上面的过程,直到HTML页面引入的全部资源(图片,CSS,favicon.ico等等)所有都获取完毕,区别只是头部的 GET / HTTP/1.1 会变成 GET /$(相对www.google.com的URL) HTTP/1.1 。
若是HTML引入了 www.google.com 域名以外的资源,浏览器会回到上面解析域名那一步,按照下面的步骤往下一步一步执行,请求中的 Host 头部会变成另外的域名。
HTTPD(HTTP Daemon)在服务器端处理请求/相应。 最多见的HTTPD有Linux上经常使用的Apache与windows的IIS。 * HTTPD接收请求 * 服务器把请求拆分为如下几个参数:
- HTTP请求方法(GET, POST, HEAD, PUT 和 DELETE )。在访问Google这种状况下,使用的是GET方法
- 域名:google.com
- 请求路径/页面:/ (咱们没有请求google.com下的指定的页面,所以 / 是默认的路径)
浏览器渲染引擎从网络层取得请求的文档,通常状况下文档会分红8kB大小的分块传输。
HTML解析器的主要工做是对HTML文档进行解析,生成解析树。
解析树是以DOM元素以及属性为节点的树。DOM是文档对象模型(Document Object Model)的缩写,它是HTML文档的对象表示,同时也是HTML元素面向外部(如Javascript)的接口。树的根部是"Document"对象。整个DOM和HTML文档几乎是一对一的关系。
解析算法
HTML不能使用常见的自顶向下或自底向上方法来进行分析。主要缘由有如下几点:
因为不能使用经常使用的解析技术,浏览器创造了专门用于解析HTML的解析器。解析算法在 HTML5 标准规范中有详细介绍,算法主要包含了两个阶段:标记化(tokenization)和树的构建。
解析结束以后
当浏览器把文档标记为“可交互的”,浏览器开始解析处于“推迟”模式的脚本,也就是那些须要在文档解析完毕以后再执行的脚本。以后文档的状态会变为“完成”,浏览器会进行“加载”事件。
你能够在 HTML5 标准规范中看到标记化和构建树的详细算法。
浏览器容错
在解析HTML网页是永远不会出现“语法错误”,浏览器会修复因此错误,而后继续解析。
加载/预加载网页的外部资源(CSS,图像,Javascript 文件等)
执行同步 Javascript 代码。
渲染结束后,浏览器根据某些时间机制运行JavaScript代码(好比Google Doodle动画)或与用户交互(在搜索栏输入关键字得到搜索建议)。相似Flash和Java的插件也会运行,尽管Google主页里没有。这些脚本能够触发网络请求,也可能改变网页的内容和布局,产生又一轮渲染与绘制。