一个 http 请求的整个流程
负责域名解析的 DNS 服务
DNS服务提供通过域名来找到对应的ip,使用域名的原因是域名比ip地址更好记忆。
image.png
加速静态内容访问速度的 CDN
CDN是一种内容分发网络(Content Delivery Network),将相对稳定的内容放在离用户最近的内容,这样做的好处是用户的访问速度会更快和节省整个广域网的消耗,我们一般会把静态的文件如图片,脚本和静态文件放在CDN中
HTTP 协议通信原理
HTTP是应用层协议,TCP/UDP是传输层的协议。
image.png
请求发起过程,在 tcp/ip 四层网络模型中所做的事情不停的封装如下图
TCP头:根据端口号定位进程,用于点对点传输
IP头:根据ip定位主机,用于段对端传输
MAC头:根据mac地址定位网卡
image.png
注:目的mac地址的获取
通过ARP(Adress Resolve Protocol)可以根据已知ip获取mac地址
接收端收到数据包以后的处理过程不停的解封装如下图
image.png注:为什么有了mac地址还需要ip
mac地址表示人的身份证,全局唯一;
IP地址表示一台机器在网络中的位置类似于城市名+道路号+门牌号
TCP/IP 的分层管理
分层的原因:降低复杂度和增加灵活性
分层负载(OSI的七层模型)
对于分层来讲,可以有下层没上层,但不可以没上层无下层如路由器。
二层负载均衡
针对的是MAC地址,特点:一个集群提供一个虚拟MAC地址,通过改写报文的目的MAC地址来请求到具体的服务器
三层负载均衡
针对的是IP,特点:一个集群提供一个虚拟ip,通过负载均衡算法来请求到具体的服务器
四层负载均衡
针对的是传输层(IP + 端口号),特点:一个集群提供一个虚拟ip,通过修改数据包的地址信息(IP + 端口号)来请求到具体的服务器
七层负载均衡
针对的是应用层,特点:通过虚拟的url或主机名来接受请求,然后在分配到具体的服务器
TCP/IP 协议的深入分析
TCP类似于打电脑,UDP协议类似于校园广播
TCP 握手协议
TCP的可靠性是连接的建立,通过三次握手来建立连接,即代码的connect的过程
image.png
ACK 可设为 1/0,1表示确认号有效,0表示确认号无效。
SYN:用在连接建立的过程
Seq:表示初始序号
第一次:客户端发送完毕:客户端进入SYN_SEND 状态
第二次:服务端发送完毕:服务端进入SYN_RCVD 状态
第三次:客户端发送完毕:客户端进入ESTABLISHED状态
服务端接受完毕:服务端进入ESTABLISHED状态
SYN 攻击
客户端伪造大量发送不存在的ip包(源ip),服务器确认,等待客户端的确认恢复,从而导致超时和正常的syn请求因队列满而被丢弃。
TCP 四次挥手协议
TCP协议是全双工的,对应的代码是客户端和服务端的close操作。
image.png
FIN:被用来释放连接,它表示发送方已经没有数据要传输了,但是可以继续接收数据
第一次挥手:客户端发送完成:客户端进入 FIN_WAIT_1 状态
第二次挥手:服务端发送完成:服务器端进入 CLOSE_WAIT 状态,客户端接收完成:客户端进入 FIN_WAIT_2 状态,服务端准备关闭
第三次挥手:服务端发送完成:服务端进入LAST_ACK,服务端开始关闭
第四次挥手:客户端发送完成:客户端进入TIME_WAIT,客户端在ZMSL的时间内,没有接收到ACK包,则自动进入CLOSED状态
服务端接收完成:服务端进入CLOSED状态
问题
为什么连接的时候是三次握手,关闭的时候却是四次握手?
三次握手是因为ACK和SYN可以一起发送;
四次挥手是因为ACK和FIN不可以一起发送,第二次挥手表示对客户端的确认,但服务端关闭没有准备好,第三次挥手表示服务端关闭准备好了
为什么 TIME_WAIT 状态需要经过 2MSL(最大报文段生存时间)才能返回到 CLOSE状态?
因为网络是不可靠的,会导致最后一个ACK会出现丢失的情,从而引发服务端的超时重传。
使用协议进行通信
通过socket实现网络通信
基于 TCP 协议实现通信
客户端
public static void main(String[] args)
{
Socket socket=null;
PrintWriter out=null;
try {
socket=new Socket("127.0.0.1",8080);
out=new PrintWriter(socket.getOutputStream(),true);
out.println("Hello, Mic");
} catch (IOException e) {
e.printStackTrace();
}finally {
if(out!=null){
out.close();
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务端
public static void main(String[] args)
throws IOException {
ServerSocket serverSocket = null;
BufferedReader in = null;
try {
/**
* 通过构造器来绑定这个服务(bind),来监听端口
* ip:来定位网卡,因为一个机器中会有多个网卡,也会有多个ip
* port:定位应用程序
*/
serverSocket = new ServerSocket(8080);
//阻塞服务端
Socket socket = serverSocket.accept();
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println(in.readLine());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (serverSocket != null) {
serverSocket.close();
}
}
}
基于 TCP 实现双向通信对话功能
客户端
public static void main(String[] args) {
try {
//找到目标的ip和端口
Socket socket=new Socket("localhost",8080);
//在当前链接上写入输入
PrintWriter out=new PrintWriter(socket.getOutputStream(),true);
//控制台的输入流
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
//拿到输入流
BufferedReader in=new BufferedReader(new InputStreamReader
(socket.getInputStream()));
String readline=sin.readLine(); //获得控制台的输入
while(!readline.equals("bye")){
out.println(readline);
System.out.println("Server:"+in.readLine());
readline=sin.readLine(); //重新获取
}
} catch (IOException e) {
e.printStackTrace();
}
}
服务端
public static void main(String[] args) {
ServerSocket serverSocket=null;
try {
//服务端一定需要去监听一个端口号,ip默认就是本机的ip地址
serverSocket=new ServerSocket(8080);
Socket socket = serverSocket.accept();
//拿到输入流(阻塞, read/write阻塞)
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//输出流
PrintWriter printWriter=new PrintWriter(socket.getOutputStream());
//通过控制台拿到数据
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("Client:"+in.readLine()); //获得输入流的信息
String line=sin.readLine(); //获得控制台输入的数据
while(!line.equals("bye")){
printWriter.println(line); //写回到客户端
printWriter.flush();
System.out.println("client:"+in.readLine()); //读取客户端传过来的数据
line=sin.readLine(); //重新读取控制台的数据
}
System.out.println(in.readLine()); //获得客户端的输入信息
} catch (IOException e) {
e.printStackTrace();
}
}
socket通信模型
image.png理解TCP的通信原理及IO阻塞
了解 TCP 协议的通信过程
image.pngsend():将数据发送到Tcp发送缓冲区
recv():将数据从Tcp接收缓冲区中读取
若TCP接收缓冲区满了,则滑动窗口关闭。
滑动窗口协议
发送窗口
发送端可以不等待应答而连续发送的最大幀数称为发送窗口的尺寸。
接收窗口
接收方允许接收的幀的序号表,凡落在接收窗口内的幀,接收方都必须处理,落在接收窗口外的幀被丢弃。
理解阻塞到底是什么回事
阻塞指一个服务端只能处理一个客户端的请求,其他客户端的请求阻塞。
一个客户端对应一个线程
缺点:线程的个数受限,若线程过多时,增加cpu上下文的切换
image.png
非阻塞模型
阻塞IO
阻塞IO是当客户端的数据从网卡缓冲区复制到内核缓冲区之前,服务端会一直阻塞。以socket接口为例,进程空间中调用 recvfrom,进程从调用 recvfrom 开始到它返回的整段时间内都是被阻塞的,因此被成为阻塞 IO 模型
image.png
非阻塞 IO
非阻塞 IO 模型的原理很简单,就是进程空间调用 recvfrom,如果这个时候内核缓冲区没有数据的话,就直接返回一个 EWOULDBLOCK 错误,然后应用程序通过不断轮询来检查这个状态状态,看内核是不是有数据过来。
image.png
I/O 复用模型
I/O 多路复用的本质是通过一种机制(系统内核缓冲 I/O 数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作
网友评论