浅谈操作系统原理(九) 传输层(TCP/UDP)和应用层(HTTP)-张柏沛IT博客

正文内容

浅谈操作系统原理(九) 传输层(TCP/UDP)和应用层(HTTP)

栏目:Linux 发布时间:2020-04-04 19:15 浏览量:264

传输层 


UDP协议详解

UDP —— 用户数据报协议
这里的数据报是指应用层直接传过来的数据报,UDP协议不会对其进行任何处理,不拆分也不合并

UDP协议下包含 UDP首部+UDP数据报的数据
其中UDP数据报的数据 = 应用层的数据,一点不多一点不少

所以UDP是一个简单的协议

UDP首部包含 16位的源端口号和16位目的端口号
即UDP首部包括两个要通信的进程的端口。即本机器的进程的端口和目的机器的进程的端口。

UDP是一个无连接的协议

即使用UDP协议通信 是无需在两个机器间建立连接的。意味着无需建立连接就可以发送请求


UDP不能保证可靠的交付数据,不能知道数据在网络中是否丢失,即使丢失了也不知道。

UDP是面向报文传输的

UDP没有拥塞控制

UDP首部开销很小(首部内容很少)


====================================

TCP协议详解

TCP 传输控制协议
这是一个非常复杂的协议

TCP协议包括 TCP首部+TCP数据报的数据



TCP是面向连接的协议
即发送请求前要先建立连接
TCP的一个连接有两端,是一个点对点的通信

TCP提供可靠的传输服务

TCP是种全双工的的通信(即两个计算机可以同时向双方发送数据)

TCP是面向字节流的协议(这意味着TCP的数据会分拆多次放到多次报文中传输)

TCP的头部包含源端口,目的端口,序号,确认号,数据偏移,保留字段,TCP标记(重要),窗口(重要),校验和,紧急指针,填充,TCP选项(可选)


序号
占32位
一个字节有一个序号
TCP头部的序号是指TCP数据报中数据第一个字节(首字节)的序号

确认号
占32位
确认号表示期望收到下一个TCP数据报的数据的首字节序号;也是滑动窗口中的第一个字节序号


例如一个TCP的数据报的数据首字节序号是501,数据长度为100字节。
那么确认号就是601,表示希望下一个收到的TCP数据报的首字节序号为601。

TCP标记
TCP标记是一个6位的二进制
其中每一位都有其含义

第一位:URG   紧急位,URG=1表示是紧急数据
第二位:ACK   确认位,ACK=1确认号才有效
第三位:PSH   推送位,PSH=1表示要尽快将数据交付给应用层
第四位:RST   重置位,RST=1,表示连接发生错误,重新建立连接
第五位:SYN   同步位,SYN=1,表示连接请求报文,用于同步连接
第六位:FIN    终止位,FIN=1 表示释放连接

例如 TCP标志位为 010010 就表示 URG=0,ACK=1,PSH=0,RST=1,SYN=1,FIN=0


窗口
占16位的二进制
窗口指明了滑动窗口的大小

例如 窗口的十进制是1000,表示滑动窗口的大小能容纳1000个字节的数据


紧急指针 
当URG=1时才会用到。

 
==================================

可靠传输的基本原理

将计算机分为发送方和接收方。

停止等待协议
发送方发送消息给接收方,消息方接收消息后会发送一个确认的信号给发送方,发送方才会发送下一个消息。
停止等待协议就是发送方发送完消息后,在接收到对方的确认信号前不会再发送新消息,而是会等待这个确认信号发过来


但是复杂的网络环境就会出现很多意外和差错的
情况1:发送方发送的网络报文丢失
一旦丢失,接收方就接收不到发送方的消息,也就不会发送确认信号。那么此时发送方不会一直等待,而是等待一会之后,重新发送之前的消息。这个叫做超时重传。



情况2:发送方发送的消息被接收方收到,但是接收方返回的确认信号丢失
此时发送方一样接收不到确认消息,照样会超时重传,发送方在一段时间后重新发送上一次的消息



情况3:接收方的确认信号没有丢失,但是在网络中逗留了很久(超过了发送方等待确认报文的最大时间)才到达发送方
此时发送端照样会超时重传


