数据是怎么一步一步到服务器的

当我们在浏览器输入www.baidu.com,回车以后,是怎么显示百度页面的?

首先通过DNS查询IP

首先我们要知道查询服务器都是通过IP来查找的,那么第一步就要查询www.baidu.com这个域名对应的IP是什么,这时候就需要去查询DNS服务器,那这个DNS服务器的地址我们怎么知道呢?一般DNS服务器地址可以自动获取或者手动设置

DNS

DNS服务器地址知道了,那浏览器是怎么去查DNS服务器的呢?这就需要调用Socket库,里面封装了通过域名查询IP的方法,这个方法里具体做了什么,我们后面再说,注意一点:查询DNS服务器使用的是UDP,而且DNS服务器不是一台,是很多台组成的DNS服务网相互接力,完成域名查找

组装HTTP包

www.baidu.com对应的IP我们拿到了,接下来就要正式开始我们的旅程了。

首先,浏览器查询这个地址属于http请求,那我们就要组装对应的http包,http包一般分为请求头和请求体

HTTP包

在浏览器按f12可以查看每个请求的http报文,如下

因为我们只是在地址栏输入了www.baidu.com,没有请求体,平时我们提交表单等其他操作时,在这里可以看到对应的请求体。请求头都是键值对,每个键都有对应的含义。而请求体的格式就各种各样了,一般根据请求头的Content-type字段来决定。

组装TCP包

在组装TCP数据之前,我先解释一下TCP/IP协议,准确来说,它应该是叫TCP/IP协议簇,指的是能够在不同网络间实现信息传输的协议簇,里面包括FTP\SMTP\TCP\UDP\IP等等,因为里面TCP和IP具有代表性,所以把它叫做TCP/IP协议。

浏览器在组装好HTTP报文以后,会调用Socket库里相应的方法来把报文委托给TCP/IP协议栈来处理。

  1. 先通过socket(参数)来获得socket描述符。
  2. 通过connect(参数)来和服务端建立链接。
  3. 通过write(参数)来发送数据。
  4. 通过read(参数)来获取数据。
  5. 最后close(描述符)关闭socket。

在解释上面操作之前,我们要清楚TCP是面向连接的,客户端和服务端通信首先需要建立连接,然后才能通信。其实这个连接就是两边的套接字连接形成一个虚拟的管道,下文用socket表示套接字。

发送数据,我们首先来看一下前三步。

第一步通过socket(参数)来获得socket描述符。先来介绍一下什么是描述符,描述符可以理解为一种事物的索引,例如文件描述符,操作系统如果要操作文件,需要通过文件描述符来访问文件,socket描述符也是一样,操作socket需要通过socket描述符。后面的操作都是通过socket来完成,所以我们首先要获得socket描述符。可以通过netstat -ano查看本机创建的socket。

本机socket

第二步通过connect(参数)来和服务端建立链接。这里就是我们面试经常被问到的三次握手了,我们简单的回顾一下什么是三次握手,首先客户端会生成一个SYN比特位为1的TCP包发送给服务端,服务端接收到以后也会发一个SYN比特位为1的TCP包给客户端,但是这个TCP包里还包含了一个确认收到客户端包的ACK号,最后客户端收到服务端发过来的包以后会返回一个确认号ACK给服务端,这样客户端和服务端就建立连接了,客户端的socket和服务端的socket就像是通过一个管道连接起来了。

第三步通过write(参数)来发送数据。连接建立好以后,就要开始发送数据了,在调用read方法里会将数据委托给TCP模块来处理,TCP模块接受到数据后,会在请求包前面加上TCP头部,里面包含发送方和接收方的端口,用来查找具体连接哪个socket。

TCP模块收到数据并不会马上发送数据,因为数据怎样发送是应用程序来控制的,有的程序是一次性发送所有数据,有的确实逐字节或者逐行发送,如果TCP模块接到数据就马上发送,那可能会出现发送一堆小包的情况,导致网络效率下降。那TCP在什么时候发送数据呢?当TCP收到数据以后,会放入缓冲区,当数据达到一个网络包的最大长度时,或者等待超过一定时间时,就会执行发送操作

HTTP请求消息一般不会很长,一个网络包就能装下,但如果请求数据很大,超过了一个网络包的最大容量,这就需要对包进行拆分,拆分后的每个包前面都加上TCP头部,

HTTP拆包

第四步通过read(参数)来获取数据。和发送数据一样,接收的数据会先暂存在缓冲区,当调用read方法获取数据时,会先从缓冲区中取数据,如果缓冲区中还没有数据,则挂起当前线程,等接收到返回数据以后,先判断数据包是否完整,如果不完整则继续接收剩下的数据包,接收完以后按顺序连接起来还原出原始的数据,最后将数据交给应用程序。

