Java NIO端口映射通过Selector实现非阻塞双向桥接:监听本地端口接收客户端连接,异步连接目标服务器,为每对channel分配独立ByteBuffer并注册OP_READ,利用attach关联配对关系,读到数据后flip写入对端,写满时注册OP_WRITE,对端关闭则清理资源。

用 Java NIO 实现简易端口映射(即端口转发),核心是建立两个通道:一个监听本地端口接收客户端连接,另一个向目标服务器发起连接,再双向桥接双方的读写数据。整个过程不依赖线程阻塞,靠 Selector 驱动事件轮询,适合轻量级、低并发场景。
一、关键组件与设计思路
端口映射本质是“TCP 连接中继”,需维护两组 Channel:
-
LocalServerChannel:绑定本地端口(如
localhost:8080),接受客户端连接请求; -
RemoteSocketChannel:对每个客户端连接,异步连接到目标地址(如
example.com:80); -
双向数据桥接:客户端 ↔ 本机 ↔ 目标服务,需为每对连接分配两个
ByteBuffer(in/out),并注册OP_READ到 selector。
注意:NIO 中不能直接在同一个 channel 上同时读写,需为 client 和 remote 各持一个独立 channel,并分别注册读就绪事件。
二、核心步骤与代码骨架
以下为可运行的最小可行实现(省略异常处理和资源关闭细节,生产环境需补全):
立即学习“Java免费学习笔记(深入)”;
1. 初始化 Selector 和 ServerSocketChannel
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.bind(new InetSocketAddress("localhost", 8080));
server.register(selector, SelectionKey.OP_ACCEPT);
2. 处理 ACCEPT 事件:接受客户端,发起远程连接
- 获取
SocketChannel client,设为非阻塞; - 创建
SocketChannel remote,调用remote.connect(...); - 将
client和remote都注册到 selector,初始只关注OP_READ(若 connect 未完成,先注册OP_CONNECT); - 用
SelectionKey.attach()关联 client ↔ remote 的配对关系(例如用自定义Connection对象存两个 channel 和 buffer)。
3. 处理 CONNECT 事件(针对 remote channel)
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 检查
remote.finishConnect()是否成功; - 成功后,把该 key 的 interest set 改为
OP_READ; - 此时 client 和 remote 都已就绪,可开始数据透传。
4. 处理 READ 事件(双向)
- 从触发事件的 channel(client 或 remote)读取数据到 buffer;
- flip 后写入其配对 channel(client 数据 → remote,remote 数据 → client);
- 若 write 返回 0(底层 TCP 窗口满),需注册
OP_WRITE等待可写; - 读到 -1(对端关闭),应关闭本端 channel 并取消 key。
三、注意事项与简化技巧
缓冲区管理:每个连接配一对 ByteBuffer(如 8KB),复用避免频繁分配;compact() 而非 clear() 处理半包。
连接配对存储:用 SelectionKey.attach(new Connection(client, remote)) 是最轻量方式;也可用 ConcurrentHashMap 存 key → connection 映射。
写就绪处理:NIO write 可能未写完,务必检查返回值;建议在 write 返回值 OP_WRITE,并在下次 OP_WRITE 触发时继续写,完成后取消该 interest。
不支持 UDP:本方案基于 TCP Stream;UDP 映射需用 DatagramChannel + connect() 绑定远端地址,逻辑更简单(无连接状态),但需自行处理丢包/乱序。
四、启动与测试
编译运行后,在终端执行:
curl -v http://localhost:8080/ --proxy-insecure
即可看到请求被转发到目标服务(如你配置的目标是 httpbin.org:80)。可用 telnet localhost 8080 手动发 HTTP 请求验证。
不复杂但容易忽略细节,关键是把连接生命周期(accept→connect→read/write→close)用事件驱动串起来,避免阻塞和资源泄漏。

















