Java 网络通信底层原理与 Tomcat 通信架构
整体知识体系脑图
Java网络通信底层
│
├── 计算机网络基础
│ ├── OSI七层
│ ├── TCP/IP
│ ├── Socket
│ ├── 内核态/用户态
│ └── DMA
│
├── TCP
│ ├── 三次握手
│ ├── 四次挥手
│ ├── 滑动窗口
│ ├── 拥塞控制
│ ├── 粘包拆包
│ └── TIME_WAIT
│
├── UDP
│ ├── 无连接
│ ├── 不可靠
│ ├── RTP
│ ├── QUIC
│ └── WebRTC
│
├── Java IO
│ ├── BIO
│ ├── NIO
│ ├── AIO
│ ├── Selector
│ └── DirectBuffer
│
├── Linux IO模型
│ ├── select
│ ├── poll
│ ├── epoll
│ ├── Reactor
│ └── Proactor
│
├── Netty
│ ├── EventLoop
│ ├── Pipeline
│ ├── ByteBuf
│ ├── 零拷贝
│ └── 内存池
│
└── Tomcat
├── Connector
├── Acceptor
├── Poller
├── Coyote
├── Catalina
└── Servlet
第一章:网络通信本质
1.1 什么是网络通信
网络通信的本质:
两个进程之间的数据交换
注意:
不是两个机器。
真正通信的是:
进程 <-> 进程
浏览器:
Chrome进程
Tomcat:
Java进程
1.2 浏览器访问 Tomcat 到底发生了什么
当你输入:
http://localhost:8080/index
实际上发生:
浏览器
↓
DNS解析
↓
获取IP
↓
TCP建立连接
↓
发送HTTP请求
↓
网卡
↓
Linux内核
↓
Socket缓冲区
↓
Tomcat NIO线程
↓
Servlet
↓
返回Response
1.3 网络通信本质 = Socket
Socket:
操作系统提供的网络通信抽象
本质:
文件描述符(fd)
Linux:
一切皆文件
所以:
- 磁盘文件
- Socket
- 管道
- 网卡
都对应:
fd
1.4 Socket 本质
创建 Socket:
Socket socket = new Socket("127.0.0.1",8080);
本质:
Java JNI 调用:
socket()
Linux 内核创建:
struct socket
同时:
分配fd
例如:
fd = 5
1.5 内核态与用户态
这是网络编程核心。
应用程序:
用户态
操作系统:
内核态
用户态不能直接操作:
- 网卡
- 内存
- 中断
- DMA
所以必须:
系统调用
例如:
socket.read()
本质:
recv()
CPU切换:
用户态 -> 内核态
这个过程:
上下文切换
成本很高。
1.6 数据为什么能到达 Tomcat
核心流程:
浏览器发送数据
↓
网卡收到二进制数据
↓
DMA复制到内核缓冲区
↓
TCP协议栈处理
↓
放入Socket接收缓冲区
↓
epoll通知Tomcat
↓
Tomcat读取数据
1.7 DMA 是什么
DMA:
Direct Memory Access
传统IO:
磁盘 -> CPU -> 内存
DMA:
磁盘 -> 内存
CPU不参与数据搬运。
所以:
减少CPU消耗
1.8 一次 read() 发生了什么
inputStream.read();
本质:
read系统调用
Linux:
recv()
发生:
用户态buffer
↑
copy
↑
Socket缓冲区
这里有:
数据拷贝
性能损耗巨大。
1.9 零拷贝
传统:
磁盘 -> 内核buffer
内核buffer -> 用户buffer
用户buffer -> socket buffer
socket buffer -> 网卡
4次拷贝。
零拷贝:
sendfile()
直接:
磁盘buffer -> 网卡
Tomcat:
支持sendfile
Netty:
支持零拷贝
第二章:TCP 深度原理
2.1 TCP 为什么可靠
TCP保证:
- 不丢
- 不乱
- 不重复
- 按序
核心:
序列号 + ACK + 重传
2.2 TCP 三次握手
流程:
客户端 服务端
SYN -------->
<-------- SYN + ACK
ACK -------->
2.3 为什么不能两次握手
因为服务端不知道:
客户端是否收到ACK
如果只有两次:
服务端可能误认为连接成功。
2.4 SYN Flood
攻击:
大量发送SYN
不发送ACK
导致:
半连接队列打满
Tomcat无法建立连接。
2.5 半连接队列
收到 SYN:
连接未完成
放入:
SYN Queue
收到 ACK:
进入:
Accept Queue
2.6 四次挥手
FIN
ACK
FIN
ACK
为什么四次?
因为:
TCP是全双工
双方必须分别关闭。
2.7 TIME_WAIT
主动关闭方:
TIME_WAIT
作用:
防止旧包影响新连接
保证最后ACK到达
2.8 为什么会粘包
TCP:
面向字节流
不是消息。
所以:
可能连续发送
接收方:
不知道边界
2.9 如何解决粘包
方案:
固定长度
100字节
分隔符
\n
length字段
消息头 + length
Netty:
LengthFieldBasedFrameDecoder
第三章:Java BIO/NIO
3.1 BIO 模型
while(true){
Socket socket = server.accept();
new Thread(()->{
socket.read();
}).start();
}
问题:
一个连接一个线程
10万连接:
10万个线程
直接炸。
3.2 阻塞本质
socket.read()
线程:
阻塞等待数据
CPU浪费严重。
3.3 NIO 核心
核心:
一个线程管理多个连接
3.4 Selector 本质
Java:
selector.select()
本质:
Linux:
epoll_wait()
3.5 epoll 为什么快
select:
遍历所有fd
epoll:
事件通知
核心:
回调机制
3.6 Reactor 模型
核心:
事件驱动
流程:
连接来了
↓
Reactor监听
↓
事件分发
↓
Handler处理
第四章:Tomcat 网络通信架构(核心)
4.1 Tomcat 架构
Server
└── Service
├── Connector
└── Container
4.2 Connector 核心
Connector负责:
网络通信
包括:
- TCP
- HTTP解析
- Socket管理
4.3 Tomcat NIO 模型
核心线程:
Acceptor
Poller
Worker
4.4 Acceptor
负责:
accept连接
本质:
accept()
4.5 Poller
负责:
epoll_wait
监听:
读写事件
4.6 SocketProcessor
收到事件:
封装任务
交给线程池。
4.7 完整请求流程
浏览器
↓
TCP
↓
Socket
↓
Acceptor
↓
Poller
↓
SocketProcessor
↓
HTTP解析
↓
CoyoteAdapter
↓
Engine
↓
Host
↓
Context
↓
Wrapper
↓
Servlet
4.8 Tomcat 为什么高并发
核心:
NIO
epoll
Reactor
线程池
4.9 Tomcat Reactor 模型
Tomcat:
主从Reactor
主Reactor:
Acceptor
从Reactor:
Poller
Worker:
业务处理
4.10 HTTP 请求解析
Tomcat:
Http11Processor
解析:
RequestLine
Header
Body
4.11 keep-alive
HTTP1.1默认:
长连接
Tomcat:
不会立即关闭Socket。
减少:
TCP握手成本
第五章:Netty 为什么快
5.1 Netty 核心
Reactor + epoll + 零拷贝
5.2 EventLoop
本质:
死循环线程
负责:
- IO
- 任务
- 定时任务
5.3 ByteBuf
替代:
ByteBuffer
优势:
- 动态扩容
- 池化
- 零拷贝
5.4 Netty 如何减少 GC
核心:
内存池
类似:
Redis内存池
避免:
频繁new byte[]
5.5 Netty 如何解决粘包
内置:
LengthFieldBasedFrameDecoder
DelimiterBasedFrameDecoder
第六章:Linux epoll 底层
6.1 select 为什么慢
select:
每次复制fd集合
并且:
线性遍历
复杂度:
O(n)
6.2 epoll 核心
epoll:
红黑树 + 链表
6.3 epoll_create
创建:
eventpoll对象
6.4 epoll_ctl
注册fd:
加入红黑树
6.5 epoll_wait
阻塞等待事件。
有事件:
直接返回就绪链表
无需遍历全部fd。
第七章:百万连接架构
7.1 Redis 为什么快
Redis:
单线程 + epoll
避免:
线程切换
7.2 Kafka 为什么吞吐高
Kafka:
顺序IO
零拷贝
批量发送
7.3 Nginx 为什么抗高并发
核心:
多进程 + epoll
7.4 百万 IM 架构
客户端
↓
LB
↓
Gateway(Netty)
↓
MQ(Kafka)
↓
业务集群
↓
Redis路由
第八章:源码调试
8.1 调试 Tomcat
断点:
NioEndpoint#Poller
观察:
epoll事件处理
8.2 调试 Netty
断点:
NioEventLoop#run
理解:
事件循环
8.3 抓包分析
tcpdump -i eth0 port 8080
Wireshark:
观察:
- SYN
- ACK
- FIN
8.4 jstack 分析线程
jstack pid
分析:
- BLOCKED
- WAITING
- RUNNABLE
最终总结
真正的 Java 高并发本质:
Linux内核 + epoll + Reactor + 线程池
Tomcat 本质:
Java版 Reactor 网络框架
Netty 本质:
更强大的 Reactor 框架
高并发核心:
减少线程
减少阻塞
减少拷贝
减少上下文切换
真正深入 Java 网络底层后,你会发现:
高级框架只是Linux网络模型的封装。
更多推荐

所有评论(0)