第五步最后close(描述符)关闭socket。这就是我们熟悉的四次挥手,再来回顾一下四次挥手,当服务器响应消息后,这时请求过程就结束了,服务器一方会发起断开过程,这是HTTP1.0,如果是HTTP1.1则是客户端发起端口过程,两者只是开头不一样,过程都是一样的。

首先客户端程序调用close方法,TCP模块会生成包含端口信息的TCP头部,里面FIN比特位为1,服务端收到FIN为1的TCP头部时,会将自己的socket标记改为断开状态,然后返回给客户端一个ACK号。服务端同样也会调用close方法来关闭socket,发送一个FIN为1的TCP头部包给客户端,然后客户端会返回一个ACK号给服务端。至此双方就断开连接了。

上面提到的三次握手和四次挥手如图

三次握手和四次挥手

组装IP包

TCP模块在执行连接、收发和断开等操作时,都需要委托IP模块将包发送给通信对象。IP模块会在TCP包前面加上IP头部和MAC头部,IP头部里记录了源IP和目的IP,MAC头部里记录了下一个路由设备的MAC地址。这里要明确一点,IP头部是IP协议,而MAC头部是以太网协议(以太网可以简单的理解成局域网)。

IP包

IP头部中的IP地址是固定不变的,一直是目的服务器的IP地址,但是MAC头部中的MAC地址是变化的,当请求包到达第一个路由器时,路由器会根据路由表查出下一个路由器的MAC地址,然后将此MAC地址替换原MAC地址,再继续转发。一个请求包从客户端到服务端一般会经过多个路由器。

那是怎么从IP得到MAC地址的呢?这就需要通过ARP(Address Resolution Protocol)来查询,ARP其实就是一种广播,把包发给以太网中所有设备,当其中设备发现包中IP是自己的,就会返回信息告诉自己的MAC地址。当然这种每次都广播太耗性能了,所以会有ARP缓存,如果以前查过此IP的MAC地址,那ARP缓存中会有记录,直接查缓存就行了,如果缓存中没有再进行ARP广播。可以通过arp -a命令来查看本地ARP缓存记录。

ARP缓存

网卡

IP生成的包只是存在内存中的一串数字信息,无法直接发送给对方,因此我们需要将数字信息转换为电或光信号,才能在网线中传输,负责这一步的就是网卡。但是光有网卡还不行,还需要网卡驱动,各种硬件设备都有自己的驱动程序,当然网卡也不例外,驱动程序就是硬件厂商开发的专用程序。

网卡驱动从IP模块获取包之后,会将其复制到网卡内的缓冲区中,然后向MAC模块(这里MAC模块和IP中的MAC头部区分开)发送发送命令,MAC模块从缓冲区中取出包,并在开头加上报头和起始帧分界符,在末尾加上用于检测错误的FCS帧校验序列。

网卡包

用电信号来表示数字信息时,就需要让0和1两种比特分别对应特定的电压和电流,通过电信号来读取数字信息过程则相反。在测量电压和电流时,必须判断每个比特的界限在哪里,如果数字信号有连续的0或者1,则很难判断界限在哪。要解决这个问题,可以添加一个用于参照的时钟信号,它是一串均匀变化的电信号。将数字信号和时钟叠加在一起传输就可以了,读取数字信号减去时钟信号就可以了。

集线器

加上报头、起始帧分界符和FCS之后,就可以将包通过网线发送出去了。发送信号分为两种,使用集线器的半双工模式(同一时刻只能发送或者接收)和使用交换机的全双工模式(同一时刻发送和接收可以并行)。现在基本都是交换机的全双工模式了

在半双工模式下,为了避免信号碰撞,首先要判断网线中是否有其他信号在传输,如果有,则需要等待其他信号传输完毕再进行传输,网卡中MAC模块将包从报头到FCS按每个比特转换成电信号,然后由PHY/MAU(两者传输速率不同)模块将信号转换为可在网线中传输的格式后通过网线发送出去。

集线器将信号发送给所有连接在它上面的线路,传输中间可能因为噪声等干扰,信号到达接收设备(交换机或路由器)以后可能会失真,传输过程可以通过各种手段来减少干扰,如双绞线等。接收设备接收到包以后,会通过FCS来校错,如果出错就会丢弃包(TCP模块有重传机制,如果没收到ACK信号,会进行重传)。

交换机

交换机接收到包,首先信号到达PHY/MAU模块(和集线器一样),然后将信号转为通用格式到MAC模块,MAC模块再将信号转为数字信息,然后通过末尾的FCS校错。交换机有多个端口,每个端口和计算机中的网卡类似,只是它的端口没有MAC地址,不会校验包是否是传给自己的,而是接收所有的包放入缓冲区中。

