一、BIO
1、机制
采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的链接,它接收到客户端的连接请求之后为每个客户端请求创建一个新的线程进行链路处理,处理完成之后通过输出流将响应返回给客户端,线程销毁,这就是典型的一请求一应答的通信模型。
2、分析
当客户端并发访问量增加后,服务端的线程个数和客户端并发访问按1:1的正比关系递增,线程膨胀之后,系统的性能会急剧下降,甚至会发生线程堆栈溢出、创建失败,最终发生宕机或将死的惨状。
当然我们可以在服务端使用线程池的方式,来保护我们的系统受到高并发的冲击,但是即使线程池在大毕竟也是有限的,这样会出现大量的请求等待线程池的资源,从而性能、时延、并发量还是会面临很糟糕的情况。
二、NIO
1、机制
概念组成就不说了,网上很多。
采用nio通信模型的服务端,通常由一个独立的线程selector(选择器)来管理一个或多个channel,当channel注册了selector之后,selector会监听channel的各种事件,如SelectionKey.OP_ACCEPT-接收事件,当注册的事件发生后,通过迭代器获取选中的事件-SelectionKey,如果SelectionKey为请求连接事件,则保存客户端的SocketChannel并设置非阻塞,再添加可读监听事件,这样在数据可读之前,selector可以做一些其他的事情;如果SelectionKey为可读事件,则可以通过线程池用SocketChannel获取数据,进行接下来的逻辑处理,最后将响应返回给客户端。
2、分析
由于selector可以判断数据的接收状态,所以可以节省掉等待io数据的时间,而监听状态的时间会很快,可以由单线程完成,这样也避免了线程的上下文切换。
三、代码
1、服务端
public class NIOServer { //选择器 private Selector selector; /** * 对该通道做一些初始化的工作 */ public void initServer(int port) throws IOException { // 获得ServerSocket通道 ServerSocketChannel serverChannel = ServerSocketChannel.open(); // 设置通道为非阻塞 serverChannel.configureBlocking(false); // 绑定到port端口 serverChannel.socket().bind(new InetSocketAddress(port)); // 获得选择器 this.selector = Selector.open(); //为该通道注册SelectionKey.OP_ACCEPT事件 serverChannel.register(selector, SelectionKey.OP_ACCEPT); } /** * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 */ @SuppressWarnings("unchecked") public void listen() throws IOException { // 轮询访问selector while (true) { //当注册的事件到达时,方法返回;否则,该方法会一直阻塞 selector.select(); // 获得selector中选中的项的迭代器,选中的项为注册的事件 Iterator ite = this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); // 删除已选的key,以防重复处理 ite.remove(); // 客户端请求连接事件 if (key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key .channel(); // 获得和客户端连接的通道 SocketChannel channel = server.accept(); // 设置成非阻塞 channel.configureBlocking(false); //可以给客户端发送信息 channel.write(ByteBuffer.wrap(new String("abc").getBytes())); //在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。 channel.register(this.selector, SelectionKey.OP_READ); } else if (key.isReadable()) { // 获得了可读的事件 read(key); } } } } /** * 处理逻辑 */ public void read(SelectionKey key) throws IOException{ // 服务器可读取消息:得到事件发生的Socket通道 SocketChannel channel = (SocketChannel) key.channel(); // 创建读取的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(10); channel.read(buffer); byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("服务端收到信息:"+msg); ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes()); channel.write(outBuffer);// 将消息回送给客户端 } /** * 启动服务端测试 */ public static void main(String[] args) throws IOException { NIOServer server = new NIOServer(); server.initServer(8000); server.listen(); } }
2、客户端
public class NIOClient { //选择器 private Selector selector; /** * 对该通道做一些初始化的工作 */ public void initClient(String ip,int port) throws IOException { // 获得一个Socket通道 SocketChannel channel = SocketChannel.open(); // 设置通道为非阻塞 channel.configureBlocking(false); // 选择器 this.selector = Selector.open(); // 客户端连接服务器,其实方法执行并没有实现连接,需要在listen()方法中调 //用channel.finishConnect();才能完成连接 channel.connect(new InetSocketAddress(ip,port)); //将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。 channel.register(selector, SelectionKey.OP_CONNECT); } /** * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 */ @SuppressWarnings("unchecked") public void listen() throws IOException { // 轮询访问selector while (true) { selector.select(); // 获得selector中选中的项的迭代器 Iterator ite = this.selector.selectedKeys().iterator(); while (ite.hasNext()) { SelectionKey key = (SelectionKey) ite.next(); // 删除已选的key,以防重复处理 ite.remove(); // 连接事件发生 if (key.isConnectable()) { SocketChannel channel = (SocketChannel) key .channel(); // 如果正在连接,则完成连接 if(channel.isConnectionPending()){ channel.finishConnect(); } // 设置成非阻塞 channel.configureBlocking(false); //在这里可以给服务端发送信息哦 channel.write(ByteBuffer.wrap(new String("abc").getBytes())); //在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。 channel.register(this.selector, SelectionKey.OP_READ); // 获得了可读的事件 } else if (key.isReadable()) { read(key); } } } } /** * 处理逻辑 */ public void read(SelectionKey key) throws IOException{ SocketChannel channel = (SocketChannel) key.channel(); // 创建读取的缓冲区 ByteBuffer buffer = ByteBuffer.allocate(10); channel.read(buffer); byte[] data = buffer.array(); String msg = new String(data).trim(); System.out.println("客户端收到信息:"+msg); } /** * 启动客户端测试 */ public static void main(String[] args) throws IOException { NIOClient client = new NIOClient(); client.initClient("localhost",8000); client.listen(); } }
相关推荐
基于java的BIO、NIO、AIO通讯模型代码实现
对java io总结时编写的测试代码,包括BIO,NIO,AIO的实现,Java io操作是编程人员经常使用到的,以前只是使用没有对这三种IO做系统的了解,本文将对这三种IO作详细的介绍并附有测试完整代码
几个用java写的小程序,实现了bio和nio
java的bio nio aio 几种io的详细解释
主要介绍了Java使用BIO和NIO进行文件操作对比代码示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
一站式学习Java网络编程 全面理解BIO:NIO:AIO1
Java IO与NIO开发文档
java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现Reactor模型源码java基于NIO实现...
java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java...
其实NIO这类代码都有一些比较统一的写法,因此将BIO,NIO,AIO的实现代码貼出来进行分享,有需要可以下载,直接执行对应的SERVER和CLIENT端的main函数即可
《Socket 之 BIO、NIO、Netty 简单实现》博客附件。 博客地址:https://blog.csdn.net/Supreme_Sir/article/details/112725728
详细介绍NIO以及和BIO的对比,原有的 IO 是面向流的、阻塞的,NIO 则是面向块的、非阻塞的。
bio nio aio demo下载,可直接运行main方法。了解 bio nio aio基础用法
Java NIO系列教程(一) Java NIO 概述 Java NIO系列教程(二) Channel Java NIO系列教程(三) Buffer Java NIO系列教程(四) Scatter/Gather Java NIO系列教程(五) 通道...Java NIO系列教程(十二) Java NIO与IO
Java NIO与IO性能对比分析.pdf
Java中I/O是以流为基础进行数据的输入输出的,所有数据被串行化(所谓串行化就是数据要按顺序进行输入 输出)写入输出流。简单来说就是java通过io流方式和外部设备进行交互。 在Java类库中,IO部分的内容是很庞大的,...
netty案例,netty4.1基础入门篇零《初入JavaIO之门BIO、NIO、AIO实战练习》 ...
BIO,NIO,AIO,Netty面试题
这是一本介绍java io以及nio相关知识的书,书中对知识的讲解通俗易懂,是学习java nio以及复习java io相关知识的必备书籍。注意:本书为英文版!!!
【Java IO】从NIO到Reactor三种模式 博客地址:https://blog.csdn.net/qq_36963950/article/details/107998164