本文参考 JavaGuide

网络

常见网络模型

  • OSI七层模型:如下。
  • TCP/IP四层模型:如下。
  • 混合模型:混合模型结合了OSI模型和TCP/IP模型的优点,以更好地解释和实现网络通信。
    • 五层模型: 物理层、数据链路层、网络层、传输层和应用层。 去除了OSI模型中的会话层和表示层,将它们的功能合并到应用层中。
  • IEEE 802模型:主要用于局域网(LAN)和城域网(MAN)。包含物理层和数据链路层,进一步细分为介质访问控制(MAC)层和逻辑链路控制(LLC)层。

OSI七层模型

  • 应用层:为计算机提供服务
  • 表示层:数据处理(编码、解码、加密解密、压缩解压缩)
  • 会话层:管理(建立、维护、重连)应用程序之间的会话
  • 传输层:为两台主机进程之间的通信提供通用的数据传输服务
  • 网络层:转发、路由和寻址(决定数据在网络中的游走路径)
  • 数据链路层:帧编码和误差纠正控制
  • 物理层:透明地传送比特流传输

TCP/IP四层模型

  • 应用层(应用层、表示层、会话层)
  • 传输层
  • 网络层
  • 网络接口层(数据链路层、物理层)

为什么网络要分层

复杂系统需要分层,每层专注于一类事情。主要有三个原因:

  • 各层之间相互独立,不需要关注其他层如何实现的,只需知道如何调用下层的接口。
  • 提高了灵活性和可替换性。每层可使用最合适的技术实现,只需保证提供的功能及接口没改变就行。这也与平时系统开发高内聚、低耦合的原则契合。
  • 将复杂的网络问题分解为较小的、清晰的小问题来处理解决,使得计算机网络系统易于设计和维护。

常见网络协议

应用层协议

  • HTTP(Hypertext Transfer Protocol,超文本传输协议):基于 TCP 协议,是一种用于传输超文本和多媒体内容的协议,主要是为 Web 浏览器与 Web 服务器之间的通信而设计的。
  • SMTP(Simple Mail Transfer Protocol,简单邮件发送协议):基于 TCP 协议,是一种用于发送电子邮件的协议。
  • POP3/IMAP(邮件接收协议):基于 TCP 协议,两者都是负责邮件接收的协议。IMAP 协议是比 POP3 更新的协议,它在功能和性能上都更加强大。IMAP 支持邮件搜索、标记、分类、归档等高级功能,而且可以在多个设备之间同步邮件状态。
  • FTP(File Transfer Protocol,文件传输协议): 基于 TCP 协议,是一种用于在计算机之间传输文件的协议,可以屏蔽操作系统和文件存储方式。(不安全,更安全可以用SFTP)
  • Telnet(远程登陆协议):基于 TCP 协议,用于通过一个终端登陆到其他服务器。Telnet 协议的最大缺点之一是所有数据(包括用户名和密码)均以明文形式发送,这有潜在的安全风险。
  • SSH(Secure Shell Protocol,安全的网络传输协议):基于 TCP 协议,通过加密和认证机制实现安全的访问和文件传输等业务。
  • RTP(Real-time Transport Protocol,实时传输协议):通常基于 UDP 协议,但也支持 TCP 协议。它提供了端到端的实时传输数据的功能,但不包含资源预留存、不保证实时传输质量,这些功能由 WebRTC 实现。
  • DNS(Domain Name System,域名管理系统): 基于 UDP 协议,用于解决域名和 IP 地址的映射问题,端口为 53。

传输层协议

  • TCP(Transmission Control Protocol,传输控制协议 ):提供 面向连接 的,可靠 的数据传输服务。
  • UDP(User Datagram Protocol,用户数据协议):提供 无连接 的,尽最大努力 的数据传输服务(不保证数据传输的可靠性),简单高效。

网络层协议

  • IP(Internet Protocol,网际协议):TCP/IP 协议中最重要的协议之一,主要作用是定义数据包的格式、对数据包进行路由和寻址,以便它们可以跨网络传播并到达正确的目的地。(IPv4、IPv6)
  • ARP(Address Resolution Protocol,地址解析协议):ARP 协议解决的是网络层地址和链路层地址之间的转换问题(IP 地址转 MAC 地址)。
  • ICMP(Internet Control Message Protocol,互联网控制报文协议):一种用于传输网络状态和错误消息的协议,常用于网络诊断和故障排除。
  • NAT(Network Address Translation,网络地址转换协议):用于内部网到外部网的地址转换过程中。
  • OSPF(Open Shortest Path First,开放式最短路径优先):一种内部网关协议(Interior Gateway Protocol,IGP),也是广泛使用的一种动态路由协议,基于链路状态算法,考虑了链路的带宽、延迟等因素来选择最佳路径。
  • RIP(Routing Information Protocol,路由信息协议):一种内部网关协议(Interior Gateway Protocol,IGP),也是一种动态路由协议,基于距离向量算法,使用固定的跳数作为度量标准,选择跳数最少的路径作为最佳路径。
  • BGP(Border Gateway Protocol,边界网关协议):一种用来在路由选择域之间交换网络层可达性信息(Network Layer Reachability Information,NLRI)的路由选择协议,具有高度的灵活性和可扩展性。

浏览器输入URL到页面展示的过程

  1. 在浏览器中输入指定网页的 URL。
  2. 浏览器通过 DNS 协议,获取域名对应的 IP 地址。
  3. 浏览器根据 IP 地址和端口号,向目标服务器发起一个 TCP 连接请求。
  4. 连接建立后,浏览器在 TCP 连接上,向服务器发送一个 HTTP 请求报文,请求获取网页的内容。
  5. 服务器收到 HTTP 请求报文后,处理请求,并返回 HTTP 响应报文给浏览器。
  6. 浏览器收到 HTTP 响应报文后,解析响应体中的 HTML 代码,渲染网页的结构和样式,同时根据 HTML 中的其他资源的 URL(如图片、CSS、JS 等),再次发起 HTTP 请求,获取这些资源的内容,直到网页完全加载显示。
    • 在解析HTML的过程中,浏览器会遇到外部资源如CSS、JavaScript、图像等,会再次发送请求获取这些资源。
    • 浏览器解析CSS文件,构建CSSOM(CSS Object Model),并与DOM树合并形成渲染树(Render Tree)。
    • 如果有JavaScript代码,浏览器会暂停渲染,先执行JS代码,这可能改变DOM结构和样式。
    • 浏览器计算每个元素的位置和尺寸,进行布局,然后将渲染树绘制到屏幕上。
    • 事件监听与用户交互:直到页面变得可交互,浏览器开始监听用户的动作,如点击、滚动等。
  7. 浏览器在页面加载完成后或不需要和服务器通信时,可以主动关闭 TCP 连接,或者等待服务器的关闭请求。
  8. 后续操作:浏览器可能继续监听用户事件,执行异步JS代码,处理AJAX请求等。

URL的组成

URL(Uniform Resource Locators),即统一资源定位器。网络上的所有资源都靠 URL 来定位,每一个文件就对应着一个 URL,就像是路径地址。

URL的组成

  • 协议:用于指定访问资源的协议,如 http、https、ftp(文件传输)、file(本地文件)、mailto(邮件)、telnet(远程登录)等。
  • 域名/IP:用于指定资源所在的主机名或 IP 地址。
  • 端口号:用于指定访问资源的端口号,HTTP默认端口号是 80,HTTPS默认端口号是 443。
  • 资源路径:从第一个/开始,表示从服务器上根目录开始进行索引到的文件路径。
  • 参数:浏览器在向服务器提交请求时,在 URL 中附带着参数会提取这些参数。参数采用键值对的形式key=value,每一个键值对使用&隔开。
  • 锚点:锚点就是在要访问的页面上的一个锚点,可以直接跳转到指定的位置。在 URL 中,锚点以#开头,并且不会作为请求的一部分发送给服务端。

DNS域名系统

DNS(Domain Name System)域名管理系统,是当用户使用浏览器访问网址之后,使用的第一个重要协议,解决的是域名和 IP 地址的映射问题。

DNS解析过程

浏览器在本地维护一个hosts列表,用户访问网址时,先查看要访问的域名是否在hosts列表中,如果有就直接提取对应的 IP 地址记录,如果没有就使用 DNS 服务器获取域名和 IP 地址的映射。

DNS服务器
DNS 服务器自底向上几个层级:

  • 根域名服务器:负责管理顶级域名服务器的 IP 地址。世界上有 600多个根服务器,但只有 13 个 IP 地址。为了提高 DNS 的可靠性、安全性和性能,每个IP地址对应多个服务器。
  • 顶级域 DNS 服务器(TLD 服务器):负责管理各个顶级域名服务器的 IP 地址。如 .com.cn.org.net 等。
  • 权威 DNS 服务器:在因特网上具有公共可访问主机的每个组织机构必须提供公共可访问的 DNS 记录,如baidu.com 的 DNS 服务器,负责管理该域名下的所有子域名。
  • 本地 DNS 服务器:每个 ISP(互联网服务提供商)都有一个自己的本地 DNS 服务器。

DNS工作流程
主机cis.poly.edu想知道gaia.cs.umass.edu的 IP 地址。
迭代式查询
递归式查询

HTTP状态码

状态码 类别 原因短语
1XX Informational(信息性状态码) 接收的请求正在处理
2XX Success(成功状态码) 请求正常处理完毕
3XX Redirection(重定向状态码) 需要进行附加操作以完成请求
4XX Client Error(客户端错误状态码) 服务器无法处理请求
5XX Server Error(服务器错误状态码) 服务器处理请求出错
  • 200 OK:请求被成功处理。如发送查询用户数据的 HTTP 请求到服务端,服务端正确返回了用户数据。

  • 201 Created:请求被成功处理并且在服务端创建了一个新的资源。如通过 POST 请求创建一个新的用户。

  • 202 Accepted:服务端已接收到了请求,但还未处理。

  • 204 No Content:服务端已经成功处理了请求,但是没有返回任何内容。

  • 206 Partial Content:服务端成功处理了部分请求。如请求一个大文件,服务端只返回了部分内容。

  • 301 Moved Permanently:资源被永久重定向了。如网站的网址更换了。

  • 302 Found:资源被临时重定向了。如网站暂时关闭,重定向到一个临时的页面。

  • 304 Not Modified:客户端发送了一个条件式请求,服务端告诉客户端资源未被修改,可以使用缓存的资源。

  • 400 Bad Request:发送的 HTTP 请求存在问题。如请求参数不合法、请求方法错误。

  • 401 Unauthorized:未认证却请求需要认证之后才能访问的资源。

  • 403 Forbidden:直接拒绝 HTTP 请求,不处理。一般用来针对非法请求。

  • 404 Not Found:请求的资源未在服务端找到。如请求某个用户的信息,服务端并没有找到指定的用户。

  • 409 Conflict:表示请求的资源与服务端当前的状态存在冲突,请求无法被处理。

  • 500 Internal Server Error:服务端出问题了(通常是服务端出 Bug 了)。如服务端处理请求的时候突然抛出异常,但异常并未在服务端被正确处理。

  • 502 Bad Gateway:网关将请求转发到服务端,但服务端返回的却是一个错误的响应。

HTTP请求里包含什么

  1. 请求行(Request Line):这是HTTP请求的第一行,包含三个部分:

    • 请求方法(HTTP Method):常见的方法包括 GET, POST, PUT, DELETE, PATCH 等。不同的方法用于执行不同的操作。
    • 请求的资源路径(Request URI):表示客户端请求的资源的路径,通常是相对路径。例如,/index.html
    • HTTP版本:指定请求使用的HTTP协议版本,如 HTTP/1.1HTTP/2.0

    示例:

    1
    GET /index.html HTTP/1.1
  2. 请求头(Request Headers):请求头部分由键值对组成,用来传递客户端的配置信息、授权信息、以及其他元数据。这些头可以帮助服务器理解客户端的请求。常见的请求头有:

    • Host:服务器的域名或IP地址。例如:Host: www.example.com
    • User-Agent:描述客户端(通常是浏览器)的类型。例如:User-Agent: Mozilla/5.0
    • Accept:指定客户端可以接受的MIME类型。例如:Accept: text/html
    • Authorization:包含授权信息(如Bearer token或Basic Auth)。例如:Authorization: Bearer <token>
    • Content-Type:指定请求体的类型(如果有请求体的话)。例如:Content-Type: application/json

    示例:

    1
    2
    3
    Host: www.example.com
    User-Agent: Mozilla/5.0
    Accept: text/html
  3. 请求体(Request Body):请求体通常在POST, PUT, PATCH等请求方法中出现,携带要传递给服务器的数据。数据可以是表单数据、JSON、XML等格式。

    示例 (POST请求的请求体):

    1
    2
    3
    4
    {
    "username": "john_doe",
    "password": "123456"
    }

HTTP/HTTPS区别

基本概念

  • HTTP协议:用来规范超文本的传输,主要用来是规范浏览器和服务器端的行为。扩展性强、速度快、跨平台支持性好。
  • HTTPS协议:HTTPS基于HTTP协议,并使用 SSL/TLS 协议用作加密和安全认证,其更安全可靠。保密性好、信任度高。

HTTP和HTTPS协议都需要三次握手建立连接、四次挥手断开连接。

HTTP和HTTPS的区别

  • HTTP是明文传输,HTTPS使用SSL进行加密加密传输,更安全可靠。
  • HTTP默认端口是80,HTTPS默认端口是443。
  • HTTP连接简单,无状态,HTTPS握手阶段比较费时,所以HTTP比HTTPS快。

HTTP通信过程
HTTP 是应用层协议,它以 TCP(传输层)作为底层协议,通信过程主要如下:

  • 服务器在 80 端口等待客户的请求。
  • 浏览器发起到服务器的 TCP 连接(创建套接字 Socket)。
  • 服务器接收来自浏览器的 TCP 连接。
  • 浏览器(HTTP 客户端)与 Web 服务器(HTTP 服务器)交换 HTTP 消息。
  • 关闭 TCP 连接。

HTTP/1.0和HTTP/1.1区别

  • 连接方式:HTTP/1.0 为短连接,HTTP/1.1 支持长连接。HTTP 协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。
  • 状态响应码:HTTP/1.1 中新加入了大量的状态码,光是错误响应状态码就新增了 24 种。比如说,100 (Continue)——在请求大资源前的预热请求,206 (Partial Content)——范围请求的标识码,409 (Conflict)——请求与当前资源的规定冲突,410 (Gone)——资源已被永久转移,而且没有任何已知的转发地址。
  • 缓存机制:在 HTTP/1.0 中主要使用 Header 里的 If-Modified-Since,Expires 来做为缓存判断的标准,HTTP/1.1 则引入了更多的缓存控制策略例如 Entity tag,If-Unmodified-Since, If-Match, If-None-Match 等更多可供选择的缓存头来控制缓存策略。
  • 带宽:HTTP/1.0 中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP/1.1 则在请求头引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
  • Host头(Host Header)处理:HTTP/1.1 引入了 Host 头字段,允许在同一 IP 地址上托管多个域名,从而支持虚拟主机的功能。而 HTTP/1.0 没有 Host 头字段,无法实现虚拟主机。