交换机中维护着一张MAC地址和端口的映射表。当收到包时,交换机会将发送方MAC地址和连接的端口记录在映射表中,这样下次接收到包的目的MAC地址是这台机器的话,就可以直接通过映射表找到。有时候也需要删除映射表中的记录,比如笔记本电脑从a区域拿到了b区域,此时映射表中此笔记本的MAC地址还对应a区域的端口,因此保存映射表记录的时候需要设置失效时间,过了失效时间会自动删除。

交换机传输信号使用的是全双工模式,也就是发送和接收可以同时进行,而且此模式下,不会发送信号碰撞。

路由器

网络包经过交换机到达路由器,路由器再转发到下一个路由器,这个转发和交换机类似,也是通过查表来判断转发的目标,不过两者还是有区别的,路由器是基于IP设计的,而交换机是基于以太网设计的

路由器分为转发模块和端口模块,端口模块中包含各种端口用来接收和转发网络包,转发模块中有一张路由表,根据包中的IP地址和路由表中信息来指定端口转发。

首先路由器通过端口将包接收进来,这个过程取决于端口对应的通信技术,如果是以太网端口,就按照以太网规范工作,如果是无线局域网端口,就按照无线局域网的规范工作。接收到包以后先判断包中MAC地址是不是自己的,如果不是自己的则直接丢弃,如果是自己的则交给转发模块,接下来转发模块会根据接收到的包的IP头部中接收方IP地址在路由表中查询对应的端口,最后通过对应的端口转发出去。

路由器工作过程看起来和交换机很类似,但是两者还是有区别的,首先路由器各个端口都是有MAC地址和IP地址的,这和计算机中网卡一样,也就是路由器会成为发送方或者接收方。而交换机不是,它的端口没有MAC地址,不会成为发送方或者接收方,交换机用作包的转发。而且交换机是根据MAC头部中的MAC地址来转发的,而路由器是根据IP头部中的IP地址来转发的。

在交换机中,需要匹配MAC地址和地址表中完全一致的记录,而路由器则匹配IP地址中的网络号部分,下面是一张简单的路由器路由表

路由表

在这里解释一下什么是子网掩码,IP地址一般分为网络号和主机号,子网掩码就是用来区分网络号和主机号,比如一个IP地址:10.11.6.54,子网掩码是:255.255.255.0。子网前三位都是255,对应二进制都是1,那前三位表示网络号,后面一位是主机号。那10.11.6.54对应的网络号是10.11.6,主机号是54。

前面说的路由器只匹配IP地址中的网络号部分,那IP地址是10.11.6开头的都会匹配到第一条记录。如果找不到匹配记录,则会匹配最后一个0.0.0.0默认路由

路由器包的转发和计算机类似,先查询路由表中对应的转发记录,如果对应的网关是IP,则下一个转发地址就是这个网关IP,如果网关地址是空的,则下一个转发地址是包中目的IP,然后通过ARP获取IP对应的MAC地址,接着改写原包中MAC头部中目的IP的MAC地址。

一般路由器后面连着互联网,转发端口连着ADSL(电话线)或者FTTH(光纤)等等。

互联网接入路由器

信号从以太网路由器到达互联网接入路由器进入互联网。

互联网接入路由器

互联网接入路由器基本工作流程和以太网路由器一样,但是其中会有一点区别。互联网接入路由器接收到以太网包以后,会取出IP包,然后会在其头部加上MAC头部(BAS的MAC地址)、PPPoE头部和PPP头部,然后发给ADSL Modem(猫)

互联网接入路由器包

ADSL Modem(猫)

ADSL Modem叫做调制解调器,因电话线中传递的只能是模拟电信号,而猫可以将数字信号转换为模拟电信号

ADSL Modem接收到互联网接入路由器传过来的包以后,会将包拆分成很多个信元(非常小的数据块),然后将这些信元转换成电信号发送出去。这里的电信号和以太网中的电信号有点不一样,以太网中使用的是方波,而这里使用的是圆滑波。

ADSL Modem将信元转换成电信号以后,信号会进入分离器,然后ADSL信号会和电话信号一起从电话线传输出去,这个分离器的作用就是将ADSL信号和电话信号分离。

信号从分离器出来以后会通过室内电话线,然后到达电线杆上的电话电缆。接着信号会到达电话局中的DSLAM,这是电话局用的多路ADSL Modem,用来将很多个ADSL Modem的功能集中在一起。到达DSLAM后,信号会还原成数字信号。

信号从DSLAM出来后会到达一个叫BAS的包转发设备,BAS会将信元还原成原始的包,它会将包前面的MAC头部和PPPoE头部丢弃(这两个头部就是用来找BAS的,此时它们的任务完成了,可以被丢弃了),然后在包的前面加上隧道专用头部(L2TP),并发送到隧道的出口。

BAS包

