java nio 简介及高并发网络编程实现
java nio
nio(non-blocking i/o,非阻塞 i/o)是 java 在 jdk 1.4 中引入的一套新的 i/o api,旨在解决传统 i/o(即 bio,阻塞 i/o)在高并发场景下的性能和扩展性不足的问题。
nio 的核心特点
非阻塞 i/o:支持非阻塞模式,可以让线程不必一直等待 i/o 操作完成,从而提高系统资源利用率。
基于缓冲区(buffer):数据的读写通过缓冲区进行,而不是直接通过流(stream)。
选择器(selector):通过一个线程管理多个通道(channel),极大地提升了高并发场景下的扩展性和效率。
多路复用:通过 selector 机制可以同时监控多个通道的状态(如连接就绪、读数据就绪等)。
bio(阻塞 i/o)与 nio(非阻塞 i/o) 对比
| 特性 | bio | nio |
|---|---|---|
| i/o 模式 | 阻塞,线程会等待 i/o 完成 | 非阻塞,线程无需等待 i/o 完成 |
| 线程模型 | 每个连接一个线程 | 一个线程管理多个连接 |
| 适用场景 | 低并发、简单场景 | 高并发、网络编程场景 |
| 性能 | 线程资源成本高,扩展性差 | 更高效的资源利用,扩展性更好 |
nio 的核心组件
1.channel(通道)
类似流(stream),但 channel 同时支持读和写。
常见的 channel:socketchannel、serversocketchannel、datagramchannel、filechannel。
2.buffer(缓冲区)
数据读写通过 buffer 进行。
常见的缓冲区:bytebuffer、charbuffer、intbuffer 等。
3.selector(选择器)
核心组件,用于监听多个通道的事件,如连接就绪、读就绪、写就绪等。
通过多路复用机制实现一个线程管理多个通道。
4.selectionkey
表示通道和选择器的注册关系,包含通道的事件类型(如读、写、连接等)。
java nio 网络高并发编程示例
场景描述
服务器端:监听客户端请求,接收数据并返回信息。
客户端:连接服务器,发送数据并接收响应。
服务器端代码
import java.io.ioexception;
import java.net.inetsocketaddress;
import java.net.socketexception;
import java.nio.bytebuffer;
import java.nio.channels.*;
import java.util.iterator;
public class nioserver {
public static void main(string[] args) {
try {
// 1. 创建 serversocketchannel 用于监听客户端连接
serversocketchannel serverchannel = serversocketchannel.open();
serverchannel.bind(new inetsocketaddress(8080));
serverchannel.configureblocking(false); // 设置为非阻塞模式
// 2. 创建 selector 并注册 serversocketchannel,监听 accept 事件
selector selector = selector.open();
serverchannel.register(selector, selectionkey.op_accept);
system.out.println("nio server started on port 8080...");
while (true) {
// 3. 检查是否有事件发生,阻塞等待
if (selector.select() == 0) {
continue; // 如果没有事件就绪,继续循环
}
// 4. 获取就绪的事件集合
iterator<selectionkey> keyiterator = selector.selectedkeys().iterator();
while (keyiterator.hasnext()) {
selectionkey key = keyiterator.next();
keyiterator.remove(); // 移除当前处理的 key,避免重复处理
try {
// 5. 处理不同的事件
if (key.isacceptable()) { // 客户端连接事件
handleaccept(key, selector);
} else if (key.isreadable()) { // 读取客户端数据事件
handleread(key);
}
} catch (ioexception e) {
system.err.println("error handling client connection: " + e.getmessage());
key.cancel(); // 取消出错的键
if (key.channel() != null) {
key.channel().close();
}
}
}
}
} catch (ioexception e) {
e.printstacktrace();
}
}
// 处理 accept 事件
private static void handleaccept(selectionkey key, selector selector) throws ioexception {
serversocketchannel serverchannel = (serversocketchannel) key.channel();
socketchannel clientchannel = serverchannel.accept(); // 接受客户端连接
clientchannel.configureblocking(false); // 设置为非阻塞模式
clientchannel.register(selector, selectionkey.op_read); // 注册 read 事件
system.out.println("client connected: " + clientchannel.getremoteaddress());
}
// 处理 read 事件
private static void handleread(selectionkey key) throws ioexception {
socketchannel clientchannel = (socketchannel) key.channel();
bytebuffer buffer = bytebuffer.allocate(1024);
int bytesread;
try {
bytesread = clientchannel.read(buffer); // 从通道中读取数据
} catch (socketexception e) {
system.err.println("connection reset by client: " + clientchannel.getremoteaddress());
clientchannel.close();
key.cancel();
return;
}
if (bytesread > 0) {
buffer.flip(); // 切换到读取模式
string message = new string(buffer.array(), 0, buffer.limit());
system.out.println("received from client: " + message);
// 回写数据到客户端
buffer.clear();
buffer.put(("echo: " + message).getbytes());
buffer.flip();
clientchannel.write(buffer);
} else if (bytesread == -1) { // 客户端断开连接
system.out.println("client disconnected: " + clientchannel.getremoteaddress());
clientchannel.close();
key.cancel(); // 取消注册的事件
}
}
}
客户端代码
import java.io.ioexception;
import java.net.inetsocketaddress;
import java.nio.bytebuffer;
import java.nio.channels.socketchannel;
public class nioclient {
public static void main(string[] args) throws ioexception {
// 1. 创建 socketchannel 连接服务器
socketchannel socketchannel = socketchannel.open();
socketchannel.configureblocking(false);
if (!socketchannel.connect(new inetsocketaddress("127.0.0.1", 8080))) {
// 等待连接完成
while (!socketchannel.finishconnect()) {
system.out.println("connecting to server...");
}
}
system.out.println("connected to the server");
// 2. 发送数据到服务器
string message = "hello, nio server!";
bytebuffer buffer = bytebuffer.wrap(message.getbytes());
socketchannel.write(buffer);
// 3. 接收服务器回写的数据
buffer.clear();
int bytesread = socketchannel.read(buffer);
if (bytesread > 0) {
buffer.flip();
string response = new string(buffer.array(), 0, buffer.limit());
system.out.println("received from server: " + response);
}
socketchannel.close();
}
}
运行结果
客户端输出
connected to the server
received from server: echo: hello, nio s
进程已结束,退出代码为 0
服务器端输出
nio server started on port 8080...
client connected: /127.0.0.1:50104
received from client: hello, nio server!
connection reset by client: /127.0.0.1:50104
nio 高并发的关键点
非阻塞 i/o:通过 selector 一个线程可以同时监听多个客户端连接,无需为每个连接创建一个线程,降低了线程开销。
多路复用:selector 提供多路复用机制,能同时监听多个事件(如连接就绪、读就绪等)。
io 操作优化:通过 bytebuffer 进行 i/o 操作,避免了传统流的频繁数据拷贝,提高了读写效率。
nio 的局限性和改进
1.局限性
nio 的使用相对复杂,需要手动管理通道和缓冲区。
在高并发场景下,selector 的性能可能成为瓶颈。
2.改进方向
netty:一个基于 nio 的高性能网络框架,简化了 nio 的使用,同时提供了更高的吞吐量和扩展性。
异步 i/o(aio):java nio 2.0(jdk 7 引入)提供了异步 i/o,进一步优化了线程资源利用。
适用场景和建议
适用场景:高并发的网络应用,例如 web 服务器、消息推送服务;i/o 密集型应用。
使用建议:对于复杂的高性能网络应用,建议使用 netty 等成熟框架,避免直接操作 nio 的底层代码。
以上就是一文带你搞懂java如何实现网络nio高并发编程的详细内容,更多关于java网络nio高并发编程的资料请关注代码网其它相关文章!
发表评论