所以停止等待协议是指一方发送消息后要等待接收到对方返回的确认消息才会继续发送下一个消息,而如果出现报文丢失可以通过超时重传重发消息。通过这种方式做到可靠传输的

为了判断确认信号的接收是否超时,发送端每发送一个消息都会设置一个定时器,这个定时器叫做 “超时定时器”


停止等待协议是简单的可靠传输协议。
但是等待协议对信道的利用效率不高,因为每次发送端发送消息都要等待确认消息返回才能发送下一个消息,而且一次只能发一个报文。


连续ARQ协议
ARQ 自动重传请求

ARQ协议下,发送端发送消息报文是可以批量发送的。
也就是说停止等待协议下,发送端发送报文是一个个发送的,一次只能发送一个报文。而ARQ协议,发送端一次可以批量发送多个报文。

这里涉及到一个概念叫做滑动窗口。
滑动窗口限定已发送但未接受到确认消息的发送端报文的个数。


例如:发送端有100个报文要发送,这100个报文要排队排成一个队列。
滑动窗口的报文数为6。
那么一开始发送端会批量发送6个报文(1~6号报文)给接收方。
过了一会,1号2号报文的确认消息返回,3~6号的确认还未返回。此时发送端会在发送两个报文(7,8号报文)

这样一来滑动窗口中的报文始终保持在6个。

只要有报文的确认消息返回给发送端,滑动窗口就会往下滑动,发送后面的新消息报文给接收方


再介绍一个概念叫做累计确认
滑动窗口不会每接收到一个确认消息就往后滑动一个报文。而是检测到滑动窗口中的某个报文被确认了,那么久认为这个报文之前的报文都被确认了,这时滑动窗口会往下滑动多个报文。

例如一开始发送端会批量发送10个报文(1~10号报文)给接收方。此时1~4号报文被确认,滑动窗口没有去检测它们,当5号报文被确认,滑动窗口会往后移动5个报文。此时窗口中的报文是6~15号

因为如果每个报文被返回确认消息时滑动窗口都要检查一次才往下滑动,效率也是不高的

窗口的大小是被记录在TCP报文头部的。
=====================================

TCP协议的可靠传输

TCP的可靠传输是基于连续ARQ协议
TCP的滑动窗口是以字节为单位的(因为TCP是面向字节流的协议),而不是像上面说的以报文为单位的。

发送者要发送的字节排成一列,等待发送,滑动窗口内的字节会被批量发送到接受者。当某个字节被接受者接收并发出确认信号的时候,滑动窗口就会往后面的字节滑动去发送后面的字节给接收者。(TCP报文中的字节流中的每一个字节都有自己的序号)



例如,窗口的大小有7个字节,此时窗口内包含7个字节,字节序号为23~29。
而且23~29号字节都已经发送给接收者。



之后,23,24号字节被确认,此时滑动窗口可以往后移动2个字节。



现在考虑一种情况:


滑动窗口内覆盖着23~29号字节,这7个字节都已经发送出去了。
但是之后只有25,27号字节被确认。
此时由于窗口的第一个字节23号字节没有被确认,所以滑动窗口无法往后滑动。
如果超时计时器的时间到了23号字节还没有被确认,此时23~29号字节会全部重新发送(传送)。
这么一来重传的效率就不高

也就是说,滑动窗口必须按序被确认才可以往后滑动。


为了解决这个重传效率不高的问题,TCP提供了选择重传的功能。

选择重传是通过记录未被接收的字节的序号边界,然后重传这个序号边界内的所有字符。
例如记录的是1000号和1500号的序号,那么会重传1000~1500号的字节

TCP报文头部可以记录10个这样的序号边界。

从上面的过程中,可以看到TCP要发送的所有字节是排成一列去传输的,如果这一列包含了很多字节,一个TCP报文传输不过来,就会分成多个TCP报文传输。
每个TCP报文中的数据的传输是一个一个字节(或者多个字节多个字节的)传输的(以流的形式传输),就是因为滑动窗口是一个一个字节(或者多个字节多个字节)传输的。
假如滑动窗口的大小为1000个字节,而一个TCP报文的大小为100个字节,那么当窗口在往后滑动的过程中,字节一个个传到接收方,接收方接收到100个字节的时候就当做是接收到了一个TCP报文。之后接收方不会真的一个一个字节的返回确认信息,而是等一个TCP报文的字节都接收到了,才对这一个TCP报文的字节统一返回一个确认信息(是个ACK=1的报文)。或者接收方会对发送方发送的多个报文统一返回一个确认报文,而不是发送方发送一个报文接收方就返回一个确认报文。