HTTP/1.1和HTTP/2.0区别

  • 多路复用(Multiplexing):HTTP/2.0 在同一连接上可以同时传输多个请求和响应(可以看作是 HTTP/1.1 中长链接的升级版本),互不干扰。HTTP/1.1 则使用串行方式,每个请求和响应都需要独立的连接,而浏览器为了控制资源会有 6-8 个 TCP 连接的限制。。这使得 HTTP/2.0 在处理多个请求时更加高效,减少了网络延迟和提高了性能。
  • 二进制帧(Binary Frames):HTTP/2.0 使用二进制帧进行数据传输,而 HTTP/1.1 则使用文本格式的报文。二进制帧更加紧凑和高效,减少了传输的数据量和带宽消耗。
  • 头部压缩(Header Compression):HTTP/1.1 支持Body压缩,Header不支持压缩。HTTP/2.0 支持对Header压缩,使用了专门为Header压缩而设计的 HPACK 算法,减少了网络开销。
  • 服务器推送(Server Push):HTTP/2.0 支持服务器推送,可以在客户端请求一个资源时,将其他相关资源一并推送给客户端,从而减少了客户端的请求次数和延迟。而 HTTP/1.1 需要客户端自己发送请求来获取相关资源。

HTTP/2.0和HTTP/3.0区别

  • HTTP/2.0:使用 TCP 作为传输协议、使用 HPACK 进行头部压缩、依赖 TLS 进行加密。

  • HTTP/3.0:使用基于 UDP 的 QUIC 协议、使用更高效的 QPACK 进行头部压缩、在 QUIC 中直接集成了 TLS。QUIC 协议具备连接迁移、拥塞控制与避免、流量控制等特性。

  • 传输协议:HTTP/2.0 是基于 TCP 协议实现的,HTTP/3.0 新增了 QUIC(Quick UDP Internet Connections) 协议来实现可靠的传输,提供与 TLS/SSL 相当的安全性,具有较低的连接和传输延迟。你可以将 QUIC 看作是 UDP 的升级版本,在其基础上新增了很多功能比如加密、重传等等。HTTP/3.0 之前名为 HTTP-over-QUIC,从这个名字中我们也可以发现,HTTP/3 最大的改造就是使用了 QUIC。

  • 连接建立:HTTP/2.0 需要经过经典的 TCP 三次握手过程(由于安全的 HTTPS 连接建立还需要 TLS 握手,共需要大约 3 个 RTT)。由于 QUIC 协议的特性(TLS 1.3,TLS 1.3 除了支持 1 个 RTT 的握手,还支持 0 个 RTT 的握手)连接建立仅需 0-RTT 或者 1-RTT。这意味着 QUIC 在最佳情况下不需要任何的额外往返时间就可以建立新连接。

  • 头部压缩:HTTP/2.0 使用 HPACK 算法进行头部压缩,而 HTTP/3.0 使用更高效的 QPACK 头压缩算法。

  • 队头阻塞:HTTP/2.0 多请求复用一个 TCP 连接,一旦发生丢包,就会阻塞住所有的 HTTP 请求。由于 QUIC 协议的特性,HTTP/3.0 在一定程度上解决了队头阻塞(Head-of-Line blocking, 简写:HOL blocking)问题,一个连接建立多个不同的数据流,这些数据流之间独立互不影响,某个数据流发生丢包了,其数据流不受影响(本质上是多路复用+轮询)。

  • 连接迁移:HTTP/3.0 支持连接迁移,因为 QUIC 使用 64 位 ID 标识连接,只要 ID 不变就不会中断,网络环境改变时(如从 Wi-Fi 切换到移动数据)也能保持连接。而 TCP 连接是由(源 IP,源端口,目的 IP,目的端口)组成,这个四元组中一旦有一项值发生改变,这个连接也就不能用了。

  • 错误恢复:HTTP/3.0 具有更好的错误恢复机制,当出现丢包、延迟等网络问题时,可以更快地进行恢复和重传。而 HTTP/2.0 则需要依赖于 TCP 的错误恢复机制。

  • 安全性:在 HTTP/2.0 中,TLS 用于加密和认证整个 HTTP 会话,包括所有的 HTTP 头部和数据负载。TLS 的工作是在 TCP 层之上,它加密的是在 TCP 连接中传输的应用层的数据,并不会对 TCP 头部以及 TLS 记录层头部进行加密,所以在传输的过程中 TCP 头部可能会被攻击者篡改来干扰通信。而 HTTP/3.0 的 QUIC 对整个数据包(包括报文头和报文体)进行了加密与认证处理,保障安全性。

SSL/TSL协议加密原理

HTTPS 协议中,SSL 通道通常使用基于密钥的加密算法,密钥长度通常是 40 比特或 128 比特。

SSL/TLS 的加密的原理是非对称加密和对称加密的配合使用。对称加密用来加密数据,并生成唯一私有密钥 k,非对称加密用来加密k。通信双方(Client、Server)只需要一次非对称加密,交换对称加密的密钥k,在之后的信息通信中,使用绝对安全的密钥k,对信息进行对称加密,即可保证传输消息的保密性。

  • 非对称加密采用两个密钥:公钥、私钥。在通信时,私钥仅由Server保存,公钥由Client所知晓。公钥用于加密数据,私钥用于解密数据。
  • 对称加密中双方共享唯一密钥 k,加解密算法已知,加密方利用密钥 k 加密,解密方利用密钥 k 解密。

公钥传输隐患
假设存在攻击者 A,其对应服务器为AServer,A发送给Client一个假包,假装是Server公钥,其实是诱饵服务器 AServer的公钥,Client收到后误以为是 Server的公钥,Client后续使用AServer公钥加密密钥k,然后在公开信道传输,那么攻击者 A可以捕获加密的包,然后用AServer的私钥解密,得到密钥k,这样攻击者 A 就可以解密Client和Server之间的通信。

数字证书
为了解决这个问题,需要使用数字证书,数字证书是由权威机构(CA,Certificate Authority)颁发的,用于证明公钥的合法性。具体流程如下:
假设有服务器 Server,CA 机构,客户端 Client。

  1. Server信任 CA,CA也知道 Server公钥,CA首先为 Server颁发证书(包含 Server公钥),采用散列技术为证书生成一个摘要,然后使用 CA私钥对摘要进行加密,生成数字签名。
  2. Server获得 CA颁发的证书和数字签名,并在 Client请求时,将证书和数字签名一并发送给 Client。
  3. Client信任 CA并知晓 CA公钥。Client在收到 Server的证书和数字签名时,使用 CA公钥解密数字签名,得到摘要,然后使用相同的散列技术为证书生成摘要。
  4. Client对比两个摘要是否一致,如果一致则证明证书(包含 Server公钥)是真实的,可以使用 Server公钥加密密钥k,然后在公开信道传输。

HTTP无状态如何保存用户状态

可以使用 Session 机制来保存用户状态。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了(一般情况下,服务器会在一定时间内保存这个 Session,过了时间限制,就会销毁这个 Session)。

服务端可以使用内存和数据库保存Session,Session 跟踪是通过在客户端的 Cookie 中存放 Session ID 来实现的。如果 Cookie被禁用可以利用 URL 重写把 Session ID 直接附加在 URL 路径的后面。

URI/URL区别

  • URI(Uniform Resource Identifier)是统一资源标志符,可以唯一标识一个资源。
  • URL(Uniform Resource Locator)是统一资源定位符,可以提供该资源的路径。它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 定位 这个资源。

URI 的作用像身份证号一样,URL 的作用更像家庭住址一样。URL 是一种具体的 URI,它不仅唯一标识资源,而且还提供了定位该资源的信息。

Session/Cookie区别

  • 存储位置不同:Cookie 存储在客户端,Session 存储在服务端。
  • 存储容量不同:单个Cookie保存的数据<=4KB,一个站点最多保存20个Cookie。Session 存储在服务端,一般没有存储容量限制,但考虑服务器性能,一般会设置 Session 的有效期和存储容量。
  • 安全性不同:Cookie 存储在客户端,容易被篡改,不安全。Session 存储在服务端,相对安全。
  • 生命周期不同:Cookie 有过期时间,可以设置长期有效的 Cookie。Session 一般保存在内存中,会话结束后会被销毁。
  • 作用范围不同:Cookie 的作用范围是整个域名,Session 的作用范围是当前会话。
  • 传输方式不同:Cookie 会随着 HTTP 请求一起发送到服务端,Session 保存在服务端,客户端只会收到 Session ID。

GET/POST区别

  • GET 用于获取或查询资源,POST 用于创建或修改资源。
  • GET 请求是幂等的,即多次重复执行不会改变资源的状态,POST 请求是不幂等的,即每次执行可能会产生不同的结果或影响资源的状态。
  • GET 请求参数会附加在 URL 后面,POST 请求参数会放在请求体(body)中。
  • GET 请求的URL长度受到浏览器和服务器的限制,POST 请求的 body大小则没有明确的限制。
  • 由于 GET 请求是幂等的,可以被缓存,而POST 请求是不幂等的,不适合被缓存。
  • GET 请求的安全性较差,参数会暴露在 URL 中,POST 请求的安全性较好,参数在请求体中,不会暴露在 URL 中。

幂等:一个操作、方法或函数被调用多次,其结果与仅调用一次相同。

WebSocket

WebSocket 是一种基于 TCP 连接的全双工通信协议,即客户端和服务器可以同时发送和接收数据。

WebSocket 协议是应用层的协议,用于弥补 HTTP 协议在持久通信能力上的不足。客户端和服务器仅需一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket 的常见应用场景:视频弹幕、实时消息推送、实时游戏对战、多用户协同编辑、社交聊天等。

WebSocket/HTTP区别

  • WebSocket 是全双工通信,HTTP 是单向通信。且 HTTP 协议只能由客户端发起,服务器只能响应请求。
  • WebSocket 是持久连接,HTTP 是短连接。HTTP 请求结束后,连接就会断开,而 WebSocket 连接会一直保持。
  • WebSocket 使用 ws://wss://作为协议前缀,HTTP 使用 http://https://作为协议前缀。
  • WebSocket 通信数据格式比较轻量,用于协议控制的数据包头部相对较小,而 HTTP 通信每次都要携带完整的头部,网络开销较大。
  • WebSocket 支持扩展,可以自定义协议,HTTP 不支持扩展。

WebSocket工作流程

  1. 客户端向服务器发起一个 HTTP 请求,请求头中包含 Upgrade: websocketSec-WebSocket-Key等字段,表示要求升级协议为 WebSocket。
  2. 服务器收到请求后,会进行协议升级,如果支持 WebSocket 协议,将回复HTTP 101状态码,响应头中包含Upgrade: websocketSec-WebSocket-Accept:xxx等字段,表示升级成功。
  3. 现在已经建立了 WebSocket 连接,可以进行双向的数据传输。连接建立之后,通过心跳机制保持连接的稳定性和活跃性。数据以帧(frames)的形式传输,WebSocket的发送端将每条消息被切分成多个帧发送,接收端将关联的帧重新组装成完整的消息。
  4. 关闭连接时,双方都可以发送一个关闭帧,表示关闭连接。另一方收到后,会回复一个关闭帧,然后关闭连接。

PING命令原理

PING 命令是一种常用的网络诊断工具,经常用来测试网络中主机之间的连通性和网络延迟。

1
2
3
4
5
6
7
8
9
10
ping -c 4 www.baidu.com
PING www.baidu.com(36.155.132.3): 56 data bytes
64 bytes from 36.155.132.3: icmp_seq=0 ttl=49 time=8.890 ms
64 bytes from 36.155.132.3: icmp_seq=1 ttl=49 time=8.874 ms
64 bytes from 36.155.132.3: icmp_seq=2 ttl=49 time=9.136 ms
64 bytes from 36.155.132.3: icmp_seq=3 ttl=49 time=9.400 ms

--- www.baidu.com ping statistics ---
4 packets transmitted, 4 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 8.874/9.075/9.400/0.214 ms

输出由以下几部分组成:

  • ICMP Echo Request(请求报文)信息:序列号、TTL(Time to Live)值。
  • 目标主机的域名或 IP 地址:输出结果的第一行。
  • 往返时间(RTT,Round-Trip Time):从发送 ICMP Echo Request(请求报文)到接收到 ICMP Echo Reply(响应报文)的总时间,用来衡量网络连接的延迟。
  • 统计结果(Statistics):包括发送的 ICMP 请求数据包数量、接收到的 ICMP 响应数据包数量、丢包率、往返时间(RTT)的最小、平均、最大和标准偏差值。

如果PING目标主机无法正确响应,则说明网络连通性出现问题。

PING命令的原理是基于 ICMP 协议,通过发送 ICMP Echo Request(请求报文)到目标主机,目标主机收到请求后,会返回 ICMP Echo Reply(响应报文)给发送方,从而实现网络连通性的测试。

ping底层过程

  1. 发送 ICMP Echo Request
    • 当你在命令行中执行 ping 命令时,系统会构造一个 ICMP Echo Request 数据包。这是一个特别类型的 ICMP 消息,源自发送方并目标是接收方。
    • 数据包的格式包括:类型(Type)、代码(Code)、校验和(Checksum)、标识符(Identifier)、序列号(Sequence Number)等字段。
    • Type:Echo Request 的 Type 是 8。 echo Reply 的 Type 是 0。
    • Code:对于 Echo Request 和 Echo Reply,Code 都是 0。
    • Identifier (标识符):用于标识请求和响应的配对。
    • Sequence Number (序列号):每个请求包的序列号,用于区分每个请求。
  2. 网络传输
    • ping 会把 ICMP Echo Request 数据包通过 IP 协议发送到目标主机。
    • 数据包经过路由器、交换机等网络设备,根据目标地址传输到目的地。
  3. 目标主机接收并处理
    • 目标主机接收到 ICMP Echo Request 后,会根据 ICMP 协议自动回复一个 ICMP Echo Reply 消息。
    • Echo Reply 包含目标主机的标识符、序列号等信息,通常原封不动地返回给源主机。
  4. 接收 ICMP Echo Reply:发送方接收到 Echo Reply 后,ping 会计算从发送到接收的往返时间(RTT,Round Trip Time)。这个时间也被称为延迟,单位通常是毫秒(ms)。
  5. 显示结果
    • ping 会在命令行界面上显示每个数据包的往返时间以及丢包率等信息。
    • 默认情况下,ping 会发送多个包并显示每个包的 RTT,直到用户中断命令(通常按 Ctrl+C)或达到预设的发送次数。

HTTP/TCP区别

  • TCP 是一种底层的传输协议,提供可靠的数据传输。
  • HTTP 是一种应用层协议,使用 TCP 作为其传输层协议,专门用于 web 数据的传输。
  1. 层次:TCP 位于传输层,HTTP 位于应用层。
  2. 功能:TCP 提供可靠的、面向连接的通信。HTTP 用于在客户端和服务器之间传输超文本数据(如 HTML 文档、图片、视频等)。
  3. 连接方式:TCP 通过三次握手建立连接,确保通信双方准备就绪。HTTP 基于请求-响应模型,客户端发送请求,服务器返回响应。
  4. 数据传输:TCP 是流式传输数据,没有消息边界。HTTP 是面向消息,每个请求和响应都是独立的。
  5. 错误检测和恢复:TCP 具有错误检测和恢复机制,保证数据完整性。HTTP 没有错误检测和恢复机制,需要依赖 TCP 来保证数据传输的可靠性。且 HTTP 无状态,依赖于 Cookie 和 Session 来保存用户状态。
  6. 用途:TCP 适用于需要高可靠性的数据传输,如文件传输、电子邮件等。HTTP 主要用于万维网(WWW)上的数据通信,如浏览网页、提交表单等。

