在浏览器页面输入url敲击enter键,发生了什么-《网络是如何连接的》总结

        近期看完了《网络是如何连接的》这本书,觉得有收获。但是如果不形成文字的话,印象就不深刻,故有此文,巩固学识的同时,锻炼文笔。此书是日本人所写,旨在展现互联网的全貌,内容详尽,涉及面广,且深入浅出,老少咸宜。。。好吧,这本书确实写得不错,内容涉及软件、硬件,不光解释了how,还解释了why,不难看出作者是个全栈工程师。作者通过“浏览器中输入网址,到屏幕上显示内容”这条线索,逐层解析,透彻讲解各层的功能和作用。下面是我的阅读总结,不当之处,还请斧正。

1,输入网址和敲击enter按键的时候,会有中断事件产生,这里我参考了其他帖子的内容(http://blog.csdn.net/xumingjie1658/article/details/6965176):

        当用户按键时,键盘接口会得到一个代表该按键的键盘扫描码,同时产生一个中断请求。中断服务程序通知键盘中断服务程序区处理,键盘中断服务程序先从键盘接口取得按键的扫描码,然后根据其扫描码判断用户所按的键并作相应的处理,最后通知中断控制器本次中断结束并实现中断返回。

        键盘中断的大致过程就是这样,当键盘中断完成之后,也即用户敲击“enter”按键之后,就触发了浏览器的操作了,就是《网络是如何连接的》一书所阐述的内容了。

2,浏览器解析url,生成相应的请求--应用层。比如,浏览器发现时 http请求,解析出 www.baidu.com 这个域名。如果本地的dns解析缓存中有这个域名的解析,那么直接将 http请求委托 tcp 传输层进行传输。如果没有,那么需要向dns服务器发起dns查询,获得域名对应的ip地址。dns查询,有递归查询和迭代查询(也叫循环查询)两种。递归查询,就是 A 向 B查询,B向C查询,C向D查询,D查询到了,将结果返回给C,C返回给B,B再返回给A。这种查询会造成根域名服务器压力过大。迭代查询,就是 A 向B查询,B告诉A去向C查询,然后A就向C查询,C告诉A去向D查询,然后A就向D查询,D查询到了,结果返回给A。dns查询包都是使用 udp 发送的。通过dns查询到域名对应的ip地址之后,将http请求委托tcp/ip协议栈进行传输。

3,应用层委托协议栈进行传输,协议栈的tcp传输层通过套接字进行传输--传输层。浏览器通过 socket库向协议栈发出委托,涉及到socket(),connect(),read(),write(),close()等操作。首先是 调用socket()接口,产生一个 文件描述符,然后调用 connect() 接口,进行tcp的三次握手,成功之后,就可以进行 收发数据了。我们将 http请求调用write()接口发送出去,其实只是发送到底层的发送缓冲区中,tcp自己会根据时机进行发送,或者根据设置的tcp_nodelay立马发送。这些操作都是系统调用,会引发用户态与内核态的切换,所以相对来说比较耗时。如果一个数据包太大,协议栈自己会根据MTU(实际上是根据MSS(MTU-40 = 1500-40=1460B))大小自动分割。TCP通过超时重传、确认机制、滑动窗口等保证可靠性。当服务器收到http请求之后,会将数据返回给浏览器所在的客户端,客户端调用 read() 接口接收数据,然后断开连接,断开连接这里有四次通信。这些 connect(),write(),read(),close()都是委托IP模块封装数据进行发送。套接字是由src_ip,src_port,dst_ip,dst_port这四个字段识别的,一个套接字,对应着源端的应用程序 与 目的端的应用程序之间的通信链路。

4,tcp模块委托IP模块进行传输--网络层,数据链路层。tcp模块会生成tcp头部和数据部分,然后交给IP模块。IP模块会在tcp头部再加上一段 IP头部和MAC头部。IP头部是用在网络间的路由,也即用在互联网上的寻址。如果有多块网卡,根据目的ip地址和本地路由表,从中选择一块合适的网卡进行发送。MAC头部,用于局域网内的寻址。MAC地址可以从arp缓存中获取,如果没有,则需要广播arp请求,获取对应的mac地址(一般是填写网关的mac地址)。IP模块添加完这两个头部之后,就交给网卡处理。

5,IP模块委托网卡进行收发--物理层。网卡收到IP模块交付的数据之后,会在头部加上 报头和起始帧分界符,在尾部加上 帧校验序列(FCS)。报头和帧起始分界符,是用来判断读取时机的。我们想象一下,线路上来了一堆 000011111这样的连续的电信号,我们是没法同步时钟信号的,因为他们是连续的高电平或者低电平,没有电流电压的变化,无法判断应该从哪里去切分一个比特。所以报头就给了 010101 这样的56个比特,让我们有足够的时间去区分比特间的时钟信号。FCS其实就是CRC校验码,用来校验数据的正确性的,因为传输的过程中,会有噪声影响,有误码率。加上报头、帧起始分界符、FCS之后,网卡的MAC模块生产通用信号,由PHY(MAU)模块转换成可在网线中传输的格式(比如,数据信号和时钟信号的叠加信号),通过网线发送出去。通过网线发送数据,又分为全双工(收发线路独立)和半双工(收发线路共用,需进行碰撞检测),现在应该很少有集线器这种半双工的设备了。

6,数据在局域网内被发给网关路由器。数据从浏览器所在的客户端的网卡发出,到达交换机,交换机根据MAC地址表查找目的mac地址的转发端口,如果没查到则广播所有端口。交换机上面的交换电路是网格状的,只要连通一条线路的交换开关,就可以进行转发。所以,只要多条线路之间的路线不交叉,就可以并行传输。交换机将数据转发给网关路由器。

7,网关路由器通过ADSL等方式接入互联网,发给网络运营商(ISP)。

    网关路由器向外网转发的时候,会有NAT转换的过程,将内网地址屏蔽。如果网关路由器是转发的目的端口也是局域网,那么网关路由器根据数据的IP头部,路由到相邻路由器,同时去掉MAC头部,换上新的MAC头部,发给相邻路由器,相邻的路由器再根据同样的方法,路由到下一跳,最终到达服务器所在的网络。

    一般来说,服务器如果是放在公司内部,才会使用上述的方法路由过去。我们在家庭上网时,网关路由器后面接的是接入网,用于连接互联网。一般家用的接入网方式包括 ADSL、 FTTH、 CATV、 电话线、 ISDN 等, 公司则还可能使用专线。我们以ADSL为例,网关路由器(这里可以称为 互联网接入路由器)会在网络包前面加上 MAC 头部、PPPoE 头部、PPP 头 部 总 共 3 种 头 部, 然 后 发 送 给 ADSL Modem(PPPoE 方式下)。ADSL Modem 将包拆分成信元,并转换成电信号发送给分离器。从分离器出来, 就是插电话线的接口, 信号从这里出来之后, 会通过室内电话线,连到室外电话线,经过架设在电线杆上的电缆,到达电话局,到这里,就将数据发到了ADSL接入服务商。信号到达电话局后,经过DSLAM拆分成ATM信元,到达BAS(带宽接入服务器),BAS 负责将 ATM 信元还原成网络包并转发到互联网内部。BAS和网络运营商之间建立了一条专用隧道(比如L2TP协议),到达互联网内部。到这里,我们发送的数据,才真正的进入了互联网。

    在刚才的描述中,从 用户端的 互联网接入路由器 到 运营商的 BAS 之间的线路,如果选用 ADSL的方式,就是 ADSL 接入。如果选用光纤,就是光纤接入,也即FTTH。这里我们需要注意到,网关路由器,也即互联网接入路由器,已经具有公网地址,这是在用户进行了拨号上网设置等操作就完成了的。

8,网络运营商将数据在互联网上面转发,最终到达服务器所在的运营商。然后到达 服务端的 BAS,然后到达服务端所在的接入路由器,服务端可以将自己暴露在公网上,但是这样不安全,ip地址也不够,现在一般不这样做。更多的作法,是在接入路由器后面部署防火墙,保护局域网的安全。这里也可能会有NAT解析。

如果客户端请求量过大,服务器处理不过来,需要分担负载。办法有:A,在DNS上面做负载均衡;B,用 nginx 反向代理做负载均衡;C,在客户端设置缓存;D,用缓存服务器分担负载(当真实的服务器有内容变化时,去更新缓存服务器),缓存服务器可以设置客户端所在的网络(缺点,不利于真实服务器的管理和控制),也可以设置在真实服务器所在的网络(缺点,无法减少互联网中的流量),也可以设置在主流运营商内部(这就是CDN,Content Delivery Network)。

9,数据到达服务器之后,同样经过网卡-IP模块-tcp模块-应用层获取数据,并进行回复。服务端的处理,与客户端略有不同,服务端会事先调用 listen()监听端口,然后调用 accetp() 允许一个客户端的连接,然后使用多路复用技术(select,poll,epoll等)管理多个客户端的连接。我们假设此时服务端调用read()接口收到客户端发来的http请求,然后解析请求消息,比如,将url中的地址,转化为主机头对应的虚拟路径,进行相关的处理,然后调用 write()接口进行回复。回复的信息会经过tcp/ip协议栈,到达网卡,发送出去。

10,回复数据可能会原路返回到 客户端所在地运营商,也可能通过其他路由器到达 客户端所在的运营商。然后运营商通过专用隧道,到达客户端所在的电话局的BAS,然后BAS通过ADSL或者 FTTH 等线路,到达客户端所在的网关路由器,或者说 互联网接入路由器。经过路由器的NAT转换,发给客户端。

11,客户端的网卡从网线中接收信号,并转化为数字信号,放到缓冲区,校验FCS,判断是否是发给自己的(目的MAC地址是自己的MAC地址),如果不是自己的,则丢弃,否则,放入网卡缓冲区中,并产生中断,通知计算机收到数据。中断处理程序让网卡驱动去处理这个数据,网卡驱动将数据取出来之后交给tcp/ip协议栈处理:IP模块检查IP头部,接收合法包并进行分片重组(若有必要),然后tcp模块根据IP头部中的ip地址和tcp头部中的端口号,找到对应套接字,回复确认包,将数据放入接收缓冲区,等待应用程序读取。

14,浏览器调用 read()操作,陷入内核,读取服务端返回的数据,然后返回用户态,进行相关的处理,经过页面渲染,展示页面文字、图片、音视频等信息。

总结一下,简要流程是:

1,敲击enter按键,产生中断,键盘中断服务程序解析这个按键,触发了浏览器的解析操作(浏览器如何捕获enter事件,我还不太清楚)。

2,浏览器解析url中的域名,希望能知道 域名对应的ip地址。先看本地的dns缓存,如果没有,则发送dns查询包进行查询。

3,浏览器封装http请求, 调用 connect() 与服务器进行tcp的三次握手,然后调用 write() 发送http请求。该请求经过 传输层,网络层,数据链路层,物理层,从数字信号变成电信号,经由网线发送出去。

4,数据经过交换机转发,到达网关路由器。我们假设网关路由器使用 ADSL线路并使用 PPPOE 协议接入互联网,那么网关路由器会丢掉收到的数据的 MAC头部,添加PPP,PPPOE头部,重新组装一个MAC头部,发给 电话局的BAS,再经由专用隧道,接入到运营商,进入互联网。

5,互联网上各运营商之间路由转发,最终到达服务器所在的运营商,经过专用隧道,到达 BAS,然后到达网关路由器。

6,网关路由器经过NAT解析,可能还有ngnix反向代理,经过防火墙,将数据发给服务器。

7,服务器网卡接收数据,逐层传递到IP层,传输层,应用层。

8,服务器应用层进行处理并回复,逐层委托 传输层、IP层、数据链路层、物理层,发给网关路由器。

9,网关路由器发给BAS,经隧道发给运营商,进入互联网。

10,互联网内路由转发,达到客户端所在的运营商,经隧道到达BAS,然后到达客户端的网关路由器。

11,网关路由器经过NAT解析,发给客户端。

12,客户端的网卡接收数据,逐层传递到IP层,传输层,应用层(也即浏览器)。浏览器获取数据之后,进行渲染,展示页面。后续,http协议还会断开tcp连接。

至此,过程结束。

猜你喜欢

转载自my.oschina.net/u/2447371/blog/914321