所以:
可以分多个TCP报文传输量大的字节数据。
报文的传输不是将字节打包成一个块发送的,而是以流的形式一个字节一个字节的(或者多个字节多个字节的)发送的
发送端不会对每一个字节都发送一个确认报文,而是对一个TCP报文(或者多个TCP报文)内的所有字节统一发送确认报文,或者说,就是对发送方发送的这个TCP报文(或者多个TCP报文)发送确认报文。这样这些报文内的所有字节都被确认,滑动窗口就会一次性往后滑动一个报文这么多字节的位置。


PS:
1.窗口的大小不是固定的,而是在传输过程中可以变化的
2.发送者的窗口大小是由接收者返回确认报文时决定的。

下面展现一个完整的接收方和发送方的传输过程。


1.发送方发送了一个序列号为1的报文,这个报文传输了100个字节的数据[seq=1,DATA](表示发送了序列号为1~100的字节数据)
2.发送方的滑动窗口没有用完,所以再发送100个字节的数据(一个新的报文),此时报文的序列号为101。[seq=101,DATA](发送了序列号为101~200的字节)
3.接收方一次性确认这200字节的数据,发送一个确认消息给发送方[ACK=1,ack=201,rwnd=300]。

ACK=1表示这是个确认报文。

ack=201表示返回确认号为201,即告诉发送方的滑动窗口说“序号为1~200的字节都已经确认了,滑动窗口可以往后滑动了,滑动到窗口的第一个字节为序号201的字节那个位置”,此时201号之前的字节都是已被确认的字节

rwnd=300表示接收方限定发送方此时的可用窗口只有300个字节

4.发送方又发送两个报文,第一个报文100个字节,第二个报文200个字节(刚好用完了接收方限定的300个可用字节的窗口)[seq=301,DATA],[seq=401,DATA]
5.接收方对这两个报文统一发送一个确认报文[ACK=1,ack=601,rwnd=0](表示序号601前的字节都已被确认,而且限定发送方的可用窗口为0个字节。即不让发送方发送数据了)

==================================

TCP协议的流量控制

流浪控制即接收方希望发送方发送的慢一些。

流量控制是通过限制滑动窗口的大小来实现的。

假如现在是用户高并发的时段,建立了很多连接,每个连接要传输很多数据。此时客户端就是发送方,服务器就是接收方,服务器无法处理这么多请求。就可以通过滑动窗口来限制流量,减缓客户端的数据发送速度

例如上面的例子:
1.发送方发送了一个序列号为1的报文,这个报文传输了100个字节的数据[seq=1,DATA](表示发送了序列号为1~100的字节数据)
2.发送方的滑动窗口没有用完,所以再发送100个字节的数据,此时报文的序列号为101。[seq=101,DATA](发送了序列号为101~200的字节)
3.接收方一次性确认这200字节的数据,发送一个确认消息给发送方[ACK=1,ack=201,rwnd=300]。

ACK=1表示这是个确认报文。

ack=201表示返回确认号为201,即告诉发送方的滑动窗口说“序号为1~200的字节都已经确认了,滑动窗口可以往后滑动了,滑动到窗口的第一个字节为序号201的字节那个位置”,此时201号之前的字节都是已被确认的字节

rwnd=300表示接收方限定发送方此时的可用窗口只有300个字节

4.发送方有发送两个报文,第一个报文100个字节,第二个报文200个字节(刚好用完了接收方限定的300个可用字节的窗口)[seq=301,DATA],[seq=401,DATA]
5.接收方对这两个报文统一发送一个确认报文[ACK=1,ack=601,rwnd=0](表示序号601前的字节都已被确认,而且限定发送方的可用窗口为0个字节。即不让发送方发送数据了)

这里发送方返回 rwnd=300,rwnd=0 的报文目的就是限制发送端可用窗口的大小从而达到限流