HTTP 是建立在 TCP 之上的,它利用 TCP 提供的可靠连接来传输数据,但它们在网络模型中的层次和具体功能上有所不同。

TCP、UDP区别

二者都是常见的传输层协议。

  • TCP(Transmission Control Protocol,传输控制协议 ):提供 面向连接 的,可靠 的数据传输服务。
  • UDP(User Datagram Protocol,用户数据协议):提供 无连接 的,尽最大努力 的数据传输服务(不保证数据传输的可靠性),简单高效。

区别总结:

  1. TCP面向连接(三次握手四次挥手),UDP是无连接的,即发送数据之前不需要建立连接
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
  3. TCP面向字节流,TCP把数据看成一连串无结构的字节流,UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如实时视频会议)
  4. TCP只支持点对点通信,UDP支持一对一,一对多,多对一和多对多的交互通信
  5. TCP首部开销20字节,UDP的首部开销小,只有8个字节
  6. TCP通信信道是全双工的可靠信道,UDP则是不可靠信道

原文链接:https://blog.csdn.net/Li_Ning_/article/details/52117463

TCP/UDP应用场景

https://blog.csdn.net/qq_44273429/article/details/131375961

由于TCP提供了可靠的、有序的数据传输,它适用于以下场景:

  • 文件传输:TCP的可靠性和有序性确保了文件在传输过程中不会丢失和损坏,并按正确的顺序接收。
  • 网页浏览:TCP可靠地传输网页内容,确保网页的准确显示。
  • 电子邮件传输:TCP保证电子邮件的传输不会出现丢失或乱序的情况。
  • 远程登录:TCP提供了稳定的连接,适合远程登录操作。
  • 数据库管理系统:TCP确保了数据库的一致性和完整性,防止数据丢失和损坏。

由于UDP具有低延迟和高效性的特点,它适用于以下场景:

  • 视频流和音频流传输:由于UDP的低延迟,它常用于视频流和音频流的实时传输,如在线直播、视频会议等。
  • 实时游戏:UDP的快速传输和低延迟使其成为在线游戏中常用的协议,可以实现实时的游戏数据传输。
  • DNS(域名系统):UDP广泛用于域名系统中,用于域名解析和查询。
  • 实时传感器数据:UDP适用于需要快速传输实时传感器数据的场景,如工业自动化、物联网等

UDP的广播和多播

  • UDP广播是指将数据包发送到同一子网内的所有设备。广播消息使用了一个特殊的IP地址,该地址的子网内主机标志部分的二进制全部为1(即点分十进制IP的最后一部分是255)。例如,在子网192.168.1.x中,广播地址是192.168.1.255。
    • 特点:
      • 广播消息可以被子网内的所有设备接收,无需设备事先注册或加入特定的组。
      • 广播只能在局域网内使用,广域网中无法使用UDP进行广播。
      • 广播的开销较小,因为发送者只需发送一个数据包,而该数据包会被子网内的所有设备接收。
    • 应用场景:
      • 局域网内的通知或广播消息,如DHCP(动态主机配置协议)服务器向局域网内的设备发送IP地址分配信息。
      • 局域网内的设备发现或同步操作,如打印机共享或文件同步服务。
  • UDP多播(也称为组播)是一种将数据包发送到一组特定接收者的网络通信模式。多播使用特定的多播地址(在IPv4中为224.0.0.0到239.255.255.255范围内的地址),该地址标识了一组接收数据的接口。
    • 特点:
      • 多播比广播具有更高的可控性,只有加入多播组的接收者才能接收数据。
      • 多播不仅限于局域网,也可以用于广域网环境,适用于在大型网络中分发数据。
      • 多播地址由IANA(互联网号码分配机构)进行全球分配和管理。
    • 应用场景:
      • 视频直播:将视频数据同时分发给大量观众。
      • 文件分发:向大量客户端分发相同文件。
      • 在线游戏:实现游戏房间的快速组建和对手匹配。
      • 监控系统:将视频数据同时分发给多个监控终端。
      • 分布式计算:将计算任务分发给多个节点。

多播工作工程:

  1. 发送者创建一个UDP套接字,并设置多播属性。
  2. 发送者使用sendto()函数向多播地址发送数据。
  3. 网络设备(如路由器)根据多播路由协议(如IGMP、PIM等)将数据包转发到需要接收该数据的各个网段。
  4. 接收者通过加入多播组来接收数据,使用setsockopt()函数设置套接字加入多播组。

如何将UDP变为将TCP那样可靠

要使 UDP 像 TCP 那样可靠,需要在应用层实现类似 TCP 的功能。以下是一些常见的方法和步骤:

  1. 数据包确认机制(ACK):每当接收方收到一个数据包时,它会发送一个确认(ACK)回给发送方。发送方在发送数据包后会等待 ACK,如果在一定时间内没有收到 ACK,则会重传该数据包。
  2. 序列号:在每个数据包中添加一个序列号,以便接收方可以按顺序重组数据包,并检测丢失或重复的数据包。
  3. 超时和重传:发送方在发送每个数据包后启动一个定时器。如果在规定时间内没有收到 ACK,则会重传该数据包。
  4. 滑动窗口:使用滑动窗口协议来控制数据包的流动。发送方可以在等待 ACK 的同时继续发送多个数据包,从而提高传输效率。
  5. 校验和:在数据包中包含校验和,以检测数据包在传输过程中是否被损坏。接收方会检查校验和,并丢弃任何损坏的数据包。
  6. 流量控制:发送方和接收方协商一个窗口大小,以确保发送方不会超过接收方的处理能力。

三次握手四次挥手✅

建立 TCP 连接需要“三次握手”,缺一不可:

  • 一次握手:客户端发送带有 SYN(SEQ=x)标志的数据包 -> 服务端,然后客户端进入 SYN_SEND 状态,等待服务端的确认;
  • 二次握手:服务端发送带有 SYN+ACK(SEQ=y,ACK=x+1)标志的数据包 –> 客户端,然后服务端进入 SYN_RECV 状态;
  • 三次握手:客户端发送带有 ACK(ACK=y+1)标志的数据包 –> 服务端,然后客户端和服务端都进入ESTABLISHED状态,完成 TCP 三次握手。

注意,连接建立后,客户端和服务端都可以发送数据。

断开 TCP 连接则需要“四次挥手”,缺一不可:

  • 第一次挥手:客户端发送一个 FIN(SEQ=x)标志的数据包 -> 服务端,用来关闭客户端到服务端的数据传送。然后客户端进入 FIN-WAIT-1 状态。
  • 第二次挥手:服务端收到FIN标志的数据包,它发送一个 ACK(ACK=x+1)标志的数据包 -> 客户端。然后服务端进入 CLOSE-WAIT 状态,客户端进入 FIN-WAIT-2状态。
  • 第三次挥手:服务端发送一个 FIN(SEQ=y)标志的数据包 -> 客户端,请求关闭连接,然后服务端进入 LAST-ACK 状态。
  • 第四次挥手:客户端发送 ACK(ACK=y+1)标志的数据包 -> 服务端,然后客户端进入TIME-WAIT状态,服务端在收到 ACK(ACK=y+1)标志的数据包后进入 CLOSE 状态。此时如果客户端等待 2MSL 后依然没有收到回复,就证明服务端已正常关闭,随后客户端也可以关闭连接了。

TCP 是全双工通信,可以双向传输数据。任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了 TCP 连接。

只要四次挥手没有结束,客户端和服务端就可以继续传输数据!

2MSL, Maximum Segment Lifetime,即最长报文段寿命的两倍。

三次握手

四次挥手

TIME_WAIT/CLOSE_WAIT是什么

TIME_WAIT/CLOSE_WAIT是TCP关闭连接过程中出现的两种状态。

  • CLOSE_WAIT是被动关闭连接的一方(通常是服务器)出现的一种状态,当TCP连接的一端(被动关闭方)收到对方发送的FIN报文时,会发送一个ACK报文作为响应,并进入CLOSE_WAIT状态。
  • TIME_WAIT是主动关闭连接的一方(通常是客户端)出现的一种状态,当主动关闭连接的一方收到对方发送的FIN报文并发送自己的ACK报文后,就会进入TIME_WAIT状态。这个状态会持续2MSL(两倍的报文最大生存时间)。

半连接队列/全连接队列

在三次握手中,Linux 内核会维护两个队列管理连接请求:

  • 半连接队列(也称 SYN Queue):当服务端收到客户端的 SYN 请求时,此时双方还没有完全建立连接,服务端会把半连接状态的连接放在半连接队列。
  • 全连接队列(也称 Accept Queue):当服务端收到客户端的 ACK 请求时,意味着三次握手成功完成,服务端会将该连接从半连接队列移动到全连接队列。若未收到 ACK 请求,会进行重传,若超过最大重传次数,系统将从半连接队列中删除该连接信息。

两队列的存在是为了处理并发请求,确保服务端能够有效地管理新的连接请求。

为什么要三次握手

三次握手为的是确认双方收发功能都正常,缺一不可。

  • 第一次握手:Client 无法确认任何信息;Server 确认了:对方发送正常,自己接收正常
  • 第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:对方发送正常,自己接收正常
  • 第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常

第二次握手为何传回ACK和SYN

为什么传回ACK
第二次握手中,服务端传回 ACK 是为了确认接收到客户端的 SYN 报文。
为什么传回SYN
在第一次握手中,客户端发送 SYN包 是为了建立客户端到服务端的连接,然而 TCP 连接是双向的,服务端到客户端也需要建立连接,所以服务端在第二次握手中向客户端发送 SYN包,以建立服务端到客户端的连接。

同时发送 SYN 包和 ACK包,服务器能够在一个报文中同时完成这两个操作,从而减少报文的数量,加快连接建立的效率。

三次握手中可以携带数据吗

允许在第三次发送 ACK 的时候携带数据,但是不建议这样做,因为在第三次握手时,服务端还没有确认客户端的 ACK 包,可能会导致数据丢失。

为什么要四次挥手

  • 确保数据传输的完整性和可靠性:在任何一方关闭连接之前,确保所有数据都已正确接收和处理。
  • 保证双向关闭的确认:TCP 是全双工通信,每一方都需要确认对方的关闭请求,以避免数据丢失和连接的不正常终止。
  • 避免半开连接:确保在关闭过程中,没有未完成的数据传输或者遗留的数据包。
  • 保证连接的有序终止:TCP 是全双工的,每一方都需要单独关闭自己的发送和接收通道,因此需要四步来完成这一过程。

四次挥手虽然复杂,但却能够确保连接的稳定、可靠和有序终止。

为何不能把服务端发送的ACK和FIN合并变成三次挥手

因为服务端收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务端到客户端的数据传送。

如果第二次挥手客户端未收到服务端发送的ACK会怎样

客户端没有收到 ACK 确认,会重新发送 FIN 请求。

为什么第四次握手客户端需等待2*MSL

第四次挥手时,客户端发送服务端的 ACK 有可能丢失,如果服务端没收到 ACK 的话,服务端就会重发 FIN,如果客户端在 2*MSL 的时间内收到了 FIN,就会重新发送 ACK 并再次等待 2MSL,防止 Server 没有收到 ACK 而不断重发 FIN

MSL(Maximum Segment Lifetime): 一个片段在网络中最大的存活时间,2MSL 就是一个发送和一个回复所需的最大时间。如果直到 2MSL,Client 都没有再次收到 FIN,那么 Client 推断 ACK 已经被成功接收,则结束 TCP 连接。

TCP如何保证连接可靠性

  1. 基于数据块传输:数据被分割成 TCP 认为最适合发送的数据块,再传输给网络层,数据块被称为报文段或段。
  2. 对失序数据包重新排序以及去重:TCP 为了保证不发生丢包,给每个包一个序列号,这使得能够将接收到的数据根据序列号排序,去掉重复序列号的数据就可以实现数据包去重。
  3. 校验和:TCP 有端到端的校验和机制,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP 将丢弃此报文段和不确认收到此报文段。
  4. 重传机制:在数据包丢失或延迟的情况下,重新发送数据包,直到收到对方的确认应答(ACK)。
    • 超时重传:当发送方发送数据后,如果超时重传计时器超时,仍没有收到接收方的确认应答,就会重传数据包。
    • 快速重传:当发送方连续收到三个重复的 ACK 时,立即重传相应的数据包,而不必等待超时。
  5. TCP 连接每一方都有固定大小的缓冲空间,TCP 的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 利用滑动窗口实现流量控制。
  6. 拥塞控制:当网络拥塞时,减少数据的发送。TCP 在发送数据的时候会考虑网络的拥塞程度,由拥塞窗口表示,它是发送方根据网络状况自己维护的一个值,表示发送方认为可以在网络中传输的数据量。发送方发送数据的大小是滑动窗口和拥塞窗口的最小值,这样可以保证发送方既不会超过接收方的接收能力,也不会造成网络的过度拥塞。

如何实现快速重传

快速重传(Fast Retransmit):当发送方连续收到三个重复的ACK时,立即重传相应的数据包,而不必等待超时。

  • 快速重传就是要发送方尽快重传,而不是等待超时重传计时器超时再重传。
    • 要求接收方不要等待自己发送数据时捎带ACK,而是立即发送ACK
    • 即使收到了失序的报文段,也要立即发送对自己收到的报文段的重复确认ACK
    • 发送方一旦收到三个连续的ACK,就立即重传对应的报文段,而不必等待超时重传计时器超时。
    • 使用快速重传可以将网络吞吐量提高 20%~30%。

TCP如何实现流量控制

TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。 接收方发送的确认报文中的窗口字段用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。

为什么需要流量控制

因为双方在通信的时候,发送方的速率与接收方的速率是不一定相等,如果发送方的发送速率太快,会导致接收方处理不过来。如果处理不过来,会先将数据放入接收缓冲区(Receiving Buffers)里。如果接收缓冲区满了,就只能把再收到的数据包丢掉。而丢包会浪费网络资源。因此要让接收方与发送方处于一种动态平衡。

为什么要进行拥塞控制

拥塞控制是为了避免网络拥塞,确保网络的稳定性和可靠性。当网络中的数据包过多,超过网络的处理能力时,就会发生拥塞,导致数据包丢失、延迟增加、吞吐量下降等问题。拥塞控制的目的是通过限制发送方的发送速率,避免过多的数据包注入到网络中,从而保持网络的正常运行。

TCP拥塞控制的四种算法

