从网卡到 Socket:再探 Linux 网络协议栈与 iptables 数据包处理流程

部署一个 Java 服务后,我们通常会用下面的命令确认端口是否监听:

bash 复制代码
ss -lntp | grep ':8080 '

如果看到:

text 复制代码
LISTEN 0 128 0.0.0.0:8080

说明应用已经创建了监听 Socket。

但这并不意味着外部客户端一定能访问它。实际工作中经常出现这样的情况:

  • Java 进程正在运行;
  • 8080端口已经监听;
  • 云平台安全组已经放行;
  • 服务器本机访问正常;
  • 远程客户端访问却一直超时;
  • Java应用日志中没有任何请求记录。

这是因为,外部数据不会直接进入Java程序。它需要先经过服务器网卡、Linux内核网络协议栈、路由判断和Netfilter规则,最后才有机会进入Socket。

本文从应用程序最熟悉的Socket开始,逐层向下解释Linux网络处理过程,再沿着数据包的实际路径,说明iptables究竟在什么位置发挥作用。

文中的路径、地址和端口均为示例。

一、先建立整体认识:应用程序通过Socket使用网络

Java程序不会直接操作网卡,也不会自己实现TCP协议。

例如,一个简单的TCP服务端:

java 复制代码
ServerSocket serverSocket = new ServerSocket(8080);

while (true) {
    Socket socket = serverSocket.accept();
    System.out.println(
        "客户端已连接:" + socket.getRemoteSocketAddress()
    );
}

这段代码主要完成两件事:

  1. 请求操作系统创建一个监听8080端口的Socket;
  2. 调用accept(),等待操作系统交付已经建立的TCP连接。

TCP三次握手、报文确认、丢包重传和拥塞控制等工作,全部由Linux内核完成。

应用程序与网络之间的关系可以表示为:

text 复制代码
用户空间
┌──────────────────────────────┐
│ Java应用程序                  │
│ accept、read、write           │
└──────────────┬───────────────┘
               │ 系统调用
               ▼
内核空间
┌──────────────────────────────┐
│ Socket层                     │
├──────────────────────────────┤
│ TCP、UDP传输层               │
├──────────────────────────────┤
│ IP、路由、Netfilter          │
├──────────────────────────────┤
│ 链路层、网卡驱动             │
└──────────────┬───────────────┘
               │
               ▼
          物理或虚拟网络

接收数据时,数据大致从下向上流动:

text 复制代码
网卡
 ↓
链路层
 ↓
IP、路由和Netfilter
 ↓
TCP或UDP
 ↓
Socket
 ↓
应用程序

发送数据时,方向相反:

text 复制代码
应用程序
 ↓
Socket
 ↓
TCP或UDP
 ↓
IP、路由和Netfilter
 ↓
网卡

这里需要特别强调:

TCP和UDP本身就是Linux内核网络协议栈的一部分。

不能把"Linux内核网络协议栈"和"TCP/UDP处理"理解成两个独立系统。Linux内核网络协议栈是一个整体,TCP/UDP只是其中的传输层部分。

二、iptables并不直接处理数据包

日常交流中,我们经常说"iptables把数据包拦截了"。从使用角度看没有问题,但从实现上说,真正处理数据包的是Linux内核中的 Netfilter。

两者的分工是:

组件 所在空间 职责
iptables 用户空间 添加、删除、查看防火墙和NAT规则
Netfilter 内核空间 在数据路径上匹配并执行规则

当我们执行:

bash 复制代码
iptables -A INPUT \
  -p tcp --dport 8080 \
  -j ACCEPT

大致发生以下过程:

  1. iptables解析命令;
  2. iptables把规则提交给内核;
  3. 内核保存这条规则;
  4. iptables命令执行完成并退出;
  5. 后续数据包经过Netfilter时,由内核匹配这条规则。

因此,iptables不是一个必须持续运行的防火墙进程。

更准确的说法应该是:

管理员通过iptables配置规则,Linux内核中的Netfilter负责执行规则。