6.等接收方处理完毕数据后,接收方返回一个rwnd=1000的报文,此时发送端又可以继续发送数据

这就是流量控制的过程。


现在有一个特殊情况:
假如,接收方的rwnd=1000的报文丢失了,发送方就无法继续发送数据。这种报文的丢失是没有超时定时器来控制它重传的。因为超时定时器是仅用于数据报文的重传的,而不能用于确认报文的计时重传。

所以设计出来一个坚持定时器。
当发送方接收到窗口为0的报文时,会启动坚持定时器
坚持定时器会控制发送方每个一段时间发送一个窗口探测报文来询问接收方是否扩大了窗口。
这样即使rwnd=1000的报文丢失了,发送方也会通过坚持定时器知道窗口已经调大了,从而继续发送数据


============================================

TCP的拥塞控制

我们将网络看成一条公路,当网络中的报文过多就会造成拥塞。

在一条数据链路中,例如:A的报文要发送给B,途中经过网络1,路由器1,路由器2,网络2,路由器3,最后才到达B
在这个过程中报文经过了很多设备,这条数据链路中的各个部分各个设备都可能成为网络传输的瓶颈,即有一个设备慢就会导致整个数据链路慢


拥塞控制和流量控制有什么不同
流量控制主要考虑点对点的通信量的控制(例如A和路由器1之间的报文传输)
拥塞控制考虑整个网络,是全局性的考虑(涉及A,路由器1~3,B这些机器)


怎样认为发生了拥塞:
报文超时则认为是拥塞 


拥塞控制的算法

慢启动算法:
由小到大逐渐增加发送数据量
每收到一个确认报文,就加1

例如:一开始,发送方只发送1个报文。如果接收方对这一个报文发送确认报文,发送方就知道这条数链路可容纳更多的一次性发送报文。所以第二次,发送方会一次性发送2个报文,如果两个报文也统一收到确认,下一次发送方会发送4个报文。
这个过程是按指数增长的。
一次性发送的报文数量增长到一个叫做“慢启动阈值”的数量就会维持这个数量的报文速度来进行发送。

其实即使到达了慢启动阈值,发送方一次性发送的报文还可能会增加,这就涉及到拥塞避免算法


拥塞避免算法
该算法的实现如下:发送方会维护一个拥塞窗口的变量,这个拥塞窗口是比慢启动阈值略大的。
当一次性发送的报文达到阈值,只要网络不拥塞,就会尝试着将这个拥塞窗口调大,一次性发送的报文会逐步+1

例如,慢启动阈值为16。当发送方一次性发送16个报文时依旧能在超时时间内收到确认(即不拥塞),那么拥塞窗口的大小会+1。下一次发送方可以一次性发送17个报文,如果还不拥塞下一次就发18个依次类推直到拥塞。

所以慢启动算法和拥塞避免算法是结合使用的。慢启动算法的报文量是指数增长,拥塞避免是线性增长。
=========================================

TCP连接的建立(3次握手)

回顾一下TCP标记中的ACK,SYN和FIN
ACK=1,确认号才生效
SYN=1,表示连接请求报文,用于向接收方请求建立连接
FIN=1 表示释放连接


连接建立的过程:


1.发送方主动和接收方建立连接
发送一个SYN=1的报文,表示请求建立一个连接。并且携带者自己的报文的序号x。报文初始序号x不是1,而是一个随机数。[SYN=1,seq=x]

2.接收方打开TCP连接,返回一个[SYN=1,ACK=1,seq=y,ack=x+1],SYN=1表示接收方请求发送方打开一个连接;ACK=1表示对步骤1中发送方报文的确认;seq=y是接收方这份报文的序号;ack=x+1表示希望下一次接收发送方的报文时报文的序号为x+1,也就是说希望下次发送方报文的数据是x+1号之后的字节数据,x+1号之前的字节都已被确认。

3.发送方再发出一个[ACK=1,seq=x+1,ack=y+1]
ACK=1:发送方对接收方报文确认
seq=x+1:报文序号,说明该报文携带的数据是x+1之后的字节数据
ack=y+1:告诉接收方说接收方的下一个报文的序号应为y+1


通过这3次报文发送,双方打开了连接,同时同步了对方的序号(这也是三次握手的作用)