https://blog.csdn.net/love_668/article/details/116913790

  • 慢开始(Slow Start):初始阶段发送方以指数增长的方式增加拥塞窗口(Congestion Window, cwnd),逐渐探测网络的可用带宽。
  • 拥塞避免(Congestion Avoidance):当cwnd达到慢启动阈值(Slow Start Threshold, ssthresh)时,cwnd以线性增长方式增加。
    • 发送方维护一个拥塞窗口(cwnd),用于控制发送数据的速率,其值取决于网络的拥塞程度,并且动态变化。
      • cwnd的维护原则:只要网络没有拥塞,就增大cwnd,以提高发送速率;一旦网络出现拥塞,就减小cwnd
      • 判断拥塞依据:没有按时收到应当到达的报文(即发生超时重传)。
    • 发送方将拥塞窗口作为发送窗口swnd,即swnd=cwnd
    • 维护一个慢开始门限ssthresh状态变量:
      • cwnd < ssthresh时,使用慢开始算法;
      • cwnd > ssthresh时,停止使用慢开始算法而改用拥塞避免算法;
      • cwnd = ssthresh时,既可以使用慢开始算法,也可以使用拥塞避免算法;
    • 在TCP双方建立逻辑连接关系时,cwnd的值被设置为1,慢开始门限ssthresh值进行设置(假设为 16)。
    1. 慢开始阶段:cwnd从1开始,每收到一个ACKcwnd加倍,即cwnd = cwnd * 2,直到cwnd >= ssthresh
    2. cwnd >= ssthresh,开始执行拥塞避免算法,cwnd每次只增加1,即cwnd = cwnd + 1
    3. 如果此时部分数据报文段丢失,那么发送方会对这些丢失的数据报文段进行超时重传。发送方以此判断可能发生了网络拥塞,进行调整:将慢开始门限值ssthresh值更新为发生拥塞时cwnd值的一半;将cwnd的值减少为1,并重新开始执行慢开始算法。
    4. cwnd重新达到新的ssthresh时,再次执行拥塞避免算法。
  • 快速重传(Fast Retransmit):当发送方连续收到三个重复的ACK时,立即重传相应的数据包,而不必等待超时。
  • 快速恢复(Fast Recovery):在快速重传后,cwnd减半,但不重新进入慢启动,而是进入快速恢复阶段,通过继续发送数据来尽快恢复网络的正常传输。
    • 有时候个别报文丢失,但实际上并没有网络拥塞。这导致发送发超时重传,误认为发生了网络拥塞,错误地使用慢开始算法,降低了网络的传输效率。为了解决这个问题,TCP引入了快速重传机制、快速恢复机制。
    • 快速重传就是要发送方尽快重传,而不是等待超时重传计时器超时再重传。
      • 要求接收方不要等待自己发送数据时捎带ACK,而是立即发送ACK
      • 即使收到了失序的报文段,也要立即发送对自己收到的报文段的重复确认ACK
      • 发送方一旦收到三个连续的ACK,就立即重传对应的报文段,而不必等待超时重传计时器超时。
      • 使用快速重传可以将网络吞吐量提高 20%~30%。
    • 发送方收到三个重复的ACK,就知道只是丢失了个别报文,所以不使用慢开始算法,而是使用快恢复算法。
      • 发送方将慢开始门限ssthresh和拥塞窗口cwnd设置为原来cwnd的一半,然后执行拥塞避免算法。
      • 有的快恢复算法实现是将新的拥塞窗口设置的大一点,即cwnd = ssthresh(cwnd/2) + 3,然后执行拥塞避免算法。

TCP拥塞控制

注意:
慢开始是指一开始向网络中注入的报文少,并不是指cwnd的增长速度慢;
拥塞避免并非不能完全避免拥塞,而是在网络出现拥塞时使用线性增长方式控制cwnd,使网络不容易出现拥塞。

TCP如何实现拥塞控制

TCP 发送发维持一个 拥塞窗口(cwnd)的状态变量。采用了四种算法,即 慢开始拥塞避免快重传快恢复

  • 发送方维护一个拥塞窗口(cwnd),用于控制发送数据的速率,其值取决于网络的拥塞程度,并且动态变化。
    • cwnd的维护原则:只要网络没有拥塞,就增大cwnd,以提高发送速率;一旦网络出现拥塞,就减小cwnd
    • 判断拥塞依据:没有按时收到应当到达的报文(即发生超时重传)。
  • 发送方将拥塞窗口作为发送窗口swnd,即swnd=cwnd
  • 维护一个慢开始门限ssthresh状态变量:
    • cwnd < ssthresh时,使用慢开始算法;
    • cwnd > ssthresh时,停止使用慢开始算法而改用拥塞避免算法;
    • cwnd = ssthresh时,既可以使用慢开始算法,也可以使用拥塞避免算法;
  • 在TCP双方建立逻辑连接关系时,cwnd的值被设置为1,慢开始门限ssthresh值进行设置(假设为 16)。
  1. 慢开始阶段:cwnd从1开始,每收到一个ACKcwnd加倍,即cwnd = cwnd * 2,直到cwnd >= ssthresh
  2. cwnd >= ssthresh,开始执行拥塞避免算法,cwnd每次只增加1,即cwnd = cwnd + 1
  3. 如果此时部分数据报文段丢失,那么发送方会对这些丢失的数据报文段进行超时重传。发送方以此判断可能发生了网络拥塞,进行调整:将慢开始门限值ssthresh值更新为发生拥塞时cwnd值的一半;将cwnd的值减少为1,并重新开始执行慢开始算法。
  4. cwnd重新达到新的ssthresh时,再次执行拥塞避免算法。
  • 有时候个别报文丢失,但实际上并没有网络拥塞。这导致发送发超时重传,误认为发生了网络拥塞,错误地使用慢开始算法,降低了网络的传输效率。为了解决这个问题,TCP引入了快速重传机制、快速恢复机制。
  • 快速重传就是要发送方尽快重传,而不是等待超时重传计时器超时再重传。
    • 要求接收方不要等待自己发送数据时捎带ACK,而是立即发送ACK
    • 即使收到了失序的报文段,也要立即发送对自己收到的报文段的重复确认ACK
    • 发送方一旦收到三个连续的ACK,就立即重传对应的报文段,而不必等待超时重传计时器超时。
    • 使用快速重传可以将网络吞吐量提高 20%~30%。
  • 发送方收到三个重复的ACK,就知道只是丢失了个别报文,所以不使用慢开始算法,而是使用快恢复算法。
    • 发送方将慢开始门限ssthresh和拥塞窗口cwnd设置为原来cwnd的一半,然后执行拥塞避免算法。
    • 有的快恢复算法实现是将新的拥塞窗口设置的大一点,即cwnd = ssthresh(cwnd/2) + 3,然后执行拥塞避免算法。

TCP解决丢包问题

  • 重传机制
    • 超时重传(Retransmission TimeOut, RTO):当发送方没有在预期时间内收到确认应答(ACK)时,认为数据包丢失,重新发送该数据包。
    • 快速重传(Fast Retransmit):当发送方连续收到三个重复的ACK时,立即重传相应的数据包,而不必等待超时。
  • 拥塞控制
    • 慢启动(Slow Start):初始阶段发送方以指数增长的方式增加拥塞窗口(Congestion Window, CWND),逐渐探测网络的可用带宽。
    • 拥塞避免(Congestion Avoidance):当CWND达到慢启动阈值(Slow Start Threshold, SSTHRESH)时,CWND以线性增长方式增加。
    • 快速恢复(Fast Recovery):在快速重传后,CWND减半,但不重新进入慢启动,而是进入快速恢复阶段,通过继续发送数据来尽快恢复网络的正常传输。
    • 快速重传(Fast Retransmit):当发送方连续收到三个重复的ACK时,立即重传相应的数据包,而不必等待超时。
  • 选择性确认(Selective Acknowledgement, SACK):允许接收方通知发送方已接收的非连续数据块,发送方可以只重传丢失的部分数据,而不是所有后续数据包。
  • 冗余传输(Redundant Transmission):使用前向纠错码(Forward Error Correction, FEC)或者额外的冗余数据,以便在接收方能够自行修复部分丢失的数据包。
  • 流量控制(Flow Control):通过调整接收窗口(Receiver Window, RWND)的大小,确保发送方不会发送超过接收方处理能力的数据量,减少因拥塞而导致的丢包。
  • 网络层优化
    • 质量服务(Quality of Service, QoS):在路由器和交换机上设置优先级规则,确保关键数据包优先传输。
    • 负载均衡(Load Balancing):将流量分布到多个路径或链路上,避免某一路径过载导致丢包。
  • 物理层优化:改善物理连接质量,如使用更好的网络电缆、优化无线信号强度等,以减少物理层的丢包率。
  • 网络协议的改进:Quick UDP Internet Connections(QUIC),谷歌开发的一种基于UDP的协议,具有更快速的连接建立和恢复能力。

通过结合以上各种方法,可以更有效地应对TCP丢包问题,提高网络传输的稳定性和效率。

TCP粘包拆包问题

https://blog.csdn.net/u010429831/article/details/119932832
TCP 是一个面向字节流的传输层协议。发送方无法保证对方每次收到的都是一个完整的数据包。于是就有了粘包、拆包问题的出现。粘包、拆包问题只发生在TCP协议中。

  • 接收端只收到一个TCP报文段,去掉首部后,报文段中包含发送端发送来的两个数据包的信息,即为粘包。由于接收端不知道两数据包的界限,所以对于接收端来说很难处理。
  • 接收端收到了两个TCP报文段,但是去掉首部后,两个数据包要么是不完整的,要么就是多出来一部分,这种现象即为粘包、拆包问题。
    • 粘包问题分两种:一种是粘在一起的包都是完整的数据包,另一种情况是粘在一起的包有不完整的数据包。
    • 拆包:应用程序的数据包被拆分成若干部分发送出去,从接收缓存看,接收方收到的只是数据包的一部分内容。

粘包产生原因

  • 应用程序(发送方)进程写入的数据量大于TCP发送缓冲区的大小,这将会发生拆包。此时数据包需要进行拆包处理,分多次进行发送。
  • 应用进程写入的数据量小于TCP发送缓冲区的大小,这将会发生粘包。由于TCP发送缓存空间足够,它会等到有多个数据包时,再组装成一个TCP报文段,然后通过网卡发送到网络中去。
  • 当应用进程发送的数据包大于 MSS(最大报文段长度: TCP报文段的长度 - TCP首部长度)时,将会发生拆包。TCP 协议会将大的数据包拆分成 MSS 大小的数据包进行发送。
  • 接收方不及时读取接收缓冲区中的数据,将会发生粘包。接收方先把接收到的数据存放在内核接收缓冲区中,用户进程从接收缓冲区读取数据,若下一个数据包到达时前一个数据包尚未被用户进程取走,则下一个数据包放到内核接收缓冲区时就和前一数据包粘在一起,而用户进程根据预先设定的缓冲区大小从内核接收缓冲区读取数据,这样就一次性读取到了多个数据包。

什么时候需要考虑粘包问题
TCP是长连接,并且传输的是结构化数据时,如:传送的是一个结构体类型的数据,由于不知道结构化数据的边界,容易导致粘包问题的出现。这时需要考虑粘包问题的影响。

什么时候不需要考虑粘包问题

  • 如果 TCP 是短连接,即只进行一次数据通信过程,通信完成就关闭连接,这样就不会出现粘包问题。
  • 如果传输的是字符串、文件等无结构化数据时,也不会出现粘包问题。因为发送方只管发送,接收方只管接收存储就行了