一些较新的系统可能使用nftables作为底层实现,iptables命令则工作在兼容模式下。本文主要讨论经典的iptables规则模型和Netfilter数据路径。

三、Netfilter在网络协议栈中设置了五个关键位置

Netfilter在数据包经过内核网络路径时,设置了多个钩子点。iptables中最常见的五条链与这些处理位置相对应:

text 复制代码
PREROUTING
INPUT
FORWARD
OUTPUT
POSTROUTING

它们分别回答五个问题:

链 处理时机 主要解决的问题
PREROUTING 数据进入服务器后、路由判断前 是否需要修改目的地址
INPUT 确认目标是本机后 是否允许交给本机协议栈和Socket
FORWARD 确认需要经过本机转发后 是否允许转发到其他主机或容器
OUTPUT 本机产生数据后 是否允许本机程序向外发送
POSTROUTING 路由完成、数据离开前 是否需要修改源地址

不是每个数据包都会依次经过全部五条链。不同方向的数据有不同路径。

最重要的三条路径是:

text 复制代码
外部访问宿主机:
PREROUTING → INPUT → TCP/UDP → Socket

外部流量经过本机转发:
PREROUTING → FORWARD → POSTROUTING

本机应用访问外部:
Socket → TCP/UDP → OUTPUT → POSTROUTING

下面沿着这三条路径逐层展开。

四、第一条路径:外部客户端访问宿主机应用

假设Java应用监听:

text 复制代码
0.0.0.0:8080

客户端访问:

text 复制代码
服务器IP:8080

数据包在服务器中的典型路径是:

text 复制代码
网卡接收数据
    ↓
链路层处理
    ↓
IP层处理
    ↓
PREROUTING
    ↓
路由判断
    ↓
目标地址属于本机
    ↓
INPUT
    ↓
TCP或UDP处理
    ↓
查找Socket
    ↓
进入Socket接收队列
    ↓
应用程序读取

1. 数据先到达服务器网卡

客户端发送的是网络数据。以TCP为例,一个数据帧通常包含:

text 复制代码
以太网头
IP头
TCP头
应用数据

服务器网卡接收到数据帧后,通过DMA等机制将数据送入内存。网卡驱动和Linux内核开始处理这份数据。

在Linux内核中,网络数据通常由一种称为sk_buff,简称skb的数据结构描述。

此时,Java程序还没有看到这个请求。

2. 内核进入IP接收路径

内核完成必要的链路层处理后,会识别数据帧承载的是IPv4、IPv6还是其他协议。

对于IPv4数据包,内核会检查:

  • IP头是否合法;
  • 目的IP是什么;
  • 是否需要转发;
  • 上层协议是什么;
  • 数据包是否可以继续处理。

IP头中的协议字段可以说明上层协议。例如:

协议 IP协议号
ICMP 1
TCP 6
UDP 17

虽然内核已经知道数据包承载的是TCP还是UDP,但此时还不会立即把它交给TCP或UDP模块,因为还要先经过Netfilter和路由判断。

3. 数据进入PREROUTING

PREROUTING的含义是:

数据包已经进入服务器,但内核还没有完成最终路由判断。

这里经常执行DNAT,也就是修改目的地址。

例如:

bash 复制代码
iptables -t nat -A PREROUTING \
  -p tcp --dport 18080 \
  -j DNAT --to-destination 172.20.0.10:8080

数据包原来访问:

text 复制代码
宿主机IP:18080

经过规则处理后,目的地址变成:

text 复制代码
172.20.0.10:8080

因为路由判断发生在后面,所以目的地址发生变化后,数据包接下来可能不再进入INPUT,而是进入FORWARD。

4. 内核进行路由判断

Linux根据数据包当前的目的地址查询路由信息,并决定:

text 复制代码
目的地址属于本机
        ↓
      INPUT

目的地址不属于本机,需要转发
        ↓
      FORWARD

因此,INPUT和FORWARD的区别并不是由端口决定的,而是由路由判断结果决定的。

访问宿主机上直接运行的Java服务,通常进入INPUT。

访问经过DNAT映射的Docker容器,通常进入FORWARD。