TCP连接建立的过程中发送方和接收方的状态:
在接收方接收到第一个报文之前,接收方处于监听(listen)状态

发送方发出第一个报文到接收到确认报文之间的状态是“同步已发送(sync-sent)”状态

接收方发出第一个报文到接收发送方的确认报文之间的状态是“同步已接收(sync-rcvd)”状态

发送方接收到接收方确认报文后的状态是“建立连接(established)”状态

接收方接收到确认报文后的状态是“建立连接(established)”状态

所以接收方接收到发送方请求连接报文的时候不会马上建立连接,而是要发送确认报文,让发送方先建立了连接,并接收到发送方的确认报文后才建立连接。
所以发送方是先于接收方建立连接的。

发送方是在第二次握手之后进入的连接状态。
接收方是在第三次握手后进入的连接状态。
只有接收方和发送方都进入连接状态才能够进行数据传输


一个重要的问题(笔试和面试常问):
为什么双方建立连接需要三次握手而不是只要两次握手。
答:是为了防止已失效的连接请求报文传送到接收方,引起错误。


具体情况如下:
假如两次握手就能建立连接,其过程会是 A发送SYN=1给B后,B马上进入连接状态,B返回ACK=1,A收到后A进入连接状态。接收方B先于A进入连接状态

如果,A发送请求连接报文 X1 给B,但是这个报文在网络中传输了很久。此时A在超时时间内没有收到确认报文,就会再发一个请求发送报文X2。
X2比X1早到达接收方B。
B建立连接,B返回一个确认报文。

A接收到确认报文,A建立连接。

此时X1到达B,请求B建立连接,但是B已经进入连接,所以引起错误。

如果是3次握手,则在B接收到X2的时候,B还没有建立连接,此时收到了X1报文,B会发送一个对X1的确认报文给A。但是A已经进入连接状态,所以会忽略这个确认报文。


还有另一种说法:
这种说法是:
二次握手的情况下:
A发送连接请求报文给B,B马上进入连接状态,并发送给A一个确认报文。
但是这个确认报文可能会丢失,所以A就不会进入连接状态。
此时B进入连接状态,B就会一直等待A的数据传输过来,但是实际上A没有进入连接状态。

如果是三次握手就可以避免这种情况,因为如果第一个确认报文丢失,A没有进入连接状态,B会重发一个确认报文给A,这样能够确保让A进入连接状态。B接收到A的确认报文后也才进入连接状态。

又产生一个问题,如果B没有接收到A的确认报文,B就不能进入连接状态了,那不就变成A要等待B了吗?此时不就要使用4次握手了吗?那为什么有不使用4次握手建立连接呢?
因为,采取4次甚至N次握手会使建立连接的时间太长,效率低。


===============================================

TCP连接的释放(TCP的4次挥手)


1. 发送方发送 FIN=1,seq=u 的报文,表示发送方主动提出释放连接的请求报文。
发送方进入 “FIN-WAIT-1” 的等待状态(第一个等待状态)

2.接收方返回确认报文 ACK=1,seq=v,ack=u+1 表示确认接收到发送方的释放连接的请求报文
接收方进入“关闭等待”状态。关闭等待意思是我要等等才能关闭连接,为什么要等等才关闭连接呢?很可能是因为接收方没有把数据(服务端的响应数据)发送完。此时接收方还可以继续应用层的数据的发送

发送方接收到这个确认报文会进入 “FIN-WAIT-2” (第二个等待状态)

3.等接收方把响应数据都发送完了之后,就会发送一个 FIN=1,ACK=1,seq=w,ack=u+1 的报文。这个报文表示对发送方的第一个报文进行再一次确认,并且自己也发送释放连接的请求。
接收方进入“最后确认(LAST-ACK)”的状态

4. 发送方接收到接收方第二个ACK确认报文后才会结束FIN-WAIT-2的状态,然后发送一个ACK=1,,seq=u+1,ack=w+1的报文,表示对接收方的释放连接请求确认。
发送方会进入TIME-WAIT状态,并设置一个等待计时器。等待计时器会计时2个最长报文寿命时长(2MSL约等于4分钟)才结束TIME-WAIT状态

