目录
- 一、OSI七层模型
- 二、TCP/IP四层模型
-
- 1.访问网页的全过程
- 2.应用层
-
- [2.1 HTTP](#2.1 HTTP)
-
- [2.1.1 HTTP 是不保存状态的协议, 如何保存用户状态?](#2.1.1 HTTP 是不保存状态的协议, 如何保存用户状态?)
- [2.1.2 URL结构](#2.1.2 URL结构)
- [2.1.3 状态码](#2.1.3 状态码)
- [2.2 WebSocket](#2.2 WebSocket)
-
- [2.2.1 实现消息推送](#2.2.1 实现消息推送)
- [2.3 SMTP、POP3/IMAP](#2.3 SMTP、POP3/IMAP)
- [2.4 FTP](#2.4 FTP)
- [2.5 SSH](#2.5 SSH)
- [2.6 DNS](#2.6 DNS)
-
- [2.6.1 重定向](#2.6.1 重定向)
- [2.6.2 请求转发](#2.6.2 请求转发)
- 3.传输层
-
- [3.1 TCP与UDP区别](#3.1 TCP与UDP区别)
- [3.2 三次握手](#3.2 三次握手)
- [3.3 四次挥手](#3.3 四次挥手)
- 4.网络层
-
- [4.1 NAT](#4.1 NAT)
- [4.2 ARP](#4.2 ARP)
- 5.物理层
一、OSI七层模型
物链网输话示用:

二、TCP/IP四层模型
物网输用:

1.访问网页的全过程
假设网络拓扑:主机A(子网1) → 路由器R1(局域网端口p1,局域网端口p2,广域网端口p) → 路由器R2 → ... → 路由器Rn(主机B的网关) → 主机B(子网N)
- 主机A发送HTTP请求
https://www.badu.com/到主机B,首先向本地DNS服务器 发送DNS请求,用于获取目的IP。 - 本地 DNS服务器如果没有缓存www.baidu.com的IP,那么首先会访问根 DNS服务器询问com顶级DNS服务器的IP,然后访问顶级 DNS服务器的IP询问baidu.com权威DNS服务器的IP,然后访问权威 DNS服务器询问www.baidu.com的IP,并在DNS表中记录
<域名,IP>,将目的IP返回给主机A。 - 主机A根据
子网掩码判断主机B不在该局域网内 ,所以需要将请求发送给路由器。 - 主机A需要局域网内路由器端口p1的MAC ,所以使用ARP向局域网内的所有主机广播 R1路由器p1的IP ,端口p1接收到广播后单播响应 自己的MAC给主机A。
- 主机A在ARP表中记录
<IP,MAC>,封装报文{主机A的局域网IP,主机B的广域网IP,主机A的MAC,路由器端口p1的MAC,发送的数据},发送给端口p1。 - 由于当前报文是局域网IP,不同局域网之间会重复,所以路由器R1创建一个唯一的端口PortA,在NAT表中记录
<主机A的局域网IP:Port,广域网IP:PortA>。 - 路由器R1查询路由表
<IP,子网掩码,下一跳>,选择与主机B的广域网IP位于同一子网的路由器R2的IP,基于ARP发送IP获取R2的MAC。 - 修改报文
{主机A所在局域网的广域网IP:PortA,主机B的广域网IP:Port,路由器R1端口p的MAC,路由器R2的MAC,发送的数据},将报文发送给下一跳R2。 - R2同理查路由表找到IP,使用ARP获取MAC,修改报文的源MAC和目的MAC发送给Rn。
- Rn接收到报文,检查目的IP是自己,这表示当前的报文是发给该局域网内的主机的,路由器Rn检查NAT表获取主机B的局域网IP,检查ARP表获取主机B的MAC地址修改报文
{主机A的共享广域网IP:PortA,主机B的局域网IP:PortB,路由器Rn的MAC,主机B的MAC,发送的数据},发送给主机B。 - 主机B收到报文。
- 三次握手也是上述流程,只不过不封装HTTP直接使用TCP。
2.应用层
两个端口之间传输报文。
2.1 HTTP
基于TCP协议在Web服务器之间传输文本和多媒体内容 的协议,因此发送 HTTP 请求之前要三次握手建立TCP连接。
客户端向服务器发送 HTTP Request,服务器响应请求并返回 HTTP Response。HTTP协议只支持客户端主动请求,服务端被动响应。
2.1.1 HTTP 是不保存状态的协议, 如何保存用户状态?
HTTP 协议本身是无状态的,这意味着服务器默认情况下无法区分两个连续的请求是否来自同一个用户,或者同一个用户之前的操作是什么。
所以一般手动配合Cookie和Session进行信息保存。服务器为建立TCP连接的用户创建一个专属的Session session = getSession()对象,并分配一个唯一的 SessionID。服务器编辑响应报文时将SessionID写入响应头中的Set-Cookie字段发送给用户的浏览器。浏览器接收到SessionID后保存在浏览器内存,后续每次向该服务器发请求,浏览器都会自动带上SessionID。服务器收到请求后,根据SessionID就能找到对应的Session对象,从而知道这是哪个用户以及他之前的状态了。
2.1.2 URL结构

- 协议
- 域名:IP的可读版本
- 端口
- 资源路径
- 参数:用于Get请求
- 锚点
2.1.3 状态码
2XX:成功 状态码200 OK:请求被成功处理,服务器正确返回了数据。201 Created:请求被成功处理,创建了新的资源。202 Accepted:服务器接收到请求但是还未处理。204 No Content:服务器成功处理请求,单位返回任何数据。
3XX:重定向 状态码301 Moved Permanently:资源被永久重定向了,网站的网址更换了。302 Found:资源被临时重定向了,网站的某些资源被暂时转移到另外一个网址。
4XX:客户端错误 状态码400 Bad Request:发送的 HTTP 请求存在问题。401 Unauthorized:未认证却请求需要认证之后才能访问的资源。403 Forbidden:请求被拒绝,一般用来针对非法请求。404 Not Found:请求的资源未在服务端找到。409 Conflict:请求的资源与服务端当前的状态存在冲突,请求无法被处理。
5XX:服务器错误 状态码500 Internal Server Error:服务端出问题了。502 Bad Gateway:网关将请求转发到服务端,但是服务端返回的却是一个错误的响应。
2.2 WebSocket
基于TCP连接,客户端和服务器可以同时发送和接收数据 ,用于解决HTTP协议只支持单向请求、以及在长时间通信能力的不足。客户端和服务器仅需一次握手,建立连接过程如下:
- WebSocket请求基于HTTP协议,三次握手建立TCP连接后,客户端向服务器发送一个HTTP请求 ,请求头中包含
Upgrade: websocket、Sec-WebSocket-Key等字段,表示要求升级协议为WebSocket。 - 服务器收到请求后,回复
HTTP 101状态码,响应头中包含Connection: Upgrade、Sec-WebSocket-Accept: xxx等字段、表示成功升级到WebSocket协议。 - 建立WebSocket连接后,客户端和服务器都可以主动发送请求。
- 建立WebSocket连接之后,通过心跳机制来保持WebSocket连接的稳定性和活跃性
2.2.1 实现消息推送
客户端(前端)通过ws://localhost:7777/webSocket/10086与服务器建立WebSocket连接 ,用户A与用户B通信时,两个客户端都要与服务器建立WebSocket连接,然后用户A将消息发送给服务器,服务器接收到用户A的消息后将消息发给用户B。这应该就是QQ和微信实时接收消息的逻辑。
@ServerEndpoint注解的优先级 比@Component高,所以会为每个WebSocket链接建立一个单例对象 ,因此每个链接独占session和userId成员变量。而将webSocketSet和sessionMap设置为static保证该类的所有WebSocket对象共享该static变量。(这也是static的作用)- 客户端A发送消息后会自动执行服务器中客户端A与服务器建立的WebSocket对象 的
onMessage(message)方法,我们可以在方法内编写将message发送给客户端B的逻辑 ,可以规定message前16个字符为目标客户端的id,这样sessionMap.get(message.substring(16)).getAsyncRemote().sendText(message)就可以将消息发给客户端B了。
java
@Component
@Slf4j
@ServerEndpoint("/websocket/{userId}")
public class WebSocketServer {
// 当前连接的会话
private Session session;
// 当前连接的用户ID
private String userId;
// 存储所有活跃的WebSocket连接实例(线程安全)
private static final Set<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
// 存储用户ID和对应Session的映射(改用线程安全的ConcurrentHashMap)
private static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
this.session = session;
this.userId = userId;
webSocketSet.add(this);
sessionMap.put(userId, session);
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
// 移除当前连接的实例和session映射
webSocketSet.remove(this);
sessionMap.remove(this.userId);
}
/**
* 收到客户端消息调用的方法
*/
@OnMessage
public void onMessage(String message, Session session) {
// 向客户端A返回发送成功
sendOneMessage(this.userId, "已收到消息:" + message);
// 向客户端B发送消息,message.substring()截取客户端b的id,前提是规定前16个字符是目标客户端ID
sendOneMessage(message.substring(16), "来自客户端A的消息:" + message);
}
/**
* 单点推送:向指定用户发送消息
* @param userId 目标用户ID
* @param message 推送内容
*/
public void sendOneMessage(String userId, String message) {
Session targetSession = sessionMap.get(userId);
// 异步发送消息(非阻塞)
targetSession.getAsyncRemote().sendText(message);
}
/**
* 广播推送:向所有在线用户发送消息
*/
public void sendBroadcastMessage(String message) {
for (WebSocketServer webSocket : webSocketSet) {
webSocket.session.getAsyncRemote().sendText(message);
}
}
}
2.3 SMTP、POP3/IMAP
SMTP基于TCP协议发送电子邮件、 POP3/IMAP基于TCP协议接收电子邮件,所以也需要三次握手确保可靠性。
- 向邮箱服务器发送SMTP请求,将邮件交给163邮箱服务器。
- 163邮箱服务器发现我发送的邮箱是QQ邮箱,然后使用SMTP协议将邮件转发到QQ邮箱服务器。
- QQ邮箱服务器接收邮件之后就通知目标用户来收邮件,目标用户通过POP3/IMAP协议将邮件取出。
2.4 FTP
基于TCP协议传输文件的协议。
2.5 SSH
基于TCP协议通过加密和认证机制实现安全传输。
2.6 DNS
基于UDP协议管理域名和IP地址的映射。
浏览器在本地会维护一个hosts列表 ,如果本地hosts列表内没有域名-IP对应记录的话,那么就需要DNS。
- 本地DNS服务器:当主机发出DNS请求时会首先被发往本地DNS服务器,起到代理的作用。
- 根DNS服务器:提供顶级DNS服务器的IP地址
{<com, ip>,<org, ip>...}。 - 顶级DNS服务器:每个服务器对应一个
com、org、net、edu后缀 ,记录该后缀下的二级IP 地址{<a.com, ip>,<b.com, ip>...}。 - 权威DNS服务器:记录三级IP地址,对应每台主机。
2.6.1 重定向

主机cis.poly.edu想知道gaia.cs.umass.edu的IP地址:
- 首先发送给本地DNS服务器代理查询。
- 本地DNS首先向根DNS服务器查edu的顶级DNS服务器IP。
- 本地DNS向edu的顶级DNS服务器查umass.edu的权威DNS服务器IP。
- 本地DNS向umass.edu的权威DNS服务器查gaia.cs.umass.edu对应的主机IP。
2.6.2 请求转发

3.传输层
负责建立 两台设备进程之间的通信。
- TCP:提供面向连接 的、可靠的数据传输服务。
- UDP:提供无连接 的、尽最大努力的数据传输服务,不保证可靠性。
3.1 TCP与UDP区别
- TCP要建立连接才能发送数据,UDP可以直接发送数据。
- TCP通过SEQ、ACK、重传等机制保证数据有序不丢失,UDP不保证数据数据到达的顺序。
- TCP需要维护连接状态,UDP发完即走不关心数据是否达到,速度更快。
- TCP只支持点对点,UDP支持一对多(多播)、多对多(广播)。
- TCP头部占用20~60B,UDP只占8B。
3.2 三次握手

TCP依赖序列号(SEQ)与确认号(ACK)实现可靠传输,发送方发送seq=x,接收方发送ack=x+1回复x+1之前的序列都已经收到,保证了数据有序不丢失。
服务端收到SYN并返回SYN+ACK后,连接进入SYN_RCVD,等待客户端最终ACK。如果一直收不到 ACK,内核会按重传策略重发SYN+ACK,超时则清除该连接。
为什么要三次握手而不是两次?第二次握手保证了Server对Client从哪一个序号开始收发数据达成一致,第三次握手是为了保证Server的seq序列号同步,使Client对Server"从哪一个序号开始收发数据"达成一致 ,所以第三次握手只是为了确认ack,所以seq可以携带数据。
3.3 四次挥手

前两次挥手是为了确保Client端的seq数据在Server端全部接收到 ;后两次挥手是为了确保Server端的seq数据在Client端全部接收到。
由于服务器发送ACK后会标记无法向对方客户端发送消息,防止其他线程再向对方发送消息。然后才会发送FIN,所以无法合并服务器端的两步操作。
4.网络层
将报文段封装成IP数据报 ,路由找到目标主机。
- IP:定义数据包的格式 ,每个连接互联网的设备都会被分配唯一的IP,ipv4 32位,ipv6 128位。
- ARP:管理IP逻辑地址和物理层MAC物理地址之间的映射。
- NAT:管理局域网IP到广域网IP的映射。
- OSPF:动态路由协议,基于链路状态算法,考虑链路的带宽、延迟等因素来选择最佳路由路径。
- RIP:动态路由协议,基于距离向量算法,选择跳数最少的路径作为最佳路由路径。
- ICMP:传输网络状态和错误消息。
4.1 NAT
局域网内主机的IP是局域网IP ,也就是说不通局域网之间的主机IP会重复,这样是为了避免IPV4耗尽。同一局域网内的所有主机都使用同一个广域网IP与其他局域网之间的主机进行通信。
**一台路由器有多个端口,每个端口都有各自的IP和MAC。
- Wan口用于局域网内的主机与外部Internet通信,只有一个。
- 有多个LAN口,每个LAN口都是一个局域网。
NAT表一个路由器只有一张 ,用来记录<局域网IP:Port,广域网IP:Port>的映射关系,请求到达路由器时,由于携带的是Wan口的IP,所以就要通过NAT表根据端口找到目的主机 。

- 局域网中
10.0.0.1~10.0.0.4都是局域网IP,局域网内部的通信可以直接使用局域网IP。 - 但是由于不同局域网之间IP会重复,所以局域网中的四台主机共享
138.76.29.7这个一个广域网IP ,主机10.0.0.1:port1发送HTTP请求时如果目的IP地址是局域网外的IP,那么路由器将为该请求创建一个在NET中不重复的端口号port2,并使用138.76.29.7:port2向外部网络发送HTTP请求,并在NAT表中记录<138.76.29.7:port2,10.0.0.1:port1>,方便响应返回时路由器知道发给局域网内的哪台主机。
4.2 ARP
MAC地址的长度为6B,是一个网络设备真正的固定 身份标识,IP地址只是可变 的一种不重复 的身份标识。路由器具有多个端口,每个端口都有IP和MAC地址且各自维护一个ARP表 记录了该局域网 内其他网络设备的IP-MAC映射关系。
当请求到达路由器某个端口时,根据子网掩码判断目标主机在该端口对应的局域网内 ,由于路由器端口只知道局域网内主机的IP,并不知道MAC,所哟需要FF-FF-FF-FF-FF-FF广播发送ARP请求给局域网内的所有主机,携带目标MAC地址,对应的主机接收到请求后返回ARP响应给路由器端口,这样路由器端口就知道将请求发送给哪个主机了,并将缓存信息记录到ARP表中。
5.物理层
在相邻计算机节点之间传送比特流。