粘包问题解决方案

  1. 发送定长包。即发送端将每个数据包封装为固定长度(长度不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。(适合定长结构的数据)
  2. 包头加上包体长度。发送端给每个数据包添加包首部,首部中应该至少包含数据包的长度,这样接收端在接收到数据后,通过读取包首部的长度字段,便可以知道每一个数据包的实际长度了。(适合不定长结构的数据)
  3. 在包尾部设置边界标记。发送端在每个数据包尾部添加边界标记,可以使用特殊符号作为边界标记。如此,接收端通过这个边界标记就可以将不同的数据包拆分开来。但这可能会存在一个问题:如果数据包内容中也包含有边界标记,则会被误判为消息的边界,导致出错。这样方法要视具体情况而定。例如,FTP协议就是采用 “\r\n” 来识别一个消息的边界的。

ARQ协议

自动重传请求(Automatic Repeat-reQuest,ARQ)是数据链路层和传输层的错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认信息ACK,就会重新发送,直到收到确认或者重试超过一定的次数。

停止等待ARQ协议

停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认(回复 ACK)。如果过了一段时间(超时时间后),还是没有收到 ACK 确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个分组;

连续ARQ协议

连续 ARQ 协议可提高信道利用率。发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。

  • 优点:信道利用率高,容易实现。
  • 缺点:不能向发送方反映出接收方已经正确收到的所有分组的信息。如:发送方发送了5条消息,中间第三条丢失(3号),这时接收方只能对前两个发送确认。发送方无法知道后三个分组的下落,而只好把后三个全部重传一次。这也叫 Go-Back-N(回退 N),表示需要退回来重传已经发送过的 N 个消息。

超时重传机制如何实现

发送方发送完数据后,会启动一个定时器,等待接收端确认收到这个报文段。接收端成功收到报文段的话会发送会 ACK包,如果发送端在合理的往返时延(RTT)内未收到确认信息,那数据包就被标记为已丢失并进行重传。

超时重传时间 RTO(Retransmission Time Out)直接影响到 TCP 的性能和效率。如果 RTO 设置得太小,会导致不必要的重传,增加网络负担;如果 RTO 设置得太大,会导致数据传输的延迟,降低吞吐量。因此 RTO 应根据网络的实际状况,动态地进行调整。

超时重传时间不能直接使用往返时延RTT,因为其值会随着网络波动而变化。

IP地址

IP地址是每个连入互联网的设备或域(如计算机、服务器、路由器等)都被分配的一个唯一标识符。

IP地址过滤

IP 地址过滤(IP Address Filtering)就是限制或阻止特定 IP 地址或 IP 地址范围的访问。如:有一个图片服务突然被某一个 IP 地址攻击,那就可以禁止这个 IP 地址访问图片服务。

IPv4/IPv6区别

IPv4(Internet Protocol version 4)是四组由点分隔的数字,例如:123.89.46.72。IPv4 使用 4Bytes 32 位(bits)地址作为其 Internet 地址,共有约 42 亿(2^32)个可用 IP 地址。不够用。

为了解决 IP 地址耗尽的问题,采用具有更大地址空间的新版本 IP 协议 - IPv6(Internet Protocol version 6)。该格式使用由单或双冒号分隔的一组数字和字母,如:2001:0db8:85a3:0000:0000:8a2e:0370:7334。IPv6 使用 128 位互联网地址,有 2^128个可用 IP 地址。

  • IPv4采用DHCP(动态主机配置协议)来自动分配IP地址。IPv6支持自动配置(SLAAC)和DHCPv6,自动化程度更高。
  • IPv4路由表较大,互联网的快速增长导致路由表膨胀,路由效率受到一定影响。IPv6有更高效的路由聚合(aggregation),路由表规模更小,有助于提高路由效率。

判断两IP同属一个子网

  1. 获取IP地址和子网掩码:每个IP地址(IPv4)都有一个对应的子网掩码。子网掩码用于确定一个IP地址中哪些位是网络部分,哪些位是主机部分。
  2. 按位与操作(AND操作):将IP地址和子网掩码进行逐位的“与操作”(AND),得到每个IP地址的网络号。
    与操作规则:
    • 1 AND 1 = 1
    • 1 AND 0 = 0
    • 0 AND 0 = 0
  3. 比较网络号:如果两个IP地址通过子网掩码计算得到的网络号相同,那么这两个IP地址就属于同一个子网。

示例

  • IP地址1:192.168.1.10

  • 子网掩码:255.255.255.0

  • IP地址2:192.168.1.20

  • 子网掩码:255.255.255.0

  • IP地址1:192.168.1.10 -> 11000000.10101000.00000001.00001010

  • 子网掩码:255.255.255.0 -> 11111111.11111111.11111111.00000000
    结果:网络号1:11000000.10101000.00000001.00000000 -> 192.168.1.0

  • IP地址2:192.168.1.20 -> 11000000.10101000.00000001.00010100

  • 子网掩码:255.255.255.0 -> 11111111.11111111.11111111.00000000
    结果:网络号2:11000000.10101000.00000001.00000000 -> 192.168.1.0

结论:IP1和IP2的网络号相同,这两个IP地址属于同一个子网。

子网掩码

子网掩码(Subnet Mask)是与IP地址配合使用的一种技术。它用于标识IP地址中的网络部分和主机部分,从而确定一个设备属于哪个网络。子网掩码通常采用与IP地址相同的点分十进制形式表示。

子网掩码的作用

  1. 区分网络与主机:通过子网掩码可以确定一个IP地址中哪些位代表网络地址,哪些位代表主机地址。这有助于路由器正确地将数据包转发到目的网络。
  2. 网络划分:通过设置不同的子网掩码,可以将一个大的网络划分为多个较小的子网,这样可以有效管理和控制网络流量,提高网络性能和安全性。

常见的子网掩码

  • A类地址:默认子网掩码为255.0.0.0。A类地址的第一个字节表示网络部分,后三个字节表示主机部分。
  • B类地址:默认子网掩码为255.255.0.0。B类地址的前两个字节表示网络部分,后两个字节表示主机部分。
  • C类地址:默认子网掩码为255.255.255.0。C类地址的前三个字节表示网络部分,最后一个字节表示主机部分。

子网掩码的计算
子网掩码由32位组成,其中网络部分用1表示,主机部分用0表示。例如,255.255.255.0 的二进制表示为 11111111.11111111.11111111.00000000,这意味着前24位用于网络地址,剩下的8位用于主机地址。

子网划分
通过改变子网掩码中的1和0的位置,可以实现对网络的进一步细分。例如,对于一个C类地址,如果需要更多的子网而不是每个子网有很多主机,可以增加子网掩码中的1的数量。比如使用255.255.255.128(11111111.11111111.11111111.10000000)作为子网掩码,这样就创建了两个子网,每个子网有126个可用的主机地址。

CIDR表示法
CIDR(无类别域间路由)是一种改进的子网划分方法,它允许更灵活地分配和聚合IP地址。CIDR使用斜线加数字的形式来表示子网掩码,如192.168.1.0/24,这里的/24表示子网掩码中有24个连续的1,即255.255.255.0。

断点续传在浏览器如何实现

断点续传是一种在网络传输中恢复中断数据的技术,常用于下载文件时继续未完成的部分。浏览器中实现断点续传主要依赖HTTP的Range头和状态码206 Partial Content

  • Range头:客户端在请求资源时,通过Range头指定请求的范围,如Range: bytes=500-表示请求资源的 500 字节之后的部分。
  • 206 Partial Content:服务器收到带有Range头的请求后,会返回状态码206 Partial Content,并在响应头中包含Content-Range字段,指示返回的数据范围。
  • 实现细节:
    1. 发送初始请求:浏览器首先发送一个普通的GET请求,下载文件的起始部分。
    2. 检测中断:如果下载中断或用户暂停下载,记录下已下载的字节数。
    3. 发送带Range头的请求:重新开始下载时,浏览器发送一个带有Range头的请求,从上次中断的地方继续。
    4. 处理服务器响应:服务器返回206 Partial Content状态码和请求范围的内容,浏览器继续下载剩余部分。

如何判断浏览器是否支持断点续传
要检测浏览器是否支持断点续传,可以通过发送一个带有Range头的请求,并检查服务器的响应。如果服务器返回状态码206 Partial Content,则表示支持断点续传。

处理不支持断点续传的情况
如果浏览器或服务器不支持断点续传,可以采取以下几种策略:

  • 完整下载:直接进行完整下载,而不是尝试断点续传。
  • 分块下载:将文件分成多个小块,分别下载并在客户端合并。这种方法需要服务器支持多部分下载,但不依赖于HTTP的断点续传功能。

在项目中处理断点续传的支持性
在实际项目中,通常会有以下步骤来处理断点续传的支持性:

  • 检测支持性:如上所示,首先检测浏览器是否支持断点续传。
  • 优先使用断点续传:如果支持,使用断点续传来提高下载效率和用户体验。
  • 回退方案:如果不支持,使用完整下载或其他替代方案。
  • 用户提示:在用户界面上显示相关提示,例如告诉用户当前浏览器不支持断点续传,可能需要更长时间下载。

大端/小端

大端(Big Endian)和小端(Little Endian)是两种不同的字节序(也叫“字节顺序”)概念,用来定义多字节数据在内存中存储的顺序。具体来说:

  • 大端模式:将数据的高字节存储在内存的低地址,低字节存储在高地址。
  • 小端模式:将数据的低字节存储在内存的低地址,高字节存储在高地址。

为什么会存在大端和小端?

大端和小端并非仅仅是设计者的爱好,而是与历史、硬件架构设计及其应用需求相关:

  1. 历史原因:不同的计算机架构、处理器厂商在早期开发时,选择了不同的字节序。例如,Motorola 68k系列处理器使用大端字节序,而Intel x86系列使用小端字节序。不同的设计理念导致了不同的字节存储方式。

  2. 应用场景差异

    • 大端模式更符合人类书写习惯,因为我们从左到右书写高位在前的数字。
    • 小端模式则在某些场景下更加高效,比如在处理低位优先的计算操作时,小端可以直接访问低位字节,处理器可能因此提高效率。

网络传输中的大端/小端

网络传输中,通常使用大端字节序,这被称为“网络字节序”。这是因为网络传输协议(如TCP/IP)规定数据要按照大端序进行传输。

需要注意的点

  • 当不同字节序的机器(如一台大端字节序的机器和一台小端字节序的机器)进行通信时,必须确保数据按照统一的网络字节序传输。
  • 如果两台机器使用不同的字节序,在进行数据传输前,可能需要将本机的数据转换成网络字节序,并在接收到数据后再转换回本机字节序。
  • 在编写跨平台网络程序时,通常使用标准库中的函数(如htonl()ntohl()等)来进行字节序的转换,确保数据的正确性。

总结来说,字节序问题与硬件设计历史和处理器架构密切相关,而在网络传输中,为了统一和避免混乱,使用大端字节序作为标准。

长连接/短连接

在计算机网络中,长连接短连接是指客户端与服务器之间的连接状态和持续时间。它们的区别主要在于连接的维护方式和使用场景。

长连接

  • 定义:长连接是一种保持客户端与服务器之间的连接持续较长时间的方式,多个数据请求可以共享同一个连接。
  • 特点
    • 在首次建立连接后,连接不会立即关闭,而是保持一段时间,期间可以多次请求数据。
    • 减少了多次建立连接和断开连接的开销(如TCP的三次握手和四次挥手),提高了效率。
    • 常用于需要频繁数据交互或实时性要求高的场景,如聊天应用、WebSocket、视频直播等。
    • 服务器和客户端需要定期发送心跳包以保持连接活跃,防止超时关闭。
  • 优点
    • 适用于频繁交互的场景,减少重复建立连接的成本。
    • 提高了通信效率,降低了延迟。
  • 缺点
    • 占用系统资源较多,服务器需要管理更多的活跃连接。
    • 如果不合理管理,可能导致连接过多,服务器压力增大。
  • 使用场景:长连接适合需要频繁通信或实时互动的场景,例如即时通讯、在线游戏、WebSocket、数据库连接等。

短连接

  • 定义:短连接是一种在每次客户端与服务器通信时都建立新连接,并在数据传输完成后立即关闭连接的方式。
  • 特点
    • 每次请求数据都会新建一个连接,完成后立即断开连接。
    • HTTP/1.0中默认使用短连接,适用于简单的请求-响应模式。
    • 资源消耗相对较少,因为连接只在需要时才建立和维护。
  • 优点
    • 适用于较少交互或单次请求的场景,如一次性文件下载、网页请求。
    • 每次请求都重新建立连接,服务器不需要长期维护大量的活跃连接。
  • 缺点
    • 每次请求都要进行连接的建立和断开,增加了连接的开销,降低了效率。
    • 在高频请求的场景下,性能可能不如长连接。
  • 使用场景:短连接适合简单的请求-响应场景,例如静态网页访问、文件下载等。

操作系统

用户态和内核态

  • 用户态是指应用程序运行时所处的模式。在用户态下,程序执行受到严格的限制,不能直接访问硬件或内存中的关键部分。任何试图进行这些操作的指令都会导致异常,并且操作系统会终止该程序。这种限制可以防止用户程序破坏系统的稳定性和安全性。
    • 受限的指令集:只能执行非特权指令。
    • 受限的资源访问:不能直接访问硬件设备和内核内存空间。
    • 需要通过系统调用(System Call)与内核进行交互。
  • 内核态是指操作系统内核运行时所处的模式。在内核态下,系统具有完全的访问权限,可以执行任何指令,并且可以访问所有的硬件设备和内存。这种模式下,操作系统能够管理系统资源和控制硬件。
    • 完全的指令集:可以执行所有的指令,包括特权指令。
    • 完全的资源访问:可以直接访问硬件设备和内存。
    • 执行关键任务:处理系统调用、中断处理、设备管理等。

切换

  • 系统调用:当用户态的应用程序需要执行特权操作(如读写文件、分配内存、访问硬件设备)时,它会发出一个系统调用请求,触发从用户态到内核态的切换。内核处理完系统调用后,会返回用户态继续执行应用程序。
  • 中断:当外围设备完成用户请求的操作后,会向 CPU 发出相应的中断信号,这时 CPU 会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序。这种从用户态到内核态的切换是由硬件中断控制器完成的。
  • 异常:当 CPU 在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。

示例

  • 用户态:运行中的应用程序,如文本编辑器、浏览器等。它们通过系统调用请求操作系统提供的服务。
  • 内核态:操作系统内核在处理系统调用、硬件中断或其他低级别任务时的运行状态。

安全性和稳定性
用户态和内核态的分离是现代操作系统安全性和稳定性的关键。通过将用户程序与操作系统核心隔离,防止用户程序直接操作硬件或内存,减少了系统崩溃和安全漏洞的风险。

总结
用户态和内核态的划分使得计算机系统能够有效地管理资源,提供安全的运行环境,同时允许用户程序执行。用户态用于执行普通应用程序,而内核态用于执行操作系统核心任务,保证了系统的稳定性和安全性。

只有内核态不行吗?

  • 在 CPU 的所有指令中,有一些指令是比较危险的比如内存分配、设置时钟、IO 处理等,如果所有的程序都能使用这些指令的话,会对系统的正常运行造成灾难性地影响。因此,需要限制这些危险指令只能内核态运行。这些只能由操作系统内核态执行的指令也被叫做 特权指令 。
  • 如果计算机系统中只有一个内核态,那么所有程序或进程都必须共享系统资源,例如内存、CPU、硬盘等,这将导致系统资源的竞争和冲突,从而影响系统性能和效率。并且,这样也会让系统的安全性降低,毕竟所有程序或进程都具有相同的特权级别和访问权限。

系统调用

系统调用是应用程序与操作系统之间进行交互的一种方式,通过系统调用,应用程序可以访问操作系统底层资源例如文件、设备、网络等。

系统调用(System Call)是操作系统提供给应用程序的接口,用于访问操作系统内核的服务和功能。在用户程序中,凡是与系统态级别的资源有关的操作(如文件管理、进程控制、内存管理等),必须通过系统调用方式向操作系统提出服务请求,并由操作系统代为完成。

系统调用分下面几类:

  • 设备管理:完成设备(输入输出设备和外部存储设备等)的请求或释放,以及设备启动等功能。
  • 文件管理:完成文件的读、写、创建及删除等功能。
  • 进程管理:进程的创建、撤销、阻塞、唤醒,进程间的通信等功能。
  • 内存管理:完成内存的分配、回收以及获取作业占用内存区大小及地址等功能。

系统调用和普通库函数调用非常相似,只是系统调用由操作系统内核提供,运行于内核态,而普通的库函数调用由函数库或用户自己提供,运行于用户态。

虚拟内存

虚拟内存是一种内存管理技术,它为每个进程提供了一个统一的地址空间,使得进程认为它独占一个大的连续内存空间。然而,实际的物理内存可能是零散的或不足的,操作系统通过虚拟内存技术将虚拟地址转换为物理地址,从而透明地管理内存。

主要作用:

  • 隔离进程:物理内存通过虚拟地址空间访问,虚拟地址空间与进程一一对应。每个进程都认为自己拥有了整个物理内存,进程之间彼此隔离,一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。
  • 提升物理内存利用率:有了虚拟地址空间后,操作系统只需要将进程当前正在使用的部分数据或指令加载入物理内存。
  • 简化内存管理:进程都有一个一致且私有的虚拟地址空间,程序员不用和真正的物理内存打交道,而是借助虚拟地址空间访问物理内存,从而简化了内存管理。
  • 多个进程共享物理内存:进程在运行过程中,会加载许多操作系统的动态库。这些库对于每个进程而言都是公用的,它们在内存中实际只会加载一份,这部分称为共享内存。
  • 提高内存使用安全性:控制进程对物理内存的访问,隔离不同进程的访问权限,提高系统的安全性。
  • 提供更大的可使用内存空间:可以让程序拥有超过系统物理内存大小的可用内存空间。这是因为当物理内存不够用时,可以利用磁盘充当,将物理内存页(通常大小为 4 KB)保存到磁盘文件(会影响读写速度),数据或代码页会根据需要在物理内存与磁盘之间移动。

没有虚拟内存会存在什么问题?

  • 程序之间的内存隔离性差:没有虚拟内存,不同程序的内存空间可能会重叠,一个程序可以访问另一个程序的内存空间,导致数据泄露或者程序崩溃。
  • 物理内存利用率低:没有虚拟内存,每个程序都需要占用一定的物理内存,如果物理内存不足,就会导致程序无法运行。
  • 程序的内存管理复杂:没有虚拟内存,程序需要直接管理物理内存,需要考虑内存的分配、释放、回收等问题,增加了程序的复杂性。
  • 程序的安全性差:没有虚拟内存,程序可以直接访问物理内存,可能会导致程序的安全漏洞,如缓冲区溢出等。

内存池

内存池(Memory Pool)是一种内存管理技术,旨在提高内存分配和释放的效率,同时减少内存碎片化的问题。内存池主要用于那些需要频繁进行内存分配和释放的场景,比如游戏开发、实时系统、嵌入式系统等。

内存池可以在应用程序级别实现,也可以在操作系统内核中实现。例如,操作系统内核中的一些子系统(如文件系统、网络堆栈等)可能会使用内存池来提高内存管理的效率。在用户态,应用程序可以自行实现内存池,或者使用一些第三方库来管理内存池。而在内核态,操作系统可能会提供类似的机制来优化内存管理。

内存碎片

  • 外部碎片化(External Fragmentation):内存中存在许多小的、非连续的空闲块,导致大块内存请求无法满足,即使总的空闲内存量足够。
  • 内部碎片化(Internal Fragmentation):分配的内存块内部有未被使用的空间,因为分配的块大于实际需要的内存。

内存池如何解决内存碎片问题

  • 预先分配固定大小的内存块:内存池通常在初始化时预先分配一大块连续的内存,然后将这块内存划分成许多大小相等的内存块。这样做可以避免外部碎片化,因为所有的内存分配都是从预先划分好的内存块中进行的。
  • 减少分配和释放次数:通过预先分配和重用内存块,内存池减少了系统进行内存分配和释放的次数。这不仅提高了性能,还减少了产生碎片的机会。
  • 分层内存池:有些内存池会根据不同大小的内存请求划分成多个子池,每个子池管理一种特定大小的内存块。这样可以有效减少内部碎片化,因为每个内存块的大小都是根据需求精细划分的。

进程/线程/协程

https://blog.csdn.net/m0_60505735/article/details/131047046
https://blog.csdn.net/weixin_49199646/article/details/109210547

  • 进程: 进程是程序的一次执行过程,是系统资源分配和运行程序的基本单位;一个进程在其执行的过程中可以产生多个线程。
  • 线程: 线程是进程的一个执行单元,是任务调度和系统执行的最小单位;与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈。所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
  • 协程: 协程是一种用户态的轻量级线程,协程的调度完全由用户控制。

进程与线程的区别

  • 根本区别: 进程是操作系统资源分配和独立运行的最小单位;线程是任务调度和系统执行的最小单位。
  • 地址空间区别: 每个进程都有独立的地址空间,一个进程崩溃不影响其它进程;一个进程中的多个线程共享该 进程的地址空间,一个线程的非法操作会使整个进程崩溃。
  • 上下文切换开销区别: 每个进程有独立的代码和数据空间,进程之间上下文切换开销较大;线程组共享代码和数据空间,线程之间切换的开销较小。

进程与线程的联系
一个进程由共享空间(包括堆、代码区、数据区、进程空间和打开的文件描述符)和一个或多个线程组成,各个线程之间共享进程的内存空间。而一个标准的线程由线程ID、程序计数器PC、寄存器和栈组成。

进程与线程的选择

  • 线程的创建或销毁的代价比进程小,需要频繁创建和销毁时应优先选用线程;
  • 线程上下文切换的速度比进程快,需要大量计算时优先选用线程;
  • 线程在CPU上的使用效率更高,需要多核分布时优先选用线程,需要多机分布时优先选用进程
  • 线程的安全性、稳定性没有进程好,需要更稳定安全时优先使用进程。

综上,线程创建和销毁的代价低、上下文切换速度快、对系统资源占用小、对CPU的使用效率高,因此一般情况下优先选择线程进行高并发编程;但线程组的所有线程共用一个进程的内存空间,安全稳定性相对较差,若其中一个线程发生崩溃,可能会使整个进程,因此对安全稳定性要求较高时,需要优先选择进程进行高并发编程。

进程间的通信方式

进程间通信(Inter-Process Communication, IPC)是指在操作系统中不同进程之间传递数据或信息的机制。常见的进程间通信方式有多种,每种方式都有其独特的应用场景和优缺点。以下是一些主要的进程间通信方式:

  1. 管道(Pipes):
    • 匿名管道(Anonymous Pipes):主要用于有亲缘关系的进程之间的通信,如父子进程。数据以字节流的形式在进程间传递。
    • 命名管道(Named Pipes):支持在无亲缘关系的进程之间进行通信。命名管道存在于文件系统中,可以被不同的进程打开和使用。
  2. 消息队列(Message Queues):
    • 允许进程通过发送和接收消息进行通信。消息队列提供了一种在进程间传递数据的有序方式,可以实现异步通信。
  3. 共享内存(Shared Memory):
    • 进程共享一段内存空间,进程可以直接读写这段共享内存中的数据。共享内存是最快的一种通信方式,因为数据不需要在进程间复制,但需要额外的同步机制来避免并发访问问题。
  4. 信号量(Semaphores):
    • 用于控制多个进程对共享资源的访问,通过信号量可以实现进程间的同步和互斥。
  5. 信号(Signals):
    • 信号是一种有限的异步通知机制,用于通知进程某个事件的发生。进程可以捕捉和处理信号,从而实现简单的通信和控制。
  6. 套接字(Sockets):
    • 套接字不仅支持同一台计算机上进程间的通信,也支持分布式网络中不同计算机上的进程间通信。常用于网络编程中。
  7. 文件系统(File System):
    • 进程可以通过读写共享的文件进行通信。这种方式简单但效率较低,适用于需要持久化存储的场景。
  8. 内存映射文件(Memory-Mapped Files):
    • 通过将文件映射到进程的地址空间,实现文件内容的共享和通信。与共享内存类似,但数据的持久化由文件系统提供。

线程间的通信方式

线程间的通信是指在同一个进程内,不同线程之间交换数据或信号的机制。常见的线程间通信方式包括以下几种:

  1. 共享内存:
    • 全局变量:所有线程都可以访问和修改同一个全局变量。
    • 静态变量:静态变量在进程的生命周期内只初始化一次,所有线程共享。
  2. 互斥锁(Mutex):
    • 用于防止多个线程同时访问共享资源,从而避免数据竞争。
  3. 读写锁(RWLock):
    • 允许多个线程同时读数据,但在写数据时需要独占锁,确保写操作的安全性。
  4. 信号量(Semaphore):
    • 主要用于限制对共享资源的访问数量,可以控制同时访问资源的线程数。
  5. 条件变量(Condition Variable):
    • 用于线程之间的等待通知机制,一个线程可以等待一个条件变量,而另一个线程可以通知该条件变量改变状态,从而唤醒等待的线程。
  6. 事件(Event):
    • 线程可以等待一个事件,直到另一个线程设置该事件,从而实现线程之间的同步。
  7. 队列(Queue):
    • 线程安全的队列,常用于生产者-消费者模型,一个线程放入数据,另一个线程取出数据。
  8. 管道(Pipe):
    • 用于线程之间的数据传输,常见于一些操作系统提供的进程间通信机制中。
  9. 消息队列(Message Queue):
    • 一种线程安全的队列,专门用于在多个线程之间传递消息。
  10. 信号(Signal):
  • 一种用于通知线程某个事件发生的机制,通常用在异步事件处理。

CPU满载如何排查问题

当CPU满载时,排查问题通常需要系统地检查多个方面,以确定导致高负载的具体原因。以下是一些详细的排查步骤:

  1. 使用系统监控工具
    查看系统负载:首先,使用系统监控工具(如Windows的任务管理器、Linux的top或htop命令)来查看系统的整体负载情况,包括CPU、内存、磁盘和网络等资源的使用情况。
    确定高占用进程:在监控工具中查找哪个进程或应用程序的CPU占用率很高。这通常是导致CPU满载的直接原因。
  2. 分析进程和资源使用情况
    查看进程详细信息:在任务管理器或相应的系统监控工具中,查看高占用进程的详细信息,包括其启动时间、占用资源量、关联的模块或服务等。
    检查进程行为:分析进程的行为,看是否有异常操作或不必要的资源占用。例如,某些进程可能因为死循环、内存泄漏或低效的算法而持续占用大量CPU资源。
  3. 检查系统配置和设置
    查看启动项和服务:检查系统启动时自动运行的程序和服务,看是否有不必要的程序或服务在后台运行并占用CPU资源。
    优化系统设置:关闭不必要的后台程序、禁用不必要的系统服务、降低屏幕分辨率或减少同时打开的窗口数量等,以降低CPU的使用率。
  4. 检查硬件和驱动程序
    检查硬件状态:确保CPU、内存、硬盘等硬件设备工作正常,没有过热、损坏或性能瓶颈等问题。
    更新驱动程序:确保所有硬件设备的驱动程序都是最新的,以避免因驱动程序问题导致的CPU高占用。
  5. 排查病毒和恶意软件
    运行杀毒软件:使用可靠的杀毒软件对系统进行全面扫描,以排除病毒或恶意软件导致的CPU高占用。
    检查系统日志:查看系统日志文件,查找是否有与病毒或恶意软件相关的异常记录。
  6. 使用性能分析工具
    使用专业工具:对于复杂的问题,可以使用专业的性能分析工具(如VisualVM、Arthas等)来进一步分析CPU使用情况,并确定具体的性能瓶颈。
  7. 咨询专业人员
    如果以上步骤都无法解决问题,或者您对系统配置和性能分析不熟悉,建议咨询专业的技术人员或计算机维修人员。
    通过以上步骤,您可以系统地排查CPU满载的问题,并找到导致高负载的具体原因。在排查过程中,请保持耐心和细心,以便准确地定位问题并采取相应的解决措施。

孤儿进程

在操作系统中,孤儿进程(Orphan Process)和僵尸进程(Zombie Process)是两种特殊的进程状态,它们各自具有特定的行为和影响。

孤儿进程(Orphan Process)定义:孤儿进程是指在其父进程执行完毕或被终止后,该进程仍然运行着的进程。这些进程会被操作系统中的“进程收养所”(通常是init进程,其PID为1)收养,成为init进程的子进程。

特点:

  • 孤儿进程不再是任何有效进程的子进程。
  • 孤儿进程的结束处理由收养它的进程(如init进程)负责。
  • 孤儿进程本身对系统没有直接的负面影响,因为操作系统能够正确处理它们。

孤儿进程是系统能够自动处理的,

僵尸进程

僵尸进程(Zombie Process)定义:僵尸进程是指已经结束(即已经完成了执行)但是其父进程尚未通过调用wait()或waitpid()等系统调用来获取其终止状态的进程。

特点:

  • 僵尸进程已经释放了除进程描述符以外的所有资源,但仍在进程表中占有一个表项,这意味着它们的PID仍然被占用。
  • 如果系统中有大量僵尸进程,可能会导致PID耗尽,从而影响新进程的创建。
  • 僵尸进程的存在是为了让父进程能够查询子进程的退出状态。如果父进程不调用wait()或waitpid(),子进程就会一直处于僵尸状态。

解决方法:

  • 确保父进程在适当的时候调用wait()或waitpid()来回收子进程的终止状态,从而避免僵尸进程的产生。
  • 如果父进程已经结束,而子进程成为僵尸进程,那么这些僵尸进程最终会被init进程收养,并由init进程调用wait()来回收。
  • 在某些情况下,如果父进程确实不需要知道子进程的退出状态,可以考虑在子进程中调用_exit()而不是exit(),这样可以在子进程结束时立即释放资源,但这种方法并不推荐,因为它可能掩盖了潜在的错误。

僵尸进程则需要程序员通过编程来避免其产生或及时回收。

动态链接和静态链接的区别

动态链接和静态链接是计算机科学中两种重要的程序链接方式。

  • 静态链接:在程序编译时,将所有外部库文件(如DLL文件在Windows系统中,或.so文件在Linux系统中)直接嵌入到可执行文件中,形成一个独立的可执行文件。当程序运行时,就不需要再加载这些外部库文件。
  • 动态链接:在程序编译时,只生成程序的可执行文件和一些必要的资源文件,而将外部库文件放在一个单独的目录下(如系统的PATH环境变量所指定的目录)。当程序运行时,系统会在这些目录下查找所需的外部库文件,并将其加载到内存中。

优缺点
静态链接

  • 优点:

    • 程序运行时不依赖于外部库文件,减少了程序的依赖性,提高了程序的独立性和安全性。
    • 避免了因外部库文件版本不匹配导致的兼容性问题。
    • 在某些情况下,可以提高程序的运行速度(尽管这取决于具体情况,如硬盘读写速度等)。
  • 缺点:

    • 程序体积较大,因为包含了所有外部库文件的代码。
    • 升级程序时需要替换所有的外部库文件,增加了维护成本。
    • 灵活性较差,无法实现模块化编程。
      动态链接
  • 优点:

    • 程序体积较小,因为不包含外部库文件的代码,只包含对外部库文件的引用。
    • 便于升级和维护,只需替换外部库文件即可,无需重新编译整个程序。
    • 灵活性高,可以根据需要加载不同的库或模块,实现模块化编程。
    • 节省内存和磁盘空间,因为多个程序可以共享同一个外部库文件。
  • 缺点:

    • 程序运行时需要依赖于外部库文件,可能导致安全性问题和兼容性问题。
    • 性能开销较大,因为程序运行时需要不断地检查外部库或模块是否已经加载。
    • 可能导致内存泄漏等问题,需要开发者更加关注内存管理。
  • 应用场景

    • 静态链接:适用于需要将程序打包成一个独立的可执行文件,并且不需要频繁更新外部库文件的场景。例如,嵌入式系统、游戏等对性能和安全性要求较高的应用程序。
    • 动态链接:适用于项目规模较大、模块化程度较高的项目,以及需要频繁更新外部库文件的场景。例如,操作系统、数据库等需要不断更新的软件。

Linux

Linux文件系统简介

在 Linux 操作系统中,一切被操作系统管理的资源,如网络接口卡、磁盘驱动器、打印机、输入输出设备、普通文件或目录等,都被视为文件。这种设计使得 Linux 系统可以通过统一的文件接口来管理和操作不同类型的资源,从而实现了一种统一的文件操作方式。如可以使用类似于读写文件的方式来对待网络接口、磁盘驱动器、设备文件等,使得操作和管理这些资源更加统一和简便。种文件为中心的设计理念为 Linux 系统带来了灵活性和可扩展性,使得 Linux 成为一种强大的操作系统。

inode介绍

inode 是 Linux/Unix 文件系统的基础。inode存储文件的 元信息 metadata:如某个文件被分成几块、每一块在的地址、文件拥有者,创建时间,权限,大小等。

  1. 硬盘的最小存储单位是扇区(Sector),块(block)由多个扇区组成。文件数据存储在块中。块的最常见的大小是 4kb,约为 8 个连续的扇区组成(每个扇区存储 512 字节)。一个文件可能会占用多个 block,但是一个块只能存放一个文件(inode指向文档数据,如果有两个文件的数据放在同一个块中,就不对了)。每个文件都有一个唯一的 inode,存储文件的元信息。
  2. inode 是一种固定大小的数据结构,其大小在文件系统创建时就确定了,且在文件生命周期内不改变。
  3. inode 访问速度很快,系统可直接通过 inode 号码定位到文件的元数据信息,无需遍历整个文件系统。
  4. inode 数量是有限,每个文件系统只能包含固定数量的 inode。当文件系统中的 inode 用完后,即使磁盘上还有可用空间,也无法创建新的文件或目录。
  5. stat 命令可查看文件的 inode 信息,包括文件的 inode 号、文件类型、权限、所有者、文件大小、修改时间。

优点
Linux/Unix 操作系统使用 inode 区分不同的文件。这样即便文件名被修改或删除,文件的 inode 号码不会改变,从而避免一些因文件重命名、移动或删除导致的错误。同时,由于inode的访问速度非常快,可直接通过 inode 号码定位到文件的元数据信息,无需遍历整个文件系统,因此可提供更高的文件系统性能。

缺点
但使用 inode 号码也使得文件系统在用户和应用程序层面更加抽象和复杂,需要通过系统命令或文件系统接口来访问和管理文件的 inode 信息。

硬链接/软链接

在 Linux/类 Unix 系统上,文件链接(File Link)是一种特殊的文件类型,可以在文件系统中指向另一个文件。常见的文件链接类型有两种:

1、硬链接(Hard Link)

  • 在 Linux/类 Unix 文件系统中,每个文件和目录都有一个唯一的索引节点(inode)号,用来标识该文件或目录。硬链接通过 inode 节点号建立连接,硬链接和源文件的 inode 节点号相同,两者对文件系统来说是完全平等的(可以看作是互为硬链接,源头是同一份文件),删除其中任何一个对另外一个没有影响,可以通过给文件设置硬链接文件来防止重要文件被误删。
  • 只有删除了源文件和所有对应的硬链接文件,该文件才会被真正删除。
  • 硬链接具有一些限制,不能对目录以及不存在的文件创建硬链接,并且,硬链接也不能跨越文件系统。
  • ln 命令用于创建硬链接。

2、软链接(Symbolic Link 或 Symlink)

  • 软链接和源文件的 inode 节点号不同,而是指向一个文件路径。
  • 源文件删除后,软链接依然存在,但是指向的是一个无效的文件路径。
  • 软连接类似于 Windows 系统中的快捷方式。
  • 不同于硬链接,可以对目录或者不存在的文件创建软链接,并且,软链接可以跨越文件系统。
  • ln -s 命令用于创建软链接。

硬链接为啥不能跨文件系统?

硬链接是通过 inode 节点号建立连接的,而硬链接和源文件共享相同的 inode 节点号。

然而,每个文件系统都有自己的独立 inode 表,且每个 inode 表只维护该文件系统内的 inode。如果在不同的文件系统之间创建硬链接,可能会导致 inode 节点号冲突的问题,即目标文件的 inode 节点号已经在该文件系统中被使用。

Linux 文件类型

Linux 支持很多文件类型,其中非常重要的文件类型有: 普通文件目录文件链接文件设备文件管道文件Socket 套接字文件 等。

  • 普通文件(-):用于存储信息和数据, Linux 用户可以根据访问权限对普通文件进行查看、更改和删除。比如:图片、声音、PDF、text、视频、源代码等等。
  • 目录文件(d,directory file):目录也是文件的一种,用于表示和管理系统中的文件,目录文件中包含一些文件名和子目录名。打开目录事实上就是打开目录文件。
  • 符号链接文件(l,symbolic link):保留了指向文件的地址而不是文件本身。
  • 字符设备(c,char):用来访问字符设备比如键盘。
  • 设备文件(b,block):用来访问块设备比如硬盘、软盘。
  • 管道文件(p,pipe):一种特殊类型的文件,用于进程之间的通信。
  • 套接字文件(s,socket):用于进程间的网络通信,也可以用于本机之间的非网络通信。

每种文件类型都有不同的用途和属性,可以通过命令如lsfile等来查看文件的类型信息。

1
2
3
4
5
6
7
8
# 普通文件(-)
-rw-r--r-- 1 user group 1024 Apr 14 10:00 file.txt

# 目录文件(d,directory file)*
drwxr-xr-x 2 user group 4096 Apr 14 10:00 directory/

# 套接字文件(s,socket)
srwxrwxrwx 1 user group 0 Apr 14 10:00 socket

Linux 目录树

Linux 使用目录树这种层次结构来组织文件和目录。目录树由根目录(/)作为起始点,向下延伸,形成一系列的目录和子目录。每个目录可以包含文件和其他子目录。结构层次鲜明,就像一棵倒立的树。
Linux目录树

常见目录说明:

  • /bin: 存放二进制可执行文件(ls、cat、mkdir等),常用命令一般都在这里;
  • /etc: 存放系统管理和配置文件;
  • /home: 存放所有用户文件的根目录,是用户主目录的基点,比如用户 user 的主目录就是/home/user,可以用 ~user 表示;
  • /usr: 用于存放系统应用程序;
  • /opt: 额外安装的可选应用程序包所放置的位置。一般情况下,我们可以把 tomcat 等都安装到这里;
  • /proc: 虚拟文件系统目录,是系统内存的映射。可直接访问这个目录来获取系统信息;
  • /root: 超级用户(系统管理员)的主目录;
  • /sbin: 存放二进制可执行文件,只有 root 才能访问。这里存放的是系统管理员使用的系统级别的管理命令和程序。如 ifconfig 等;
  • /dev: 用于存放设备文件;
  • /mnt: 系统管理员安装临时文件系统的安装点,系统提供这个目录是让用户临时挂载其他的文件系统;
  • /boot: 存放用于系统引导时使用的各种文件;
  • /lib 和/lib64: 存放着和系统运行相关的库文件;
  • /tmp: 用于存放各种临时文件,是公用的临时文件存储点;
  • /var: 用于存放运行时需要改变数据的文件,也是某些大文件的溢出区,比方说各种服务的日志文件(系统启动日志等。)等;
  • /lost+found: 这个目录平时是空的,系统非正常关机而留下的文件(windows下xxx.chk)就在这里。

Linux 环境变量

在 Linux 系统中,环境变量是用来定义系统运行环境的一些参数,比如每个用户不同的主目录(HOME)。

按照作用域来分,环境变量可以简单的分成:

  • 用户级别环境变量 : ~/.bashrc~/.bash_profile
  • 系统级别环境变量 : /etc/bashrc/etc/environment/etc/profile/etc/profile.d

上述配置文件执行先后顺序为:/etc/environment –> /etc/profile –> /etc/profile.d –> ~/.bash_profile –> /etc/bashrc –> ~/.bashrc

如果要修改系统级别环境变量文件,需要管理员具备对该文件的写入权限。建议用户级别环境变量在 ~/.bash_profile中配置,系统级别环境变量在 /etc/profile.d 中配置。

按照生命周期来分,环境变量可以简单的分成:

  • 永久的:需要用户修改相关的配置文件,变量永久生效。
  • 临时的:用户利用 export 命令,在当前终端下声明环境变量,关闭 shell 终端失效。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 输出当前的PATH环境变量的值
echo $PATH
# 输出当前的HOME环境变量的值
echo $HOME

# 列出当前的环境变量值
export -p
env
# export 命令可以修改指定的环境变量。不过这种方式修改环境变量仅仅对当前 shell 终端生效,关闭 shell 终端就会失效。修改完成之后,立即生效。
export CLASSPATH=./JAVA_HOME/lib;$JAVA_HOME/jre/lib

# 通过 vim 命令修改环境变量配置文件。这种方式修改环境变量永久有效
vim ~/.bash_profile
# 如果修改的是系统级别环境变量则对所有用户生效,如果修改的是用户级别环境变量则仅对当前用户生效。
# 修改完成之后,需要 `source` 命令让其生效或者关闭 shell 终端重新登录。
source /etc/profile

Linux常用命令

可以在这个网站查看更加详细的命令:https://wangchujiang.com/linux-command/
可以在这个网站解释常见命令(Linux、Git、npm)的意思:https://www.shell.how/

目录切换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 切换到该目录下 usr 目录
cd user

# 切换到上一层目录
cd ..
cd ../

# 切换到系统根目录
cd /

# 切换到用户主目录
cd ~

# 切换到上一个操作所在目录
cd -

目录操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 显示目录中的文件和子目录的列表。
ls [目录]
# 显示 /home 目录下的文件和子目录列表。
ls /home
# 查看到该目录下的所有目录和文件的详细信息
ls -l
ll # 是ls -l的缩写

# 创建新目录(增)
mkdir [选项] 目录名
# 创建一个名为 `my_directory` 的新目录,并将其权限设置为 755,即所有用户对该目录有读、写和执行的权限。
mkdir -m 755 my_directory

# 在指定目录及其子目录中搜索文件或目录(查)
find [路径] [表达式]
# 列出当前目录及子目录下所有文件和文件夹
find .
# 在 /home 目录下查找以 .txt 结尾的文件名
find /home -name "*.txt"
find /home -i name "*.txt" # 忽略大小写
# 在当前目录及子目录下查找所有以 .txt 和 .pdf 结尾的文件
find . \( -name "*.txt" -o -name "*.pdf" \)
find . -name "*.txt" -o -name "*.pdf"

# 显示当前工作目录的路径
pwd

# 删除空目录(删)
rmdir [选项] 目录名
# 删除名为 my_directory 的空目录,并且会递归删除 my_directory 的空父目录,直到遇到非空目录或根目录。
rmdir -p my_directory
# 删除文件/目录(删)
rm [选项] 文件或目录名
# 删除名为 my_directory 的目录,-r(recursive,递归) 表示会递归删除指定目录及其所有子目录和文件。
rm -r my_directory
# 强制删除名为 my_directory 的目录,f(force,强制)表示强制。
rm -rf my_directory

# 复制文件或目录(移)
cp [选项] 源文件/目录 目标文件/目录
# 将 file.txt 文件复制到 /home 目录下,并重命名为 file.txt。
cp file.txt /home/file.txt
# 将 source 目录及其下的所有子目录和文件复制到 destination 目录下,并保留源文件的属性和目录结构。
cp -r source destination

# 移动文件或目录(移),也可以用于重命名文件或目录。
mv [选项] 源文件/目录 目标文件/目录
# 将 file.txt 文件移动到 /home 目录下,并重命名为 file.txt。`mv` 与 `cp` 的结果不同,`mv` 好像文件“搬家”,文件个数并未增加。而 `cp` 对文件进行复制,文件个数增加了。
mv file.txt /home/file.txt

文件操作

mvcprm 等文件和目录都适用的命令,这里就不重复列举了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 创建新文件或更新已存在文件(增)。
touch [选项] 文件名..
# 创建file1.txt file2.txt file3.txt 3 个文件
touch file1.txt file2.txt file3.txt

# 创建硬链接/软链接。
ln [选项] <源文件> <硬链接/软链接文件>
# 创建名为 file_link 的软链接,指向 file.txt 文件。
ln -s file.txt file_link # -s 选项代表的就是创建软链接,s 即 symbolic(软链接又名符号链接) 。

# 文件的查看(查)。
cat 文件名
more 文件名
less 文件名
tail 文件名
# 对某个文件进行动态监控,如 Tomcat 文件
tail -f 文件

# 修改文件的内容(改)。
vim 文件名
# 在实际开发中,使用 vim 编辑器主要作用就是修改配置文件,步骤:
# vim 文件-->进入文件-->命令模式-->按i进入编辑模式-->编辑文件-->按Esc进入底行模式-->输入:wq/q!
# (输入 wq 代表写入内容并退出,即保存;输入 `q!`代表强制退出不保存)。

文件压缩

1)打包并压缩文件:
Linux 中的打包文件一般是以 .tar 结尾的,压缩的命令一般是以 .gz 结尾的。而一般情况下打包和压缩是一起进行的,打包并压缩后的文件的后缀名一般 .tar.gz

  • z:调用 gzip 压缩命令进行压缩
  • c:打包文件
  • v:显示运行过程
  • f:指定文件名