而接收方接收到发送方的确认报文会直接进入关闭状态

但发送方要等待TIME-WAIT状态结束后才会进入关闭状态

在TIME-WAIT状态下,客户端的这个连接的端口是不会被释放的。也就是说客户端无法通过这个端口再建立一个新连接。即这个状态下,这个端口不能复用。


一个很重要的问题:
为什么需要有TIME-WAIT状态呢?
答:
1.
是为了确保发送方的最后一个报文即确认报文被接收方接收到了,从而接收方正确关闭连接。
假如这个确认报文丢失,接收方没有收到,就不会正确关闭连接。
接收方会重新发一个释放连接请求报文(FIN=1的报文),给发送方。

关键来了,假如发送方没有TIME-WAIT状态,而是直接进入关闭状态,发送方就无法再接收到接收方第二次发送的FIN=1报文了。
这就导致接收方无法关闭连接

2.确保当前连接的所有报文都已经过期
因为TIME-WAIT 会等待2MSL的时间,所以这个时间内,前面所有的可能存在网络延迟而无法准时达到接收方或发送方的报文都会因为达到报文寿命而消逝。

以防止这些延迟报文被已经建立了新连接的发送方或接收方接收到导致新连接被破坏或者干扰


=========================

套接字和套接字编程

我们使用端口来标记不同的网络进程
端口使用16个位表示,所以端口范围是0~65535

套接字就是这个一台计算机的IP加端口: 例如 23.224.23.233:80 就是一个套接字

套接字(socket)是一个抽象概念,它其实表示的是TCP连接的一端(发送端或接收端)

通过套接字可以进行数据的发送或接收。
而通过tcp连接可以进行数据的传输
一个tcp由两个套接字组成(发送方套接字和接收方套接字)

那么数据的发送,传输和接收分别是由 发送端套接字,tcp连接,接收方套接字来完成的


套接字编程的过程

服务端:
创建服务端套接字-->进程绑定套接字-->监听套接字-->接收和处理数据

客户端:
创建客户端套接字-->连接服务端的套接字(TCP三次握手在此发生)-->发送信息以下是用python实现的一个简单的套接字连接:

#服务端
# coding=utf-8

import socket

def server():
    # 创建套接字
    s = socket.socket()
    host = "127.0.0.1"
    port = 6666

    # 绑定端口
    s.bind((host,port))

    # 监听端口
    s.listen(5)     # 最多允许5个并发连接

    while True:
        sock,addr = s.accept()   # 接收客户端的数据报文(这里会阻塞),返回一个客户端套接字和客户端地址,客户端地址包括客户端ip和端口
        print("Connect Addr:" , addr)
        sock.send(b"Welcome to zbpblog!")     # 通过这个客户端套接字发送数据给客户端,这里要发送byte字节类型不能发送str类型
        sock.close()                         # 关闭连接

if __name__ == "__main__":  
    server()        # 运行服务端

PS:上面一共有两个套接字:s是服务端用于监听IP和端口的套接字;sock是客户端的套接字。一旦accept()接收到客户端的连接(connect())就会获取到这个客户端sock;
 

# 客户端
# coding=utf-8

import socket

def client(i):
    # 创建一个套接字
    s = socket.socket()

    # 连接服务端的套接字
    s.connect(("127.0.0.1",6666))

    # 接收返回的数据数据
    print("Recv msg:%s ; Client:%s" % (s.recv(1024),i))     # 限定长度为1024字节

    s.close()

if __name__ == "__main__":
    for i in range(5):
        client(i)

PS:请不要在pycharm中同时运行服务端和客户端,而是在pycharm运行客户端,在cmd中运行服务端。请先运行服务端,后运行客户端。 

对比一下网络套接字和域套接字
使用网络套接字通信需要经过协议栈,也就是要经过传输层,网络层,物理层,路由器,物理层,网络层,传输层。即使你是单机不同端口的通信,也会经过路由器和协议栈。

而域套接字是通过一个域套接字文件进行通信,无需经过协议栈。

所以,单机不同端口的通信最后使用域套接字进行,效率高。
而两台不同的机器通信只能通过网络套接字进行。

==========================================

应用层 

DNS详解
DNS(域名系统)