5. 本机流量经过INPUT

假设目的地址属于本机,数据包进入INPUT。

放行8080端口的规则可能是:

bash 复制代码
iptables -A INPUT \
  -p tcp --dport 8080 \
  -j ACCEPT

如果规则是:

bash 复制代码
iptables -A INPUT \
  -p tcp --dport 8080 \
  -j DROP

数据包会被静默丢弃。

这时:

  • 数据已经到达服务器;
  • 网卡已经接收到数据;
  • IP层已经解析了数据;
  • TCP模块还没有正常接收这个报文;
  • 监听8080的Java程序看不到请求;
  • 应用访问日志不会出现记录;
  • 客户端通常不断重传,最后连接超时。

如果使用:

bash 复制代码
iptables -A INPUT \
  -p tcp --dport 8080 \
  -j REJECT --reject-with tcp-reset

服务器会明确拒绝TCP请求。客户端通常会更快收到连接被拒绝的信息。

DROP与REJECT的典型区别如下:

动作 服务端行为 客户端常见表现
DROP 静默丢弃 等待重传,最后超时
REJECT 返回拒绝信息 较快失败
ACCEPT 继续交给后续协议栈 继续进行TCP/UDP处理

客户端出现Connection refused时,也可能是端口没有程序监听。因此,不能只根据一个错误信息判断一定是iptables导致的。

6. INPUT放行后才进入TCP或UDP处理

INPUT放行后,IP层根据协议字段,把数据交给TCP、UDP或其他协议模块。

如果是TCP,进入TCP处理。

如果是UDP,进入UDP处理。

传输层模块会根据以下信息查找通信状态和Socket:

text 复制代码
源IP
源端口
目的IP
目的端口
传输层协议

因此,更准确的数据路径是:

text 复制代码
Linux内核网络协议栈内部:

IP接收
  ↓
PREROUTING
  ↓
路由判断
  ↓
INPUT
  ↓
TCP或UDP
  ↓
Socket

TCP和UDP并不位于Linux内核网络协议栈之外,它们就是协议栈的一部分。

五、TCP三次握手是如何经过Netfilter的?

假设客户端向服务器8080端口建立TCP连接。

第一次握手:

text 复制代码
客户端发送SYN

服务端接收路径:

text 复制代码
网卡
 ↓
PREROUTING
 ↓
路由判断
 ↓
INPUT
 ↓
TCP模块

如果INPUT允许,TCP模块查找监听8080的Socket,然后准备返回SYN-ACK。

第二次握手:

text 复制代码
服务端发送SYN-ACK

发送路径:

text 复制代码
TCP模块
 ↓
IP层
 ↓
OUTPUT
 ↓
POSTROUTING
 ↓
网卡
 ↓
客户端

第三次握手:

text 复制代码
客户端发送ACK

再次进入:

text 复制代码
PREROUTING
 ↓
INPUT
 ↓
TCP模块

三次握手完成后,内核会把连接放入监听Socket的已完成连接队列。

随后,Java程序中的:

java 复制代码
serverSocket.accept();

才能获得这个连接。

所以:

text 复制代码
网卡收到SYN

不等于:

text 复制代码
Java的accept()立即返回

在两者之间,还存在PREROUTING、路由判断、INPUT、TCP状态机和Socket队列等处理环节。

如果SYN在INPUT处被DROP,TCP模块无法正常处理握手,应用程序也无法感知这次连接尝试。

六、UDP数据是如何到达Socket的?

UDP接收路径的前半部分与TCP相同:

text 复制代码
网卡
 ↓
链路层
 ↓
IP层
 ↓
PREROUTING
 ↓
路由判断
 ↓
INPUT
 ↓
UDP模块
 ↓
Socket接收队列
 ↓
应用程序

UDP没有TCP中的:

  • 三次握手;
  • 序号和确认;
  • 自动重传;
  • 有序字节流;
  • 四次挥手。

INPUT放行后,UDP模块根据目的IP和目的端口查找对应Socket,然后把完整数据报放入Socket接收队列。

假设Java程序等待UDP报文:

java 复制代码
DatagramSocket socket = new DatagramSocket(9000);

byte[] buffer = new byte[1024];
DatagramPacket packet =
    new DatagramPacket(buffer, buffer.length);

socket.receive(packet);

如果iptables配置:

bash 复制代码
iptables -A INPUT \
  -p udp --dport 9000 \
  -j DROP

数据报在进入UDP模块之前被丢弃,receive()会继续等待。

客户端发送方法返回成功,也不能证明服务端已经收到。它通常只说明数据已经交给客户端本机内核。

因此,UDP应用通常需要在业务协议中自行设计:

  • 请求编号;
  • 响应报文;
  • 超时;
  • 重试;
  • 去重;
  • 幂等处理。

七、第二条路径:Docker端口映射为什么走FORWARD?

假设Docker发布了一个端口:

text 复制代码
宿主机18080 → 容器172.20.0.10:8080

客户端访问:

text 复制代码
宿主机IP:18080

数据进入宿主机后,大致经历:

text 复制代码
客户端请求
    ↓
宿主机网卡
    ↓
PREROUTING
    ↓
DNAT:目的地址修改为172.20.0.10:8080
    ↓
路由判断
    ↓
目标是容器地址
    ↓
FORWARD
    ↓
POSTROUTING
    ↓
Docker网桥
    ↓
容器网络命名空间
    ↓
容器中的Socket

这里的关键是DNAT。

数据刚进入宿主机时,目的地址是:

text 复制代码
宿主机IP:18080

Docker创建的NAT规则把它修改为:

text 复制代码
172.20.0.10:8080

随后内核进行路由判断,发现新的目的地址不是宿主机,而是Docker网络中的容器,于是数据走FORWARD。

这就解释了一个常见现象:

INPUT链没有放行18080,但Docker映射的18080仍然可能被外部访问。

这不是iptables失效,而是该流量没有经过宿主机的INPUT链。

排查Docker端口时,通常要检查:

bash 复制代码
iptables -t nat -L DOCKER -n -v --line-numbers
iptables -L DOCKER-USER -n -v --line-numbers
iptables -L FORWARD -n -v --line-numbers

它们分别关注:

位置 作用
nat/DOCKER 把宿主机端口转换成容器地址和端口
DOCKER-USER 执行用户自定义的容器转发控制
FORWARD 决定数据是否允许经过宿主机转发

具体规则结构可能随Docker版本、网络模式和防火墙后端发生变化,应以系统实际规则为准。

八、第三条路径:本机应用主动访问外部服务

假设Java应用连接远程数据库:

text 复制代码
Java应用 → 数据库IP:5432

数据由本机应用产生,典型路径是:

text 复制代码
Java调用connect()
        ↓
Socket层
        ↓
TCP模块创建连接状态并生成SYN
        ↓
IP层和路由处理
        ↓
OUTPUT
        ↓
POSTROUTING
        ↓
网卡发送
        ↓
远程数据库

这种流量不是从外部网卡进入的,因此不会经过本机的PREROUTING和INPUT。

OUTPUT控制本机产生的流量

例如:

bash 复制代码
iptables -A OUTPUT \
  -p tcp \
  -d 192.0.2.30 \
  --dport 5432 \
  -j DROP

即使数据库正常运行、地址正确、Java代码也没有问题,连接仍可能超时,因为SYN在离开服务器之前被OUTPUT丢弃。

对Java服务而言,下列通信都可能受到OUTPUT规则影响:

  • 访问数据库;
  • 访问Redis;
  • 注册到Nacos;
  • 连接Kafka;
  • 调用其他微服务;
  • 访问外部模型接口。

POSTROUTING处理即将离开的数据

数据完成路由选择后,在离开本机前经过POSTROUTING。

这里经常执行:

  • SNAT;
  • MASQUERADE;
  • 部分数据包标记处理。

例如容器访问外部网络:

bash 复制代码
iptables -t nat -A POSTROUTING \
  -s 172.20.0.0/16 \
  ! -o docker0 \
  -j MASQUERADE