1
tar [-zcvf] 打包压缩后的文件名 要打包压缩的文件

假如 test 目录下有三个文件分别是:aaa.txtbbb.txtccc.txt,若要打包 test 目录并指定压缩后的压缩包名称为 test.tar.gz 可以使用命令:

1
2
tar -zcvf test.tar.gz aaa.txt bbb.txt ccc.txt
tar -zcvf test.tar.gz /test/

2)解压压缩包:

1
tar [-xvf] 压缩文件
  • x:代表解压
1
2
3
4
# 将 /test 下的 test.tar.gz 解压到当前目录下
tar -xvf test.tar.gz
# 将 /test 下的 test.tar.gz 解压到根目录/usr 下
tar -xvf test.tar.gz -C /usr # -C 代表指定解压的位置

文件传输

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# scp 即 secure copy,安全复制:通过 SSH 协议安全传输文件,实现本地与远程主机之间的文件上传与下载。
scp [选项] 源文件 远程文件
# 将本地目录 my_directory 上传到远程服务器 /home/user 目录下。
scp -r my_directory user@remote:/home/user
# 将远程服务器的 /home/user 目录下的 my_directory 目录下载到本地。
scp -r user@remote:/home/user/my_directory

# scp 命令要在本地和远程系统之间建立 SSH 连接进行文件传输,需要确保远程服务器已经配置了 SSH 服务,并且具有正确的权限和认证方式。