我们知道,要访问一个网络服务可以访问 ip:port 来访问。
例如 23.224.25.233:80 访问的是23.224.25.233的一个网站

但是对于用户来说,ip和端口不好记,所以推出了DNS

DNS服务就是将域名转换为ip和port,然后再去访问 ip:port 对应的网络进程服务


域名由点,字符和数字组成
点分割不同的域
域名可以分为顶级域,二级域和三级域。

例如 www.baidu.com 这个域名有三个域;com是顶级域,baidu是二级域,www是三级域

顶级域分为国家和通用两类:
国家如: cn,us,uk,ca
通用如:com,net,org,gov

二级域如:
qq,taobao ,google,baidu 等等很多很多


顶级域下有很多二级域,呈现一个树状结构

DNS服务是部署在服务器上的,这个服务器是域名服务器,域名服务器是国家或者机构部署的。
DNS服务可以将域名转为IP再通信。


域名服务器也是一个树状结构,在顶层有一个根域名服务器,根域名服务器下有很多顶级域名服务器,顶级域名服务器下有很多地区性的域名服务器


根域名服务器存储着各个顶级域的信息是记录在哪个顶级域名服务器。
而顶级域名服务器记录着某个顶级域名下的二级域名是记录在哪个地区域名服务器

如果用电脑访问一个域名,会先在本地域名服务器中查找这个域名对应的IP和端口。如果找不到就会去根域名服务器找这个域名的顶级域是在哪个顶级域名服务器,再到顶级域名服务器找这个域名的二级域是对应的哪个地区域名服务器,最后在地区域名服务器找到了这个域名的IP和端口。这样一层层的往下找。

这个域名和IP:port的映射会被缓存到本地域名服务器。下次就直接在本地域名服务器找就可以


==========================================

DHCP协议

DHCP 动态主机设置协议
DHCP是一个局域网
DHCP是应用UDP协议的应用层协议

我们知道,我们的手机,电脑是不用自行配置IP地址就可以上网。这就是DHCP提供的一种“即插即用联网”的功能

现在window中使用 “自动获取IP地址” 就是DHCP的作用。它会分配一个临时的IP给你的设备使用。

首先有一个DHCP服务器
当我的主机希望被分配一个随机IP的时候,主机会使用UDP协议广播“DHCP发现报文”(报文的ip是1.1.1.1)
DHCP服务器接收到报文后会发一个DHCP提供报文给主机,表示可以为主机提供DHCP服务
主机就会向DHCP服务器发出DHCP请求报文
DHCP服务器回应并提供IP给我的主机

这个过程是在局域网中进行的。

=============================================

HTTP协议详解

HTTP 超文本传输协议

http地址:
http(s)://<主机>:<端口>/<路径>
其中主机可以是IP或者域名
端口一般是80或者443

HTTP协议是基于TCP协议实现的。

Web服务器
Web服务器包括硬件和软件部分。
硬件就是一台计算机
软件常用的有Nginx和Apache

下面主要讨论软件部分,即web服务是如何接受和处理请求的
1.Web服务器要先接收客户端的(tcp)连接
2.Web服务器接收请求(报文)
3.Web服务器处理请求
4.Web服务器获取Web资源
5.Web服务器构造响应并发送响应

步骤1的过程在传输层的TCP三次握手时已经介绍过

步骤2
HTTP请求有很多请求方法
下面介绍几个主要的方法
GET     获取指定的服务端资源
POST    提交数据到服务端
DELETE  删除指定的服务端资源
UPDATE  更新指定的服务端资源