容器使用内部地址,外部网络通常没有返回该内部网段的路由。MASQUERADE会把源地址转换成宿主机出口地址,返回数据再根据连接跟踪记录交回容器。

九、理解"链"和"表"的区别

学习iptables时,经常会混淆两组概念。

第一组是:

text 复制代码
PREROUTING
INPUT
FORWARD
OUTPUT
POSTROUTING

第二组是:

text 复制代码
raw
mangle
nat
filter
security

前者表示数据包经过的处理位置,后者表示规则的主要用途。

概念 回答的问题
Hook或链 数据包在什么阶段被检查?
表 这组规则主要用于什么目的?
规则 匹配什么数据,执行什么动作?

常见表的用途如下:

表 主要用途
raw 对连接跟踪等进行特殊控制
mangle 修改标记或部分数据包属性
nat DNAT、SNAT、MASQUERADE
filter ACCEPT、DROP、REJECT
security 与安全模块相关的额外控制

例如:

bash 复制代码
iptables -t nat -A PREROUTING ...

表示:

在PREROUTING阶段执行地址转换规则。

而:

bash 复制代码
iptables -t filter -A INPUT ...

表示:

在INPUT阶段执行访问控制规则。

不同表可以存在同名链,但它们属于不同规则集合。并不是每张表都拥有全部五条链。

十、连接跟踪与TCP状态有什么区别?

Linux Netfilter通常会使用conntrack记录通信关系。

常见连接跟踪状态包括:

text 复制代码
NEW
ESTABLISHED
RELATED
INVALID

而TCP协议栈自身维护的状态包括:

text 复制代码
LISTEN
SYN_SENT
SYN_RECV
ESTABLISHED
FIN_WAIT
CLOSE_WAIT
TIME_WAIT

这两套状态不能直接等同。

状态体系 维护者 用途
TCP状态 TCP协议栈 管理TCP连接生命周期
conntrack状态 Netfilter连接跟踪模块 对数据包进行状态化识别

例如:

bash 复制代码
iptables -A INPUT \
  -m conntrack \
  --ctstate ESTABLISHED,RELATED \
  -j ACCEPT

这条规则表示:

允许属于已有通信或关联通信的数据包进入。

它并不是单纯查询某个TCP Socket是否处于ESTABLISHED状态。

UDP为什么也有ESTABLISHED?

UDP本身没有建立连接的握手过程,但conntrack可以根据五元组维护一段时间内的UDP通信记录:

text 复制代码
源IP
源端口
目的IP
目的端口
协议

当内核观察到反方向数据后,可以把这条UDP通信记录视为双向通信,并标记为ESTABLISHED。

这是Netfilter的连接跟踪状态,不表示UDP协议真的建立了TCP式连接。

十一、NAT为什么需要连接跟踪?

假设存在端口映射:

text 复制代码
宿主机18080 → 容器8080

一条新通信的首个数据包到达后,内核根据NAT规则确定转换关系,并把它与conntrack记录关联起来。

处理过程可以简化为:

text 复制代码
首个数据包
    ↓
匹配NAT规则
    ↓
确定地址和端口映射
    ↓
关联到conntrack记录
    ↓
后续同一通信沿用映射

返回方向的数据也根据连接跟踪记录完成反向转换。

例如:

text 复制代码
客户端 → 宿主机18080

被转换为:

text 复制代码
客户端 → 容器8080

容器响应时,内核会完成相应的反向转换,让客户端仍然认为自己在与宿主机18080通信。

NAT通常在新通信的首个数据包上确定映射关系,但后续数据包仍然会经过相关的Netfilter路径。它们并不会绕过防火墙。

十二、为什么新连接失败,已有连接却还能使用?

假设INPUT链中先放行已有通信:

bash 复制代码
iptables -A INPUT \
  -m conntrack \
  --ctstate ESTABLISHED,RELATED \
  -j ACCEPT

然后再拒绝8080:

bash 复制代码
iptables -A INPUT \
  -p tcp --dport 8080 \
  -j DROP

已有连接的数据包先匹配ESTABLISHED规则,因此能够继续传输。