# 在本地和远程系统之间高效地进行文件复制,并且能够智能地处理增量复制,节省带宽和时间。
rsync [选项] 源文件 远程文件
# 将本地目录 my_directory 上传到远程服务器 /home/user 目录下。
rsync -r my_directory user@remote:/home/user

# File Transfer Protocol:提供一种简单方式来连接到远程 FTP 服务器并进行文件上传、下载、删除等操作。
ftp
# 使用之前需要先连接登录远程 FTP 服务器,进入 FTP 命令行界面后,
# 用 put 命令将本地文件上传到远程主机,用 get 命令将远程主机的文件下载到本地,用 delete 命令删除远程主机的文件。

文件权限

操作系统中每个文件都拥有特定的权限、所属用户和所属组。权限是操作系统用来限制资源访问的机制,在 Linux 中权限一般分为读(readable)、写(writable)和执行(executable),分为三组。分别对应文件的属主(owner),属组(group)和其他用户(other),通过这样的机制来限制哪些用户、哪些组可以对特定的文件进行什么样的操作。

通过 ls -l 命令可查看某个目录下的文件或目录的权限。第一列的内容的信息解释如下:
Linux权限解读

文件的类型:

  • d:代表目录
  • -:代表文件
  • l:代表软链接(可以认为是 window 中的快捷方式)

Linux 中权限分为以下几种:

  • r:代表权限是可读,也可以用数字 4 表示
  • w:代表权限是可写,也可以用数字 2 表示
  • x:代表权限是可执行,也可以用数字 1 表示

文件和目录权限的区别:
对文件和目录而言,读写执行表示不同的意义。
对于文件:

  • r:可以使用 cat 查看文件的内容
  • w:可以修改文件的内容
  • x:可以将其运行为二进制文件

对于目录:

  • r:可以查看目录下列表
  • w:可以创建和删除目录下文件
  • x:可以使用 cd 进入目录

超级用户可以无视普通用户的权限,即使文件目录权限是 000,依旧可以访问。

修改文件的所有者&所在组
在 linux 中的每个用户必须属于一个组,不能独立于组外。

  • 所有者(u):一般为文件的创建者,谁创建了该文件,就是该文件的所有者。
    1
    2
    3
    4
    # 查看文件的所有者
    ls -ahl
    # 修改文件的所有者。
    chown 用户名 文件名
  • 文件所在组(g):当某个用户创建了一个文件后,这个文件的所在组就是该用户所在的组。
    1
    2
    3
    4
    # 查看文件的所有组
    ls -ahl
    # 修改文件所在的组。
    chgrp 组名 文件名
  • 其它组(o):除开文件的所有者和所在组的用户外,系统的其它用户都是文件的其它组。

修改文件/目录的权限的命令

1
2
3
4
chmod 修改后的权限 文件
# 修改 /test 下的 aaa.txt 的权限为文件所有者有全部权限,文件所有者所在的组有读写权限,其他用户只有读的权限。
chmod u=rwx,g=rw,o=r aaa.txt
chmod 764 aaa.txt

zookeeper开机自启动

