socketchannel是java nio中用于高性能非阻塞客户端通信的核心类,需配合selector、bytebuffer和socketaddress使用;创建后须configureblocking(false),connect()后需finishconnect()完成握手;读写需循环处理并自行解析协议。

Java 中 SocketChannel 是 NIO(非阻塞 I/O)体系下的核心类,用于实现高性能、可扩展的客户端网络通信。它本身不直接“发起连接”或“发送消息”,而是配合 Selector、ByteBuffer 和 SocketAddress 等组件,完成非阻塞式的连接、读写与事件驱动处理。
创建并连接 SocketChannel(非阻塞模式)
客户端使用 SocketChannel.open() 创建通道,默认是阻塞的;要支持异步操作(如配合 Selector),必须调用 configureBlocking(false)。连接过程也变为非阻塞:调用 connect() 后立即返回,需轮询 isConnectionPending() 或监听 SelectionKey.OP_CONNECT 事件确认是否连通。
- 调用
channel.connect(new InetSocketAddress("127.0.0.1", 8080)),若返回true表示已直连成功;若返回false,说明正在连接中,需注册OP_CONNECT到 Selector 等待就绪 - 连接就绪后,必须调用
channel.finishConnect()完成握手,否则后续读写会抛异常
发送数据:用 ByteBuffer 写入并 flush
SocketChannel.write() 不保证一次写完全部字节(可能只写部分),需检查返回值并循环写入,或使用 write(ByteBuffer[]) 批量提交。注意 ByteBuffer 必须处于 flip() 后的读模式(limit 设为数据长度,position 重置为 0)。
- 准备数据:
buffer.clear(); buffer.put("HELLO".getBytes()); buffer.flip(); - 循环写直到
buffer.hasRemaining()为 false:while (buffer.hasRemaining()) { channel.write(buffer); } - 实际项目中建议封装为带状态的写缓冲区(WriteQueue),避免因 TCP 窗口满导致 write() 返回 0 而卡住
接收数据:动态扩容 + 粘包/半包处理
read() 同样可能只读到部分数据,且无法预知消息边界(TCP 是字节流)。必须自行解析协议(如固定长度、分隔符、头部+长度等)。常见做法是分配一个可扩容的 ByteBuffer(如 ByteBuffer.allocateDirect(4096)),读取后根据协议提取完整消息。
- 读取前确保 buffer 处于写模式:
buffer.clear()(或compact()保留未读数据) - 读到 0 表示暂时无数据;读到 -1 表示对端关闭连接,应关闭 channel
- 例如使用“长度头 + 内容”协议:先读 4 字节 int 得到 body 长度,再读指定字节数,不足则暂存等待下次 read
配合 Selector 实现单线程多路复用
这是 SocketChannel 发挥优势的关键:一个线程管理成百上千连接。客户端需将 channel 注册到 selector,并关注 OP_CONNECT、OP_READ、OP_WRITE(仅在写缓冲区满时临时注册)。
- 连接阶段注册
OP_CONNECT;连接成功后取消该兴趣集,改注册OP_READ - 写数据前检查
isWritable(),若不可写(如 socket send buffer 满),注册OP_WRITE,待就绪后再写 - 每次
selector.select()返回后,遍历 selectedKeys,按 key 的 readyOps 分发处理逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