新的TCP SYN不属于已有连接,会继续向后匹配,最终被DROP。

这时就可能出现:

  • 已有SSE连接继续输出;
  • 已有WebSocket继续工作;
  • 新客户端无法连接;
  • 运维人员误以为规则没有生效。

如果DROP规则放在状态放行规则前面,并且匹配所有到达8080的数据包,那么已有连接也可能中断。

所以分析iptables不能只看"有没有某条规则",还要看:

  1. 规则属于哪条链;
  2. 规则位于什么顺序;
  3. 数据是否满足匹配条件;
  4. 数据处于什么conntrack状态;
  5. 规则计数是否增长。

十三、为什么客户端超时,应用日志却什么都没有?

如果数据包在INPUT处被DROP:

text 复制代码
客户端请求
    ↓
服务器网卡
    ↓
IP和Netfilter处理
    ↓
INPUT规则DROP
    ↓
处理结束

后面的TCP/UDP模块和Socket没有获得这个报文。

应用程序自然不会产生:

  • HTTP访问日志;
  • Controller日志;
  • 业务异常;
  • 调用链记录。

TCP服务的accept()可能继续等待,UDP服务的receive()也可能继续阻塞。

因此,当出现:

text 复制代码
客户端连接超时
+
应用没有任何请求日志

应当优先确认请求是否真正到达应用,而不是立即修改业务代码。

十四、沿着数据路径逐层排查

面对"端口无法访问"问题,可以按照数据包的真实路径逐层排查。

第一层:进程是否存在

bash 复制代码
systemctl status example-app --no-pager -l

或者:

bash 复制代码
ps -ef | grep java

这一层只能证明进程状态,不能证明端口和业务正常。

第二层:Socket是否监听

bash 复制代码
ss -lntp | grep ':8080 '

重点观察监听地址。

text 复制代码
127.0.0.1:8080

通常只允许本机回环访问。

text 复制代码
0.0.0.0:8080

表示监听所有本机IPv4地址。

text 复制代码
192.0.2.20:8080

表示只监听指定本地地址。

iptables放行不会改变Socket绑定范围。

第三层:本机是否可以访问

bash 复制代码
curl -v \
  --connect-timeout 3 \
  --max-time 10 \
  http://127.0.0.1:8080/health

如果本机也失败,应优先检查:

  • 应用是否真正启动完成;
  • 端口是否正确;
  • 接口路径是否正确;
  • 应用是否只支持HTTPS;
  • 业务依赖是否异常。

如果本机成功、远程失败,再检查外部网络和防火墙。

第四层:数据是否到达服务器

在服务端执行:

bash 复制代码
tcpdump -ni any tcp port 8080

同时从远程客户端发起请求。

抓包结果 排查方向
完全看不到SYN 安全组、上游路由、客户端出口
看到重复SYN,没有SYN-ACK INPUT、监听状态、响应路径
看到SYN和SYN-ACK,没有最终ACK 客户端侧或回程链路
三次握手完成,没有HTTP响应 应用处理或应用层协议
请求和响应都能看到 网络基本连通,检查客户端或代理

tcpdump的捕获位置与Netfilter钩子并不完全相同。抓到数据只能证明数据经过了某个抓包点,不能单独证明它已经通过INPUT并进入应用。

第五层:检查iptables规则和计数

宿主机服务重点检查:

bash 复制代码
iptables -L INPUT -n -v --line-numbers

Docker或转发流量重点检查:

bash 复制代码
iptables -L FORWARD -n -v --line-numbers
iptables -L DOCKER-USER -n -v --line-numbers

NAT映射检查:

bash 复制代码
iptables -t nat -L PREROUTING -n -v --line-numbers
iptables -t nat -L DOCKER -n -v --line-numbers

重点观察:

  • 是否存在匹配端口的规则;
  • ACCEPT是否位于DROP或REJECT之前;
  • 来源IP是否满足规则;
  • 请求是否走INPUT还是FORWARD;
  • 测试请求发生时,哪条规则的计数增长。

