一个Http请求的整个流程
负责域名解析的 DNS 服务
-
用户访问域名,经过DNS解析,应用层协议,提供域名到IP的解析服务
-
不用域名也可以访问目标主机的服务,但是IP本身不是那么容易记
-
加速静态内容访问速度的CDN
-
CDN (Content Delivery NetWork) 内容分发网络
-
CDN是一种网络缓存技术,把一些相对稳定的资源放到距离最终用户较近的地方
-
一节省整个广域网的带宽销毁,二提升用户的访问速度
-
静态文件放到CDN
-
HTTP协议通信
-
域名被解析之后,客户端和服务端之间,这么建立连接,并且如何通信?
-
TCP和UDP通信协议,http协议是基于tcp/ip协议之上的应用层协议,还有FTP
-
OSI七层网络模型
-
应用层
-
表示层
-
会话层
-
传输层
-
网络层
-
数据链路层
-
物理层
-
-
TCP/IP四层概念模型
-
应用层
-
传输层
-
网络层
-
数据链路层
-
-
请求发起过程,在tcp/ip四层网络模型中所作的事情
-
当应用程序用TCP传送数据时,数据被输入协议栈中,然后逐个通过每一层直到被当作一串比特流送入网络。其中每一层对收到的数据都要增加一些首部消息
-
www.xq.com 发送请求
-
传输层 [[TCP头] [HTTP请求报文] ]
-
表示当前的协议头,使用TCP协议传输
-
相当于,针对这个包裹,选择了那个公司
-
-
网络层 [[IP头] [TCP头] [HTTP请求报文] ]
-
增加IP头,IP地址是一个网卡在网络中的通讯地址,相当于门牌号。
-
这个包裹要寄送到那个地方
-
-
数据链路层 [ [MAC头] [IP头] [TCP头] [HTTP请求报文] ]
-
增加MAC头,表示这个数据包要发送到的网卡地址
-
填写的包裹要寄送给哪个具体的人
-
-
物理层 转为 01011 比特流进行传输
-
-
-
客户端如何找到目标服务
-
目标机器的mac地址怎么得到的?
-
ARP协议
-
已知目标机器的IP,需要获得目标机器得MAC地址,发送一个广播消息,这个IP是谁得,请来认领。认领的IP机器会发送一个MAC地址的响应
-
-
有了这个目标的MAC地址,数据包在链路层上广播,MAC的网卡才能发现,这个包是给它的。MAC的网卡把包收进来,然后打开IP包,发现IP地址也是自己的,再打开TCP包,发现端口也是自己的,也就是80端口,而这个时候这台机器有一个nginx是监听80端口
-
于是,请求提交给nginx,nginx返回一个网页,需要发会请求到机器,然后层层封装,最后到MAC层,因为来的时候有源MAC地址,返回的时候,源MAC就变成了目标MAC,再返回给请求的机器
-
为了避免每次都用 ARP 请求,机器本地也会进行 ARP 缓存。当然机器会不断地上线下线,
IP 也可能会变,所以 ARP 的 MAC 地址缓存过一段时间就会过期。
-
-
-
接受端收到数据包以后的处理过程
-
当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由低向上升,同时去掉各层协议加上的报文首部
-
服务器 接受请求
-
传输层 [[TCP头] [HTTP请求报文] ]
-
TCP会携带端口,将报文交给指定端口的进程进行处理
-
-
网络层 [[IP头] [TCP头] [HTTP请求报文] ]
-
拿到IP头,判断IP地址是不是自己的,如果不是就转发,继续交给上面
-
-
数据链路层 [ [MAC头] [IP头] [TCP头] [HTTP请求报文] ]
-
拿到数据以后,从数据中摘到第二层的头,检查下MAC地址和当前网卡的MAC是否匹配,如果匹配说明发给我是没错的
-
-
物理层 0101
-
当数据通过网卡的时候,看看数据是否需要请进来坐坐
-
-
-
-
为什么有了 MAC 层还要走 IP 层呢?
-
MAC地址是唯一的,MAC地址是身份证,但是跟个人所在的位置,没有关系,IP就类似于,城市+道路号+门牌号的概念
-
-
TCP/IP 的分层管理
-
TCP/IP 协议,分为四层,应用,传输,网络,数据链路层,提出了分层负载
-
分层负载
-
二层负载
-
通过改写报文的目标 MAC 地址的方式将请求转发到目标机器实现负载均衡
-
-
三层负载
-
三层负载均衡会通过一个虚拟 IP 地址接收请求,然后再分配到真实的 IP 地址
-
-
四层负载
-
四层通过虚拟 IP + 端口接收请求,然后再分配到真实的服务器
-
-
七层负载
-
七层通过虚拟的 URL 或主机名接收请求,然后再分配到真实的服务器。
-
-
-
-
-
-
TCP/IP 协议的深入分析
通信协议的原理,到底客户端跟服务器端,是怎么产生关联的?
-
TCP握手协议
-
三次握手,就是在建立TCP链接时,客户端与服务端总共发3个包来确定链接的建立
第一次 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。 第二次 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(seq=k),即SYN+ACK包,此时服务器进入SYN_RECV状态。 第三次 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。
-
CLosed CLOSED
-
Listen
-
-
SYN_SENT SYN_RECV
-
ESTABLISHED ESTABLISHED
-
ACK (Acknowledge character)即是确认字符,在数据通信中,接收站发给发送站的一种传输类控制字符。表示发来的数据已确认接收无误。
-
SYN:同步序列编号(Synchronize Sequence Numbers)。是TCP/IP建立连接时使用的握手信号。
-
SYN_RECV:是指,服务端被动打开后,接收到了客户端的SYN并且发送了ACK时的状态。再进一步接收到客户端的ACK就进入ESTABLISHED状态。
-
-
-
SYN攻击
-
Client端在短时间内伪造大量不存在的IP地址,并向Server不断发送SYN包,Server回复确认包,进入SYN_RECV状态,由于源地址不存在,这些伪造的SYN包尝试占用未连接队列导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。
-
措施
-
针对SYN攻击的几个环节,提出相应的处理方法: 方式1:减少SYN-ACK数据包的重发次数(默认是5次): sysctl -w net.ipv4.tcp_synack_retries=3 sysctl -w net.ipv4.tcp_syn_retries=3 方式2:使用SYN Cookie技术: sysctl -w net.ipv4.tcp_syncookies=1 方式3:增加backlog队列(默认是1024): sysctl -w net.ipv4.tcp_max_syn_backlog=2048 方式4:限制SYN并发数: iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT --limit 1/s
-
-
-
TCP四次挥手协议
-
TCP断开连接的时候,需要客户端和服务端总共发送4个包以确定连接的断开
-
双方均可发起挥手动作因为TCP是一个全双工协议
-
单工:广播 一个方向上传输
-
半双工 :电报 某一个时刻,只允许在一个方向上传输
-
全双工 :同时两个方向传输,它要求发送接受设备,都有独立的接受发送能力
-
-
-
流程
-
第一次挥手(FIN=1,seq=x) 假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为 1 的包,表示自己已经没有数据 可以发送了,但是仍然可以接受数据。发送完毕后,客户端进入 FIN_WAIT_1 状态。 第二次挥手(ACK=1,ACKnum=x+1) 服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求, 但还没有准备好关闭连接。发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这 个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。 第三次挥手(FIN=1,seq=w) 服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为 1。发送完毕后,服务器 端进入 LAST_ACK 状态,等待来自客户端的最后一个 ACK。 第四次挥手(ACK=1,ACKnum=w+1) 客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT 状态,等待 可能出现的要求重传的 ACK 包。 服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。 客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime) 之后,没有收到服务器端的 ACK,认为服务器端已经正常关闭连接,于是自己也关闭连接, 进入 CLOSED 状态。
-
-
问题
-
为什么连接的时候是三次握手,关闭的时候却是四次握手?
-
这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。 但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你未必会马上会关闭SOCKET,也即可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。
-
-
为什么 TIME_WAIT 状态需要经过 2MSL(最大报文段生存时间)才能返回到 CLOSE
状态?
-
虽然按道理,四个报文都发送完毕,我们可以直接进入 CLOSE 状态了,但是我们必须假 象网络是不可靠的,有可以最后一个 ACK 丢失。所以 TIME_WAIT 状态就是用来重发可能丢 失的 ACK 报文。
-
-
为什么不能用两次握手进行连接?
-
3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。 现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
-
-
如果已经建立了连接,但是客户端突然出现故障了怎么办?
-
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
-
-
-
使用协议进行通信
tcp 连接建立以后,就可以基于这个连接通道来发送和接受消息
-
socket 是一种抽象层,应用程序通过它来发送和接收数据的
-
可以把应用程序添加到网络中,并与处于同一个网络中的其他应 用程序进行通信
流程:
-
应用程序
-
TCP套接字
-
TCP端口
-
IP
理解 TCP 的通信原理及 IO 阻塞
对于 TCP 通信来说,每个 TCP Socket 的内核中都有一个发送缓冲区和一个接收缓冲区, TCP 的全双工的工作模式及 TCP 的滑动窗口就是依赖于这两个独立的 Buffer 和该 Buffer的填充状态。
发送端 接受端
send() recv()
TCP发送缓存区 TCP接受缓存区
TCP报文段 TCP报文端
应用层用户的Buffer -> 接受缓存区 -> 内核 -> 网卡
-
socket的read的工作就是把内核接受缓冲区中的数据复制到应用层用户的Buffer
-
send,则是将应用层用户的Buffer复制到 Socket 的内核发送缓冲区
-
如果应用进程一直没有读取,那么 Buffer 满了以后
-
通知对端 TCP 协议中的窗口关闭,保证 TCP 接收缓冲区不会移除,保证了 TCP 是可靠传输的
-
如果对 方无视窗口大小发出了超过窗口大小的数据,那么接收方会把这些数据丢弃。
-
-
滑动窗口协议
-
滑动窗口(Sliding window)是一种流量控制技 术。早期的网络通信中,通信双方不会考虑网络的拥挤情况直接发送数据。由于大家不知道 网络拥塞状况,同时发送数据,导致中间节点阻塞掉包,谁也发不了数据,所以就有了滑动 窗口机制来解决此问题;发送和接受方都会维护一个数据帧的序列,这个序列被称作窗口
-
发送窗口
-
就是发送端允许连续发送的幀的序号表。
发送端可以不等待应答而连续发送的最大幀数称为发送窗口的尺寸。
-
-
接受窗口
-
接收方允许接收的幀的序号表,凡落在 接收窗口内的幀,接收方都必须处理,落在接收窗口
外的幀被丢弃。
接收方每次允许接收的幀数称为接收窗口的尺寸
-
-
理解阻塞到底是什么回事
accept 是一个阻塞的方法,意味着 TCP 服务器一次 只能处理一个客户端请求,当一个客户端向一个已经被其他客户端占用的服务器发送连接请 求时,虽然在连接建立后可以向服务端发送数据,但是在服务端处理完之前的请求之前,却不会对新的客户端做出响应
一个客户端对应一个线程
-
弊端
-
创建一个线程需要占用 CPU 的资 源和内存资源
-
线程数增加,系统资源将会成为瓶颈最终达到一个不可控的状 态,所以我们还可以通过线程池来实现多个客户端请求的功能,因为线程池是可控的。
-
非阻塞模型
-
不管是线程池还是单个线程,线程本身的处理个数是有限制的
-
如果线程数太多会造成 CPU 上下文切换的开销。因 此这种方式不能解决根本问题
提出了NIO new IO
阻塞 IO
-
客户端的数据从网卡缓冲区复制到内核缓冲区之前,服务端会一直阻塞
非阻塞 IO
-
进程空间调用 recvfrom,如果这个时候内核缓冲区没有数据的话,就直接返回一个 EWOULDBLOCK 错误,然后应用程序通过不断轮询来检查这个 状态状态,看内核是不是有数据过来
I/O 复用模型
-
通过一种机制(系 统内核缓冲 I/O 数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是 读就绪或写就绪),能够通知程序进行相应的读写操作
-
什么是 fd:在 linux 中,内核把所有的外部设备都当成是一个文件来操作,对一个文件的读
写会调用内核提供的系统命令,返回一个 fd(文件描述符)。而对于一个 socket 的读写也会有
相应的文件描述符,成为 socketfd
IO 多路复用方式有【select、poll、epoll】,
select
-
进程可以通过把一个或者多个 fd 传递给 select 系统调用,进程会阻塞在 select 操作上,这样 select 可以帮我们检测多个 fd 是否处于就绪状态。
-
缺:
-
么当前进程需要线性轮询所有的 fd,也就是监听的 fd 越多,性能开销越大
-
select 在单个进程中能打开的 fd 是有限制的,默认是 1024,对于那些需要支持单机上万的 TCP 连接来说确实有点少
-
epoll
-
epoll 是基于事件驱动方式来代替顺序扫描,因此性 能相对来说更高
-
当被监听的 fd 中,有 fd 就绪时,会告知当前进程具体哪一
个 fd 就绪,那么当前进程只需要去从指定的 fd 上读取数据即可
-
【由于 epoll 能够通过事件告知应用进程哪个 fd 是可读的,所以我们也称这种 IO 为异步非
阻塞 IO,当然它是伪异步的,因为它还需要去把数据从内核同步复制到用户空间中,真正的
异步非阻塞,应该是数据已经完全准备好了,我只需要从用户空间读就行
多路复用的好处
-
I/O 多路复用可以通过把多个 I/O 的阻塞复用到同一个 select 的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。它的最大优势是系统开销小,并且不需要创建新的进程或者线程,降低了系统的资源开销