如何找到 “指定的资源”
A 在url地址中指定(很常见如 https://abc.com/article/20334.html)

B 在请求的数据中指定
就是将参数写在请求报文的请求体中

在这里我们需要对HTTP的请求和响应报文进行一定的了解

HTTP请求报文分为3部分:
请求行: 形式为 [请求方法][请求地址][HTTP版本]
例如 GET /data/info.html HTTP/1.1

请求头
例如:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: max-age=0
Connection: keep-alive
Cookie: Hm_lvt_080836300300be57b7f34f4b3e97d911=1583639375,1584365760,1584402496,1584768625; Hm_lvt_4898023a3a963db158d0eeee27c56c80=1584365739,1584402481,1584768610,1585483732; Hm_lpvt_4898023a3a963db158d0eeee27c56c80=1585483732
Host: zbpblog.com
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36
他是一个key-value的形式


请求内容(请求体)
若方法字段是GET,则此项为空,没有数据

若方法字段是POST,则请求内容就是要提交的数据

比如 user=admin&password=123456 ,使用&来连接各个字段,这是以文本的格式作为请求体
还可以以json等格式来作为请求体

报文内容如下图所示:


HTTP响应报文分为3部分:
响应行  形式为 [HTTP版本][状态码][状态解释]
如 HTTP/1.1 200 OK

响应头
如 
Connection: Keep-Alive
Content-Type: text/html; charset=utf-8
Date: Sun, 29 Mar 2020 12:08:55 GMT
Keep-Alive: timeout=5, max=97
Server: Apache/2.4.6 (CentOS) OpenSSL/1.0.2k-fips PHP/7.2.22
Transfer-Encoding: chunked
X-Powered-By: PHP/7.2.22

响应内容(体)
就是纯数据,例如html静态文件的内容,或者是json格式的数据内容

=====================================

HTTPS详解

HTTP是明文传输的。所以用HTTP传输数据是不安全的。

HTTPS是安全的HTTP协议


加密模型
有对称加密和非对称加密两种

对称加密
数据在客户端使用密钥加密,在服务端使用同一把密钥解密
两个密钥是相同的

非对称加密
数据在客户端使用密钥A(公钥)加密,在服务端使用另一把密钥B(私钥)解密
但是A,B是拥有一定数学关系的一组密钥
其中一把叫做私钥,另一把叫做公钥

密钥和公钥私钥的关系:密钥包含公钥和私钥两种

使用公钥加密后的数据(密文)只能够用私钥解密,不能用公钥解密

上面的过程中,服务器向客户端发送数据时是服务端加密,客户端解密
服务端和客户端都可以进行加密和解密

数字证书
是一个组织机构给特定对象的认证

数据证书的内容:证书序列号,签名算法,有效期,对象名称(一般是域名),对象公钥

所以证书中是有公钥的,但是没有私钥你可以就认为数字证书就是公钥
私钥是另外保存在一个私钥文件中的

数字证书和私钥文件都保存在服务器上的。

例如 
zbpblog.com.crt 这个 .crt 文件就是数字证书文件
zbpblog.com.key 这个 .key 文件就是密钥文件


SSL(安全套接层)
SSL层是介于应用层和传输层之间的一个子层
数据的加密和解密就是在SSL层完成的
所以如果使用了https,那么数据到达传输层时就已经是加密后的数据


相比于HTTP发送请求的过程,HTTPS也一样都是建立连接,发送请求,返回响应。但是HTTPS在建立连接和发送请求之间多了一步“SSL安全参数握手”的步骤

SSL安全参数握手的过程:



1.客户端生成一个随机数1。客户端将随机数1,协议版本,采用哪种加密算法发送给服务端
2.服务器生成一个随机数2,返回一个确认报文给客户端,报文内容包括确认 确认采用哪种加密算法,数字证书和随机数2
公钥在数字证书中,所以公钥其实就被传给了客户端

假设公钥是X

3.客户端接收到确认报文后,确认证书是否有效,然后生成随机数3,使用证书中的公钥X对随机数3加密

然后客户端将加密后的随机数3发送给服务端

服务端通过密钥对随机数3进行解密从而获取随机数3的明文

4.现在客户端,服务端都有了随机数1~3的明文。
双方都根据随机数1~3和相同的算法生成一个对称密钥

注意,这个对称密钥是没有经过传输的,所以减少了密钥泄漏的可能

5.客户端将请求数据通过对称密钥加密,传输给服务端,服务端通过对称密钥解密。

所以 HTTPS 是综合使用了对称加密和非对称加密两种加密模式的。


至此,TCP/IP 四层模型就已经粗略的介绍完毕


如果您需要转载,可以点击下方按钮可以进行复制粘贴;本站博客文章为原创,请转载时注明以下信息

张柏沛IT技术博客 > 浅谈操作系统原理(九) 传输层(TCP/UDP)和应用层(HTTP)

热门推荐
推荐新闻