所谓隧道,就类似于socket之间建立的TCP连接,数据从一端进入,会原封不动的从另一个出口出来,隧道也是如此。隧道有几种实现方式,TCP连接就是其中一种实现方式,这种方式需要在网络上的两台隧道路由器之间建立TCP连接,然后将两端的socket当作是路由器的端口,并从这个端口收发数据,这时的TCP就像是一根网线,包从这里穿过到达另一端。

运营商互联网

数据从隧道路由器到达运营商路由器后就进入了运营商互联网,在运营商互联网中也会有多个运营商,它们通过IX(互联网交换中心)相连,这个IX和我们分布式系统中的注册中心有点类似,如果要连接其他运营商,必须通过IX来连接。由此可见,IX在运营商互联网中还是相当重要的,所以对于它,要做到高可用,首先IX的部署场地需要具备自主发电的能力,防止停电等原因,还必须具有抗震能力,一般具有这样能力的大楼里面都可能有IX设备。

在各个运营商内部是通过NOC(网络运行中心)来连接的,各个POP(接入点)通过NOC来连接。简单点来说,POP用来连接外网系统(客户端和服务端),运营商内部各个POP通过NOC来连接,而各个运营商的NOC又通过IX来彼此连接

防火墙

数据从运营商互联网到服务器时,中间会经过防火墙,这个防火墙和个人电脑中的防火墙不一样,它是一个硬件设备,但是作用和电脑中防火墙差不多。

使用防火墙主要是为了安全,它只允许发往特定服务器中的特定程序的包通过,然后屏蔽其他的包。但是互联网中各种各样的包,怎么分辨哪些可以通过,哪些需要屏蔽呢?这就需要用到一些过滤规则。假如有这样的需求,web服务器可以被外网访问,但是web服务器本身不能访问外网,这种需求要怎么定义规则呢?

定义规则一般通过网络包中的IP头部和TCP头部来控制。首先控制web服务器能被外网访问,规则可以定义成:发送方IP和端口可以是任意的,但是接收方IP和端口必须是web服务器的IP和端口。然后控制web服务器本身不能访问外网,有人会把规则定义成:发送方的IP和端口是web服务器的则禁止。这样是不行的,因为如果这样定义,那外网访问web服务器将收不到响应,这样肯定是不行的。那应该怎样定义呢?前面说过,请求访问首先要建立TCP连接,我们可以限制web服务器连接外网,但是允许外网连接web服务器。再来回顾一下连接是怎么连接的,第一步请求方发送SYN=1,ACK=0,然后应答方返回SYN=1,ACK=1,最后请求方发送SYN=1,ACK=1,我们只要限制web服务器发送的第一个包就行了,当发现TCP头部中SYN=1,ACK=0的话,而且发送方是web服务器的IP和端口,就限制这个包的发送,如果TCP头部中SYN和ACK是其他的话就放行。这样就限制了web服务器请求外网,而外网可以请求web服务器。

防火墙可以根据包的起点和终点来控制,但是无法控制请求数据中的违法字符等,这种就需要应用程序自己来限制了。

负载均衡

当外网访问web服务器的时候,其实请求一般都会经过一层负载服务器,这是为了将一台web服务器的请求压力分摊到多台服务器上。网络包中的接收方IP和端口也是负载服务器的IP和端口,然后负载服务器根据端口或者其他一些规则将请求转发到真实的应用服务器。

到达服务器

当网络包经过负载服务器达到web服务器之后,服务器就会接收到这个包进行处理。首先说一些服务器和客户端有什么异同,在网卡,协议栈,Socket库等方面,两者是一样的,但是在Socket库的用法上是不同的,客户端是发起连接,而服务器是等待连接。客户端发送数据是使用的随机端口,而服务端接收请求,则需要绑定一个固定端口

服务器接收数据过程就是将客户端发送数据反过来,首先网卡将电信号或者光信号转换为数字信号,然后根据包末尾的FCS校错,正确的话就将数字信息保存在网卡缓冲区中,然后网卡驱动开始工作,将缓冲区中数据读取出来,交给TCP/IP协议栈来处理,接着IP模块查看IP头部,看看这个包是不是传给自己的,确认之后再将包转交给TCP模块处理。接着TCP模块查看TCP头部,如果其中SYN=1,则这个包是用来建立连接的,然后返回连接相关信息给客户端;如果接收到的是数据包,则根据发送方IP、接收方IP、接收方端口号和发送方端口号来找到对应的socket,然后将数据交给socket来处理,这样数据就会进入应用程序,最后根据请求内容向客户端浏览器返回相应的数据。

至此,数据就从我们的浏览器到达了服务器,大致过程如下图,如需查看原图,可在公众号回复“网络传输”

网络传输


扫一扫,关注我

猜你喜欢

转载自blog.csdn.net/weixin_43072970/article/details/106848832
今日推荐