网络编程基础
HTTP/HTTPS 请求流程
DNS 解析
- 当客户端(通常是浏览器)输入 URL(例如
https://www.example.com)时,它首先需要解析出对应的 IP 地址。 - 这个过程涉及 DNS(域名系统),浏览器会查询本地缓存、系统缓存,或者向 DNS 服务器发送请求,最终获取到
www.example.com的 IP 地址(如192.168.1.1)。
建立 TCP 连接,通过 三次握手 与服务器建立 TCP 连接。
客户端向服务器发送 HTTP 请求,主要包括:
- 请求行(Request Line):
GET /index.html HTTP/1.1 - 请求头(Headers):
Host: www.example.comUser-Agent: Mozilla/5.0Accept: text/htmlConnection: keep-alive
- 请求体(Body):仅在
POST、PUT等请求方法时才有,例如 JSON 数据、表单数据等。
服务器解析请求:检查 URL 路由、验证权限、查询数据库、处理业务逻辑、服务器生成 HTTP 响应。
服务器返回 HTTP 响应,主要包括:
- 状态行:
HTTP/1.1 200 OK - 响应头:
Content-Type: text/html; charset=UTF-8Content-Length: 1024Set-Cookie: sessionId=abc123; Path=/
- 响应体(Body):例如 HTML 页面内容、JSON 数据等。
浏览器解析 & 渲染
- 浏览器解析 HTML、CSS、JavaScript,并开始渲染页面。
- 如果遇到额外的资源(如 CSS、JS、图片),浏览器会再次发起 HTTP 请求获取这些资源。
连接关闭
- HTTP/1.1 默认使用 Keep-Alive,可以复用 TCP 连接。
- 如果
Connection: close,则服务器和客户端会进行 四次挥手 关闭 TCP 连接。
用户交互 & 进一步请求,用户可能会点击页面上的链接,触发新的 HTTP 请求,重复上述过程。
HTTPS(HyperText Transfer Protocol Secure)是在 HTTP 的基础上增加了 SSL/TLS 加密,以保证数据的安全性。相比 HTTP,HTTPS 主要增加了 SSL/TLS 握手 过程。
DNS 解析,将域名解析为 IP 地址,和 HTTP 一样。
- 浏览器先查询本地 DNS 缓存
- 若无结果,向系统 DNS 服务器或公共 DNS(如
8.8.8.8)发起查询 - DNS 服务器返回
example.com -> 192.168.1.1
建立 TCP 连接,通过 三次握手 建立 TCP 连接(和 HTTP 相同)。
SSL/TLS 握手(HTTPS 独有),在 TCP 连接建立后,客户端和服务器通过 TLS(Transport Layer Security)/SSL(Secure Sockets Layer) 进行安全通信:
- 客户端发送 ClientHello
- 指定支持的 TLS 版本(如 TLS 1.2 / TLS 1.3)
- 支持的加密算法(Cipher Suites)
- 生成的随机数(用于后续密钥计算)
- 服务器响应 ServerHello
- 确定 TLS 版本
- 选择加密算法
- 生成随机数
- 发送 SSL 证书(包含服务器公钥)
- 客户端验证服务器证书
- 检查证书是否可信(是否由受信 CA 签发)
- 证书是否过期
- 证书的域名是否匹配
- 密钥交换
- 客户端使用服务器公钥加密一个对称密钥(用于数据加密)
- 服务器用自己的私钥解密,获得对称密钥
- 后续通信将使用这个对称密钥进行加密
- 握手完成
- 客户端和服务器发送 Finished 消息,表示握手完成
- 之后的 HTTP 请求/响应都通过 TLS 加密通道传输
发送 HTTPS 请求,请求格式 与 HTTP 相同,但数据已经加密:
GET /index.html HTTP/1.1Host: www.example.comUser-Agent: Mozilla/5.0Accept: text/htmlAuthorization: Bearer token
服务器处理请求
- 解密 HTTPS 请求
- 解析 HTTP 请求
- 处理业务逻辑(如数据库查询、身份验证)
- 生成 HTTP 响应
服务器返回 HTTPS 响应
- 响应数据通过 对称密钥加密 后传输
- 响应格式(加密传输):
HTTP/1.1 200 OKContent-Type: text/htmlSet-Cookie: sessionId=abc123Body: <html>...</html>
浏览器解析 & 渲染
- 浏览器解密 HTTPS 响应,解析 HTML/CSS/JS
- 发现额外资源(如 CSS、JS、图片),再次发起 HTTPS 请求(重复上述流程)
连接关闭
- Keep-Alive 模式:TCP 连接可以复用,减少握手开销
- 关闭连接:若
Connection: close,则通过 四次挥手 关闭 TCP 连接。
Netty
Netty 是一个基于 Java NIO(New I/O)的高性能网络通信框架,主要用于构建高并发、低延迟的网络应用,如 IM(即时通讯)、RPC 框架、游戏服务器、代理服务器等。主要特点如下:
- 异步非阻塞(NIO):基于 Java NIO,使用 Reactor 线程模型,支持高并发请求处理。
- 高性能:相比传统的 Java BIO(阻塞 I/O),Netty 在吞吐量、并发数和延迟等方面表现更优。
- 易用性:提供了丰富的 API,屏蔽了底层 NIO 复杂性,使开发更简单。
- 支持多种协议:支持 HTTP、WebSocket、TCP、UDP、SSL/TLS 等协议,可用于多种场景。
- 内置心跳机制:方便处理长连接应用,如 IM、在线游戏等。
- 内存管理:使用 Pooled ByteBuf 进行高效的内存分配和回收,减少 GC 影响。
- 事件驱动模型:基于 ChannelPipeline 和事件驱动机制,支持灵活的业务扩展。
Netty 核心组件
- Bootstrap & ServerBootstrap(启动器)
Bootstrap:用于客户端ServerBootstrap:用于服务器端
- EventLoopGroup(线程组)
bossGroup:负责处理客户端的连接请求workerGroup:负责处理具体的 I/O 事件(读写等)
- Channel(通道)
- 代表一个网络连接(TCP 连接、UDP 通道等)
- 主要实现类有
NioSocketChannel、NioServerSocketChannel
- ChannelPipeline(通道流水线)
- 负责管理
ChannelHandler,实现数据的处理链
- 负责管理
- ChannelHandler(通道处理器)
- 负责数据的编解码、业务逻辑处理等
- 主要有
ChannelInboundHandler(入站处理器)、ChannelOutboundHandler(出站处理器)
Netty 作为一个高性能的网络通信框架,在 Java 网络编程中表现出了卓越的性能优势。其“快”的核心原因可以归纳为以下几个方面:
基于 NIO 的非阻塞式架构
- Netty 使用 Java NIO(New IO)的非阻塞特性,通过 Selector 实现多路复用,能够有效管理成千上万的连接。相比传统的阻塞式 I/O(如 Java 的 BIO),它减少了线程阻塞带来的性能开销。
- 单线程可以同时管理多个连接(多路复用)。
- 减少了线程上下文切换的开销。
- 提高了资源利用率,降低了系统内存和 CPU 的消耗。
高效的线程模型
- Netty 提供了优化的线程模型,基于 Reactor 模式实现了灵活且高效的线程管理。
- Boss-Worker 模型: BossGroup 负责接收连接,WorkerGroup 负责处理读写事件。
- 支持自定义线程池大小和事件循环组,适应不同的性能需求。
- 减少了线程竞争,提高了并发性能。
零拷贝技术
- Netty 内部实现了高效的零拷贝(Zero Copy)技术,避免了不必要的数据拷贝操作,从而显著提升了性能。
- DirectBuffer: 使用堆外内存直接与操作系统交互,避免了数据从堆内存到内核缓冲区的多次拷贝。
- CompositeByteBuf: 支持多段内存合并,不需要额外的内存拷贝操作。
- 文件传输: 通过 FileRegion 实现零拷贝文件传输。
内存管理优化
- Netty 提供了高效的内存池管理机制,通过 ByteBuf 代替传统的 ByteBuffer。
- 动态扩容:避免了 ByteBuffer 容量不足的麻烦。
- 引用计数:实现了内存的高效复用,降低了 GC 开销。
- 池化:通过内存池分配减少了频繁的内存分配和回收开销。
事件驱动机制
- Netty 基于事件驱动模型(EventLoop),能够高效处理网络事件(如连接、读、写、异常)。
- 单线程事件循环减少了锁竞争。
- 事件机制能够灵活扩展,实现业务逻辑解耦。
高度可扩展的设计
- Netty 提供了灵活的 ChannelPipeline 和 ChannelHandler 机制,使得数据处理流程可以高度定制化。
- 责任链模式:数据流可以经过多个处理器(Handler)逐步处理。
- 增强复用性:不同的业务逻辑可以通过 Handler 复用和组合。
支持 TCP 粘包拆包处理
- Netty 内置了丰富的解码器(Decoder)和编码器(Encoder),能够高效处理 TCP 粘包和拆包问题,提升了数据处理效率。
- LengthFieldBasedFrameDecoder:基于消息长度字段的协议处理。
- LineBasedFrameDecoder:按行分隔的协议解析。
跨平台和多协议支持
- Netty 支持多种传输协议(如 HTTP、WebSocket、TCP、UDP),并在不同的操作系统上进行了深度优化。
- 在 Linux 上利用 epoll 提升性能。
- 提供了灵活的协议实现,适合不同应用场景。
社区支持和持续优化
- Netty 是一个成熟的开源项目,拥有强大的社区支持,代码经过多年的优化,性能和可靠性都非常出色。
- 不断优化的底层实现。
- 丰富的文档和案例。
- 支持最新的网络协议和特性。
HTTP
截至 2024 年,HTTP(HyperText Transfer Protocol)已经发展到 HTTP/3 版本。
HTTP/0.9(1991年)
- 最初的 HTTP 版本,非常简单,仅支持 GET 方法和传输纯文本内容,没有头部信息,也不支持状态码。
HTTP/1.0(1996年)
- 引入了 HTTP 请求和响应的头部,支持更多的 HTTP 方法(如 POST、HEAD)。
- 每次请求都需要新建一个 TCP 连接,传输效率较低。
HTTP/1.1(1997年)
- 改进了性能,支持持久连接(即在同一个 TCP 连接上传输多个请求和响应)。
- 增加了对缓存、压缩、内容协商等功能的支持,提升了性能和扩展性。
HTTP/2(2015年)
- 采用了二进制格式(而非 HTTP/1.1 的纯文本格式),大大提高了传输效率。
- 支持多路复用,允许多个请求和响应并发地在同一连接上传输,减少了延迟和头部开销。
- 支持头部压缩,减少了头部信息带来的开销。
HTTP/3(2018年开始草案,2022年标准化)
- 基于 QUIC 协议(UDP 上的一种传输协议),大幅减少了连接建立时间和网络传输中的延迟问题。
- 相比于 HTTP/2 的 TCP 连接,HTTP/3 的 QUIC 基于 UDP,使得即使网络不稳定也能更好地保持连接,并提升了传输效率。
- 提供了更好的安全性和性能,尤其是在移动网络和无线网络环境下效果更好。
HTTP vs HTTPS
HTTP(HyperText Transfer Protocol)和 HTTPS(HyperText Transfer Protocol Secure)是两种用于网络通信的协议。它们的核心区别在于数据传输的安全性。
安全性
- HTTP 是一种不安全的协议,所有传输的数据都是明文的。任何人都可以在传输过程中截获并读取数据。HTTP 不提供加密、身份验证或数据完整性保护。
- HTTPS 通过使用 SSL(Secure Sockets Layer)或 TLS(Transport Layer Security)证书对数据进行加密,确保传输的数据是安全的。即使数据在传输中被截获,也无法被解读。此外,HTTPS 还通过数字证书来验证服务器的身份,确保数据传输的目标是合法的服务器。
端口号
- HTTP 默认使用端口号 80 进行通信。
- HTTPS 默认使用端口号 443 进行通信。
性能开销
- HTTP 由于不进行加密操作,通信开销较小,性能相对较高。
- HTTPS 在通信过程中需要进行加密和解密操作,涉及 SSL/TLS 握手和证书验证,因此会有一定的性能开销。不过,随着硬件和技术的进步,HTTPS 的性能开销已大幅降低,现代应用中这方面的影响已变得较小。
防止篡改和钓鱼攻击
- HTTP 由于数据是明文传输的,因此容易被中途篡改或截获,也容易被用于钓鱼攻击,因为用户无法确定服务器的真实性。
- HTTPS 通过数字证书验证服务器身份,防止钓鱼网站冒充合法网站。此外,加密确保了数据在传输过程中不会被篡改或伪造。
成本
- HTTP 不需要证书,使用成本为零。
- HTTPS 通常需要购买 SSL/TLS 证书(尽管现在有一些免费证书提供商,如 Let’s Encrypt)。为网站配置 HTTPS 可能需要一些额外的配置工作和成本。
POST vs GET
在 HTTP 协议中,POST 请求和 GET 请求是两种最常用的请求方法。它们之间有几个重要的区别,主要体现在数据传输方式、用途和安全性等方面。
请求的用途
- GET 常用于从服务器获取资源或数据,例如:请求静态内容,如网页、图片、文件等。请求是幂等的,多次发送同样的 GET 请求,结果应该是相同的,不会对服务器的状态产生副作用。
- POST 常用于将数据发送到服务器,通常用于创建或更新资源,例如:提交表单数据、上传文件等需要在服务器端进行数据处理的操作。请求不是幂等的,多次发送 POST 请求可能会导致不同的结果(例如,多次提交相同的表单可能会创建多个记录)。
数据传输方式
- GET 数据通过 URL 的查询字符串传递,也就是通过 URL 的一部分发送给服务器。例如:https://example.com/api?name=John&age=30 URL 的长度有限,通常不能超过 2048 字符,查询字符串通常用于传递少量的非敏感数据。
- POST 数据放在请求体(body)中,而不是通过 URL 传递。因此,POST 请求没有大小限制,能够传递大量数据。请求通常用于提交表单数据、JSON、XML、文件等复杂或大规模数据。
数据的可见性和安全性
- GET 由于数据包含在 URL 中,所有发送的数据都在 URL 上显示。这意味着 GET 请求的数据会出现在浏览器的地址栏中,且可能会被浏览器缓存或记录在浏览历史中。因此,GET 请求不适合传递敏感信息,如密码、信用卡号等。
- POST 数据被放在请求体中,不会显示在 URL 中,因此比 GET 请求更适合传递敏感数据。但是,POST 请求本身并不比 GET 请求更安全,因为数据仍然可以被拦截。为了真正的安全性,应该通过 HTTPS 加密传输数据。
缓存机制
- GET 通常可以被浏览器缓存。服务器也可以对 GET 请求进行缓存,以提高响应速度。
- POST 不会被浏览器自动缓存。POST 请求每次都会重新发送,并且默认情况下不允许缓存。
跨域请求限制
- GET 跨域请求时,GET 请求会有一定的限制,但通常只要服务器支持,跨域 GET 请求可以正常工作。
- POST 跨域 POST 请求需要更多的跨域资源共享(CORS)配置,因为浏览器会对跨域 POST 请求进行更多的检查,以确保安全性。
TCP 三次握手 vs 四次挥手
TCP 三次握手和四次挥手是用于建立和关闭 TCP 连接的两种机制,确保连接的建立与释放可靠和有序。
TCP 是一种面向连接的协议,在两个设备之间传输数据前,需要先建立一个可靠的连接。三次握手是指客户端和服务器在通信前要相互确认准备状态,确保双方都能发送和接收数据。
- 第一次握手,客户端向服务器发送一个 SYN(Synchronize) 包,用来请求建立连接;SYN 包中包含客户端的初始序列号 Seq=X,标记客户端将用于数据传输的序列号;客户端进入 SYN-SENT 状态。
- 第二次握手,服务器收到客户端的 SYN 请求后,向客户端回应一个 SYN-ACK 包,表示接收到请求并同意建立连接。SYN-ACK 包中包含服务器的初始序列号 Seq=Y,同时对客户端的 SYN 包中的序列号 X 做确认 Ack=X+1。服务器进入 SYN-RECEIVED 状态。
- 第三次握手,客户端收到服务器的 SYN-ACK 包后,向服务器发送一个 **ACK(Acknowledgement)**包,确认已收到服务器的响应,并表示可以开始传输数据。ACK 包中包含客户端对服务器序列号的确认 Ack=Y+1。连接建立,客户端进入 ESTABLISHED 状态,服务器也进入 ESTABLISHED 状态。
当数据传输完成后,TCP 需要通过四次挥手来释放连接,确保双方都正确关闭通信。
- 第一次挥手(客户端发送 FIN),当客户端完成数据传输后,它会发送一个 FIN(Finish) 包,表示不再发送数据,但仍然可以接收数据。客户端进入 FIN-WAIT-1 状态。
- 第二次挥手(服务器回应 ACK),服务器收到客户端的 FIN 包后,向客户端发送一个 ACK 包,表示确认已收到客户端的 FIN 请求,但服务器可能还有未发送完的数据。服务器进入 CLOSE-WAIT 状态,客户端进入 FIN-WAIT-2 状态。
- 第三次挥手(服务器发送 FIN),服务器确认所有数据发送完成后,向客户端发送一个 FIN 包,表示也不再发送数据,准备关闭连接。服务器进入 LAST-ACK 状态。
- 第四次挥手(客户端回应 ACK),客户端收到服务器的 FIN 包后,向服务器发送一个 ACK 包,确认收到服务器的 FIN 请求。客户端进入 TIME-WAIT 状态,此时要等待一段时间(通常是 2 倍的最大报文段生存时间),以确保服务器接收到了 ACK 包,然后彻底关闭连接。服务器接收到 ACK 包后,立即进入 CLOSED 状态,连接正式关闭。
四次挥手的目的是确保双方都完成了数据传输,并有序地关闭连接,防止任何数据丢失或被中途截断。
TCP vs UDP
TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Datagram Protocol,用户数据报协议)是两种常用的传输层协议,它们的主要区别在于数据传输的可靠性、顺序性、连接性等方面。
连接与无连接
- TCP 是面向连接的协议。在传输数据之前,TCP 需要建立连接。
- UDP 是无连接的协议。在传输数据之前,UDP 不需要建立连接,直接发送数据。这意味着 UDP 没有建立连接的开销,因此传输速度更快,但可靠性较低。
数据传输的可靠性
- TCP 提供可靠的数据传输。它通过确认机制、超时重传、流量控制和拥塞控制来确保数据包的正确传输和接收。TCP 会确认每个数据包的到达,并且如果某个数据包丢失,TCP 会重新发送。
- UDP 不保证可靠性。UDP 只负责将数据包发送出去,不关心数据包是否到达目的地,也不确认数据的接收情况。没有重传机制,也没有流量或拥塞控制。
有序性
- TCP 保证数据按顺序到达。TCP 协议会给每个数据包分配一个序列号,并确保数据包按照发送的顺序到达接收方。即使某些数据包延迟,TCP 也会重新排序,确保顺序正确。
- UDP 不保证数据包的顺序。由于 UDP 是无连接的协议,数据包的顺序可能会被打乱,接收方无法确保数据包按发送顺序到达。
传输速度
- TCP 由于需要建立连接、确认数据接收、进行流量和拥塞控制,因此 TCP 的传输速度相对较慢。但它的可靠性使其适用于对数据完整性要求较高的场景。
- UDP 由于没有连接建立、确认机制和流量控制,UDP 传输速度较快,适合需要快速传输、但对数据完整性要求不高的场景。
头部开销
- TCP 的头部较大,通常有 20 个字节的头部信息。TCP 头部包含序列号、确认号、窗口大小、标志位等信息,用于实现数据传输的可靠性。
- UDP 的头部较小,只有 8 个字节,包含源端口、目的端口、数据长度和校验和。由于头部较小,UDP 的开销更低,适合传输效率要求高的场景。
流量控制与拥塞控制
- TCP:具备流量控制和拥塞控制功能。TCP 会根据网络状况动态调整传输速率,防止网络拥堵,确保发送方不会过度发送数据导致网络堵塞。
- UDP:没有流量控制和拥塞控制机制。UDP 不关心网络状况,数据包一旦发送,不会进行速率调整,可能导致网络拥塞。
TCP 适用于对数据传输可靠性和顺序性要求较高的场景。例如:
- 网页浏览(HTTP/HTTPS):需要确保所有网页资源完整传输。
- 文件传输(FTP):要求文件完整、准确地到达。
- 电子邮件(SMTP、IMAP):邮件内容必须完整传输。
UDP 适用于对传输速度要求高、但对数据完整性和顺序性要求不高的场景。例如:
- 视频会议、实时流媒体(如 Zoom、YouTube、Twitch):对延迟要求敏感,允许少量数据丢失。
- 在线游戏:游戏中的动作需要快速传递,短暂的数据丢失不会影响整体体验。
- DNS 查询:查询结果通常很小,并且如果丢失可以快速重新发送。
流量模式
- TCP 是面向流的协议,数据作为无边界的字节流传输,发送方和接收方之间的数据传输通过 TCP 管道进行,数据流可以持续不断地发送。
- UDP 是面向消息的协议。每个 UDP 数据包是独立的消息单元,不需要保持数据的持续性,也不会合并数据包。因此,每个数据包都是独立的、完整的。
TIME_WAIT vs CLOSE_WAIT
在网络编程中,TIME_WAIT 和 CLOSE_WAIT 是 TCP 连接关闭时的两种常见状态。如果这些状态出现过多,可能会导致系统资源耗尽或性能下降,因此需要采取适当的措施来进行处理。
TIME_WAIT 状态表示主动关闭连接的一方(通常是客户端)在发送了最后的 ACK 后,需要等待一段时间,以确保远程服务器收到了确认并完成了连接的正常关闭。TIME_WAIT 的存在可以防止旧连接中的数据干扰新的连接。
TIME_WAIT 状态过多的原因
- 在高并发的场景下,客户端频繁建立短连接(比如 HTTP 请求),会导致大量连接进入 TIME_WAIT 状态。
- 当服务器频繁关闭连接时,可能积累大量 TIME_WAIT 状态的连接。
TIME_WAIT 处理方法是增加可用端口数量,操作系统允许分配的临时端口数是有限的,当端口耗尽时,可能会出现连接受限的问题。可以通过修改系统参数来增加可用端口的范围:
# 修改可用端口范围,默认范围一般是 32768-60999
echo "1024 65000" > /proc/sys/net/ipv4/ip_local_port_range
减少 TIME_WAIT 状态的超时时间,TIME_WAIT 状态默认的超时时间是 60 秒(2 个最大段生存时间,2MSL)。可以通过修改系统参数减少 TIME_WAIT 的持续时间:
# 修改 TCP FIN timeout 时间
echo "30" > /proc/sys/net/ipv4/tcp_fin_timeout
启用 TCP reuse 和 TCP recycle(仅适用于客户端场景)
tcp_tw_reuse 允许系统在 TIME_WAIT 状态中重新使用 socket,如果是短时间内连接的客户端,设置这个参数可以让 TIME_WAIT 状态的连接被快速重用。
tcp_tw_recycle(此选项在某些新内核中已被移除)允许更快地回收处于 TIME_WAIT 状态的 socket,但可能对 NAT 和负载均衡下的连接产生副作用。
echo "1" > /proc/sys/net/ipv4/tcp_tw_reuse
# echo "1" > /proc/sys/net/ipv4/tcp_tw_recycle # 不推荐使用,部分新内核不再支持
在客户端与服务器进行频繁短连接时,建议使用连接池来重用连接,减少新建和关闭连接的频率,避免产生过多的 TIME_WAIT 状态。
例如,在数据库、HTTP 客户端等场景下,可以使用现有的连接池工具,如 Apache HttpClient、HikariCP 等。
CLOSE_WAIT 状态表示被动关闭连接的一方(通常是服务器)在收到对方发送的 FIN 后,等待应用程序执行 close() 操作。如果系统中存在大量的 CLOSE_WAIT 状态,意味着服务器的应用程序没有及时释放连接资源。
CLOSE_WAIT 状态过多的原因是应用程序没有正确关闭连接,服务器接收到了客户端的 FIN 包,但应用程序没有调用 close() 函数来关闭连接,导致连接长时间保持在 CLOSE_WAIT 状态。
处理方法是检查应用程序代码,确保正确关闭连接,检查代码中是否有遗漏的 close() 调用,确保在完成数据传输后,应用程序能及时关闭连接。特别是对于使用流的地方,如 InputStream 和 OutputStream,一定要在 finally 块或使用 try-with-resources 来保证连接关闭。
设置合理的网络超时时间,避免长时间等待客户端的数据,从而减少 CLOSE_WAIT 状态的积累。可以通过调整操作系统的超时参数或在程序中设置 socket 读写超时。
socket.setSoTimeout(5000); // 设置读超时时间 5 秒
使用性能分析工具(如 jstack、VisualVM、jProfiler 等)检查应用程序是否存在连接未关闭的现象。可以通过分析线程的状态和堆栈来找出那些导致 CLOSE_WAIT 的代码段。
对称加密 vs 非对称加密
对称加密是指加密和解密使用相同的密钥。这种方式简单高效,但密钥的分发和管理是其主要挑战。
- 单一密钥:加密和解密都使用同一个密钥。
- 加密效率高:算法计算速度快,适合处理大规模数据。
- 安全性依赖密钥管理:如果密钥被泄露,通信内容可能被破解。
常见算法
- DES(数据加密标准):历史较久,安全性较低。
- 3DES:是对DES的改进版,更安全但效率较低。
- AES(高级加密标准):现代加密标准,速度快,安全性高。
应用场景
- 本地数据存储:如文件加密、磁盘加密。
- 通信数据传输:在一些封闭环境中用于保护数据传输(如 VPN)。
非对称加密使用一对密钥:公钥和私钥。公钥加密的数据只能用私钥解密,反之亦然。公钥可公开发布,而私钥需严格保密。
- 密钥对:分为公钥和私钥,互相配合完成加密和解密。
- 计算复杂度高:加解密速度慢,适合小数据量的安全需求。
- 支持身份验证:私钥加密可以验证数据来源。
常见算法
- RSA:常用的非对称加密算法,适用于加密和数字签名。
- ECC(椭圆曲线加密):效率更高,适合移动设备等资源受限的场景。
- DSA(数字签名算法):主要用于数字签名。
应用场景
- 身份认证:如 SSL/TLS 协议中的证书认证。
- 数据加密:对小数据量进行加密。
- 数字签名:验证数据的完整性和来源。
由于对称加密和非对称加密各有优缺点,在实际应用中,通常结合使用两者,称为混合加密。典型场景是 SSL/TLS 协议,使用非对称加密传输对称加密密钥,使用对称加密密钥进行后续数据传输加密。这种方式既能保证通信的安全性,又能提高加密效率,是现代通信加密的主流方法。
粘包 vs 拆包
粘包是网络编程中常见的问题,特别是在使用 TCP协议 进行通信时。粘包是指:多个数据包被粘在了一起,接收端在读取时无法区分数据边界,导致数据混淆。比如,客户端连续发送了两条消息:
[Hello] [World]
但接收端可能一次性收到:
[HelloWorld]
或者:
[Hell] [oWorld]
粘包通常出现在以下场景:
- 使用 TCP(流式协议,不保留消息边界)
- 发送方连续发送多条消息
- 接收方读取数据时未处理边界或协议格式
之所以会出现粘包是因为 TCP 是 面向字节流 的协议:它会把发送方的多个 send 调用合并后一次性发出去(Nagle算法),接收端也可能一次性读取多个消息(因为操作系统缓冲区已聚合)
解决粘包问题必须在协议层面 定义清晰的消息边界,常见做法有:
- 定长协议,每条消息固定长度,比如每次读 100 字节。
- 分隔符协议,每条消息后加特定分隔符,如
\n或|END|。 - 消息头+长度字段协议,在每条消息前加上表示消息长度的字段,例如前 4 字节表示正文长度。
发送数据格式:
[4字节长度][消息体]
如发送字符串 hello,编码为:
[0x00 0x00 0x00 0x05][h e l l o]
接收方先读 4 字节获取消息长度,再读对应长度的内容。
“拆包”是网络通信中与“粘包”相对的问题,特别是在使用 TCP协议 传输数据时。它是指:
一条完整的消息被 TCP 拆成了几部分发送,接收端需要多次读取才能获得完整数据。
客户端发送:
[HelloWorld]
但接收端可能第一次只收到:
[Hell]
第二次再收到:
[oWorld]
也就是说,接收端并不能保证一次 recv() 调用就能读到完整的一条消息。
会出现拆包的原因是因为:TCP 是 流式协议,不保证一次写入的数据在接收端会一次完整接收。拆包通常发生在:
- 消息比较大,被系统缓冲区分段发送
- 网络传输过程中被分片
- 接收端的读取速度或方式不匹配发送端发送方式
解决拆包的方法跟粘包是一样的 —— 定义消息边界或消息长度。常见方式:
- 定长消息,每条消息固定字节数(简单但不灵活)
- 分隔符消息,每条消息后加
\n或其他分隔符,读取时按分隔符拆 - 长度前缀消息(最常见),消息前加个长度字段,接收端就知道每条消息多长,按这个长度去拼完整一条。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)