如果某条DROP规则计数随着测试增长,就说明请求命中了它。

第六层:确认应用是否收到请求

如果端口监听、TCP握手和防火墙都正常,再检查:

  • 应用访问日志;
  • URL路径;
  • HTTP方法;
  • 请求头和请求体;
  • TLS和证书;
  • 认证信息;
  • 线程池与连接池;
  • 数据库、Redis、Nacos等依赖。

这样能够避免网络尚未打通时,就在业务代码中反复寻找问题。

十五、把整套逻辑浓缩成三条主路径

外部客户端访问宿主机服务:

text 复制代码
网卡
  ↓
PREROUTING
  ↓
路由判断
  ↓
INPUT
  ↓
TCP/UDP
  ↓
Socket
  ↓
应用程序

外部流量经宿主机转发到容器或其他主机:

text 复制代码
网卡
  ↓
PREROUTING
  ↓
路由判断
  ↓
FORWARD
  ↓
POSTROUTING
  ↓
容器或其他主机

本机应用访问外部服务:

text 复制代码
应用程序
  ↓
Socket
  ↓
TCP/UDP
  ↓
IP和路由
  ↓
OUTPUT
  ↓
POSTROUTING
  ↓
网卡

理解这三条路径后,很多现象都可以得到解释:

  • 应用端口监听,但远程访问超时:检查INPUT及上游网络;
  • Docker发布端口不受INPUT规则限制:流量主要经过FORWARD;
  • Java连接数据库超时:还要检查OUTPUT和返回路径;
  • 客户端超时但应用无日志:请求可能在到达Socket前就被丢弃;
  • 已有长连接正常,新连接失败:检查conntrack状态与规则顺序;
  • 修改NAT规则后部分连接仍沿用旧映射:已有连接可能还关联旧的conntrack记录。

iptables不会出现在Java的业务代码中,却能决定数据包是否有机会到达Java Socket。严谨地说,iptables负责配置规则,Netfilter在Linux内核网络路径中执行规则;TCP、UDP、IP、Socket和Netfilter都属于Linux内核网络处理体系的不同部分。

排查网络问题时,不要从"应用是否报错"单点判断,而应沿着数据的实际路径逐层确认:

text 复制代码
进程
  ↓
监听Socket
  ↓
本机访问
  ↓
外部数据是否到达
  ↓
Netfilter是否允许
  ↓
TCP/UDP是否完成处理
  ↓
请求是否进入应用
  ↓
业务是否正确响应

当每一层都有证据,连接超时、端口不通和容器转发异常就不再是一团模糊的"网络问题",而会变成一个可以逐步定位、验证和解决的具体问题。

相关推荐
wuminyu1 小时前
stackChunkOop内部结构与GC屏障的配合工作原理剖析
java·linux·c语言·jvm·c++
承渊政道1 小时前
Linux网络学习【TCP Socket编程实战:connect的断线重连与客户端访问Linux验证】
linux·网络·学习·ubuntu·tcp·socket编程
sunoo-2291 小时前
嵌入式Linux驱动学习笔记(第3-6天):从misc封装、platform总线到设备树、中断与单总线外设实战
linux·arm开发·驱动开发·笔记·单片机·学习
雪落漂泊1 小时前
Linux基础开发工具(一)软件包管理器与Vim编辑器篇
linux·运维·服务器
崽崽..1 小时前
【Linux】网络编程的调试工具以及连接失败的具体排查方法
linux·网络
承渊政道1 小时前
CentOS 7单机部署Elasticsearch 7.17.29:从启动报错、CRUD验证到固定公网地址的完整过程
linux·elasticsearch·centos·内网穿透·cpolar·单机部署
ESDWAN1 小时前
海外股票网络环境优化与交易场景应用:从行情获取到交易系统稳定性设计
开发语言·网络·php
zxanz11 小时前
SSL 证书申请中,服务器验证跟 DNS 解析验证有什么区别?
服务器·网络协议·ssl
进击的荆棘1 小时前
Linux系统——库的原理与制作之ELF文件的形成与加载的过程(上)
linux·elf·动静态库