1
2
3
4
5
6
7
8
# 新建一个脚本 zookeeper
zookeeper
# 为新建的脚本 zookeeper 添加可执行权限
chmod +x zookeeper
# 把 zookeeper 这个脚本添加到开机启动项里面
chkconfig --add zookeeper
# 查看是否添加成功
chkconfig --list

用户管理

Linux 系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统。

用户的账号一方面可以帮助系统管理员对使用系统的用户进行跟踪,并控制他们对系统资源的访问;另一方面也可以帮助用户组织文件,并为用户提供安全性保护。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 创建用户账号,保存在 /etc/passwd 文本文件中。
useradd [选项] 用户名

# 删除用户帐号。
userdel [选项] 用户名

# 修改用户账号的属性和配置如用户名、用户 ID、家目录。
usermod [选项] 用户名

# 设置用户的认证信息,如用户密码、密码过期时间等。
passwd [选项] 用户名
# 显示用户账号密码信息。
passwd -S 用户名
# 清除用户密码,会导致用户无法登录。
passwd -d 用户名
# 修改用户密码,随后系统会提示输入新密码并确认密码。
passwd 用户名

# su 即 Switch User,切换用户:在当前登录的用户和其他用户之间切换身份。
su [选项] 用户名

用户组管理

每个用户都有一个用户组,系统可以对一个用户组中的所有用户进行集中管理。不同 Linux 系统对用户组的规定有所不同,如 Linux 下的用户属于与它同名的用户组,这个用户组在创建用户时同时创建。

组的增加、删除和修改实际上就是对 /etc/group 文件的更新。

1
2
3
4
5
6
7
8
# 增加一个新的用户组。
groupadd [选项] 用户组

# 要删除一个已有的用户组。
groupdel 用户组

# 修改用户组的属性。
groupmod [选项] 用户组

系统状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 实时查看系统的 CPU 使用率、内存使用率、进程信息等。
top [选项]

# 类似于 top,但提供了更加交互式和友好的界面,可让用户交互式操作,支持颜色主题,可横向或纵向滚动浏览进程列表,并支持鼠标操作。
htop [选项]

# 查看系统总共运行了多长时间、系统的平均负载等信息。
uptime [选项]

# vmstat (Virtual Memory Statistics) 显示虚拟内存状态,但是它可以报告关于进程、内存、I/O 等系统整体运行状态。
vmstat [间隔时间] [重复次数]

# 查看系统的内存使用情况,包括已用内存、可用内存、缓冲区和缓存等。
free [选项]
# -h选项表示以人类可读的格式(如KB、MB、GB)显示内存大小。
free -h

# 查看系统的磁盘空间使用情况,包括磁盘空间的总量、已使用量和可用量等,可以指定文件系统上。
df [选项] [文件系统]
# 查看全部文件系统。
df -a

# 查看指定目录或文件的磁盘空间使用情况,可指定不同的选项来控制输出格式和单位。
du [选项] [文件]

# 用于收集、报告和分析系统的性能统计信息,包括系统的 CPU 使用、内存使用、磁盘 I/O、网络活动等详细信息。
# 其特点是可以连续对系统取样,获得大量的取样数据。取样数据和分析的结果都可以存入文件,使用它时消耗的系统资源很小。
sar [选项] [时间间隔] [重复次数]

# 查看系统中的进程信息,包括进程的 ID、状态、资源使用情况等。
ps [选项]
# 两个命令都是查看当前系统正在运行进程,两者的区别是展示格式不同。
ps -ef
ps -aux
# 查看特定的进程。如查看包括 redis 字符串的进程
ps aux|grep redis
pgrep redis -a

# 管理系统的服务和单元,可以查看系统服务的状态、启动、停止、重启等。
systemctl [命令] [服务名称]

网络通信

1
2
3
4
5
6
7
8
9
10
11
12
# 测试与目标主机的网络连接。
ping [选项] 目标主机

# 查看系统的网络接口信息,包括网络接口的 IP 地址、MAC 地址、状态等。
ifconfig
ip

# 查看系统的网络连接状态和网络统计信息,可以查看当前的网络连接情况、监听端口、网络协议等。
netstat [选项]

# 比 netstat 更好用,提供了更快速、更详细的网络连接信息。
ss [选项]

其他

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 以系统管理者的身份执行指令,经由 sudo 所执行的指令就好像是 root 亲自执行。
sudo + 其他命令

# 搜索命令,--color 代表高亮显示。
grep 要搜索的字符串 要搜索的文件 --color

# 杀死进程(-9 表示强制终止)先用 ps 查找进程,然后用 kill 杀掉。
kill -9 进程的pid

shutdown
# 指定现在立即关机
shutdown -h now
# 指定 5 分钟后关机,同时送出警告信息给登入用户。
shutdown +5 "System will shutdown after 5 minutes"

# 重开机
reboot
# 做个重开机的模拟(只有纪录并不会真的重开机)。
reboot -w

常见Linux线程间的通信方式

如何查询内存大小

1
2
3
4
5
# -h选项表示以人类可读的格式(如KB、MB、GB)显示内存大小。
free -h

# /proc/meminfo文件包含了详细的内存使用信息。可以在这个文件中搜索与内存大小相关的信息,比如MemTotal项显示了总的物理内存大小。
cat /proc/meminfo

管道

管道(Pipes)使用竖线|)符号表示。它的作用是将一个命令的输出作为另一个命令的输入。这样,用户可以将多个命令串联起来,形成一个命令管道,从而执行复杂的任务。

1
2
# 查找当前目录下所有.txt文件,并显示这些文件的内容行数:
ls *.txt | xargs wc -l

这里,ls *.txt命令列出所有.txt文件,然后通过管道|将这些文件名传递给xargs命令,xargs命令再将这些文件名作为wc -l命令的参数,wc -l命令计算并显示每个文件的行数。

如何查询系统日志文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 对于使用systemd的Linux系统,journalctl命令是查看系统日志的强大工具。
# 查看所有系统日志
journalctl
# 查看nginx服务器的日志
journalctl -u nginx.service
# 查看系统启动时的日志
journalctl -b
# 查看系统上一次启动时的日志
journalctl -b -1
# 搜索包含特定文本的日志
journalctl | grep "特定文本"
# 对于不直接使用systemd日志或需要查看传统日志文件的情况,可以直接查看/var/log目录下的日志文件。
# 查看系统消息日志(通常是系统级别的警告和错误信息):
cat /var/log/syslog
# 或者在某些系统中可能是
cat /var/log/messages
# 查看认证和授权相关的日志(如SSH登录尝试):
cat /var/log/auth.log
# 或者
cat /var/log/secure
# 查看Web服务器(如Apache或Nginx)的日志(通常位于/var/log/apache2或/var/log/nginx目录):
cat /var/log/apache2/access.log
cat /var/log/nginx/access.log

使用logrotate管理日志文件。/var/log目录下的日志文件可能会随着时间增长而变得非常大。为了管理这些日志文件的大小和数量,大多数Linux系统使用logrotate工具定期轮转、压缩、删除或邮寄旧日志文件。logrotate的配置文件通常位于/etc/logrotate.conf以及/etc/logrotate.d/目录下的文件中。

重定向

重定向(Redirection)允许用户将命令的输出(标准输出或标准错误输出)重定向到文件或其他命令中,或者将文件的内容作为命令的输入。重定向使用大于号(>)和小于号(<)等符号表示。

  • 标准输出重定向(>):将命令的输出重定向到文件中。如果文件已存在,则覆盖原有内容;如果文件不存在,则创建新文件。
  • 标准输出追加重定向(>>):将命令的输出追加到文件的末尾,而不是覆盖原有内容。
  • 标准输入重定向(<):将文件的内容作为命令的输入。
  • 标准错误输出重定向(2>):将命令的错误输出重定向到文件中。
    1
    2
    3
    4
    5
    6
    7
    8
    # 将ls命令的输出(即当前目录下的文件和目录列表)重定向到files.txt文件中。
    ls > files.txt
    # 将文本"Hello, World!"追加到greeting.txt文件的末尾。
    echo "Hello, World!" >> greeting.txt
    # 将files.txt文件的内容作为wc -l命令的输入,计算并显示文件的行数。
    wc -l < files.txt
    # 如果non_existent_file文件不存在,ls命令的错误输出(通常是“No such file or directory”消息)将被重定向到errors.txt文件中。
    ls non_existent_file 2> errors.txt

Linux如何查看文件前5行

1
head -n 5 filename

Linux怎么看进程占用多少内存

1
2
# 实时查看系统的 CPU 使用率、内存使用率、进程信息等。
top [选项]

Linux抓包

功能:tcpdump是一个强大的命令行抓包工具,可以捕获经过网络接口的数据包,并将其显示或保存到文件中。它支持复杂的过滤器和表达式,能够灵活地筛选和捕获所需的数据包。
特点:tcpdump是Linux系统自带的抓包工具之一,使用起来非常方便。它提供了丰富的选项和参数,可以精确控制抓包过程,包括指定网络接口、设置抓包数量、保存抓包数据等。
使用场景:适用于需要深入分析网络流量、诊断网络问题或进行安全审计的场景。

1
sudo tcpdump [ -adeflnNOpqStvx ] [ -c 数量 ] [ -F 文件名 ] [ -i 网络接口 ] [ -r 文件名] [ -s snaplen ] [ -T 类型 ] [ -w 文件名] [表达式 ]

二、常用选项说明
-a:将网络地址和广播地址转变成名字。
-c 数量:指定要抓取的包的数量。
-d:将匹配信息包的代码以人们能够理解的汇编格式给出。
-e:在输出行打印出数据链路层的头部信息,例如源MAC和目标MAC。
-f:将外部的Internet地址以数字的形式打印出来。
-i 网络接口:指定tcpdump需要监听的接口。
-n:对地址以数字方式显式,不进行主机名解析。
-nn:除了-n的作用外,还把端口显示为数值,不进行端口名解析。
-P:指定要抓取的包是流入还是流出的包,可以给定的值为”in”、”out”和”inout”,默认为”inout”。
-q:快速打印输出,即打印很少的协议相关信息,输出行简短。
-s snaplen:设置tcpdump的数据包抓取长度为snaplen,如果不设置默认将会是65535字节。
-t:在输出的每一行不打印时间戳。
-v、-vv、-vvv:分别产生详细程度递增的输出信息。
-w 文件名:将抓包数据输出到文件中而不是标准输出。
-r 文件名:从给定的数据包文件中读取数据。
-X:输出包的头部数据,以16进制和ASCII两种方式同时输出。
-XX:输出包的头部数据,以16进制和ASCII两种方式同时输出,更详细。
-D:列出可用于抓包的接口。
-F:从文件中读取抓包的表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 默认启动
tcpdump
# 默认情况下,tcpdump将监视第一个网络接口(非lo口)上所有流通的数据包。
# 监视指定网络接口的数据包
tcpdump -i eth1
# 如果不指定网卡,默认tcpdump只会监视第一个网络接口,如eth0。
# 截获主机hostname发送的所有数据
tcpdump src host hostname
# 监视所有发送到主机hostname的数据包
tcpdump dst host hostname
# 监视指定主机和端口的数据包
tcpdump tcp port 22 and host hostname
# 抓取包含特定IP的数据包
tcpdump -i eth0 -vnn host 10.84.10.217
# 抓取包含特定端口的数据包
tcpdump -i eth0 -vnn port 22
# 抓取特定协议的数据包
tcpdump -i eth0 -vnn udp
# 将抓取的数据包记录存到文件中
tcpdump -i eth0 -vnn -w /tmp/fil1 -c 100
# 从文件中读取数据包进行分析
tcpdump -i eth0 -vnn -r /tmp/fil1 tcp

tcpdump只能抓取流经本机的数据包。
在使用复杂的表达式时,建议使用单引号将表达式括起来,以防止shell对特殊字符进行错误解析。抓取长度(snaplen)的设置需要根据实际需求进行调整,以避免包截断或处理时间过长。

epoll线程模型

epoll 是一种用于高效处理大规模 I/O 多路复用的 Linux 内核机制,通常用于网络服务器或其他需要处理大量并发 I/O 操作的场景。与传统的 selectpoll 相比,epoll 在处理大量文件描述符(如 socket 连接)时具有显著的性能优势。通过 epoll,我们可以以事件驱动的方式来监控多个文件描述符的状态变化(可读、可写、异常等)。

epoll 工作模型

epoll 工作模式主要有两种:

  1. **LT(Level-Triggered,电平触发)**:这种模式类似于 selectpoll,当文件描述符处于可读或可写状态时,epoll_wait 会不断返回该文件描述符,直到操作完成。因此,开发者在处理事件时需要确保一次性读取所有可用数据。

  2. ET(Edge-Triggered,边沿触发):与 LT 不同,ET 模式只在文件描述符状态从不可用到可用的瞬间触发事件通知。如果数据未读完,而此时不再有新的数据到达,epoll_wait 将不会再次返回该文件描述符。ET 模式通常与非阻塞 I/O 配合使用,以避免发生事件遗漏的问题。

epoll 线程模型

在高并发网络服务器设计中,epoll 通常配合多线程模型来提高并发处理能力。典型的 epoll 线程模型包括:

  1. 单线程 + epoll 模型:这种模型中,主线程通过 epoll_wait 来监听所有 I/O 事件,所有 I/O 操作均由该线程处理。虽然实现简单,但在大规模并发情况下性能有限,因为所有操作都是在单线程中完成的,容易成为瓶颈。

  2. 主线程 epoll + 工作线程池模型

  • 主线程负责通过 epoll_wait 监听所有的 I/O 事件,并将需要处理的事件分发给工作线程池中的线程来处理。
  • 当某个文件描述符可读或可写时,主线程会将其任务分配给线程池的空闲线程进行处理,避免了阻塞主线程。
  • 工作线程负责处理读取、写入等 I/O 操作,一旦操作完成,线程回到线程池等待下一个任务。
  • 这种模型结合了 epoll 的高效事件处理机制与线程池的并行计算能力,能够在大规模并发环境下发挥较高的处理性能。
  1. 多线程 epoll 模型
  • 每个线程维护一个独立的 epoll 实例,处理各自的文件描述符集合。通过合理的负载均衡策略,将连接分配到不同线程的 epoll 实例中,避免某一线程成为性能瓶颈。
  • 这种模型适合 CPU 密集型的场景,因为每个线程都可以独立地进行 I/O 多路复用,从而最大化利用多核处理器的资源。

典型的 epoll + 线程池模型的流程:

  1. 初始化 epoll 实例:在主线程中创建一个 epoll 实例。
  2. 注册文件描述符:将需要监控的 socket 文件描述符通过 epoll_ctl 注册到 epoll 实例中。
  3. 等待事件:主线程调用 epoll_wait 等待文件描述符上发生的事件。
  4. 分发事件:当有事件发生时,将文件描述符及其相关操作分发给线程池中的工作线程。
  5. 处理 I/O:工作线程执行读取、写入等 I/O 操作,并根据需要再次注册或修改文件描述符的监听事件。

这种模型非常适合需要处理大量 I/O 请求且对响应时间要求较高的应用场景,比如网络服务器、即时通讯系统等。

小结

epoll 是 Linux 下高效处理并发 I/O 的利器,其与线程池结合可以实现高性能的网络服务器架构。通过合理的线程模型和 epoll 机制,能够有效解决大规模并发连接下的性能瓶颈问题。