政府 门户 网站建设阿里指数
IO模型
用什么样的通道进行数据传输和接收,java支持3种io网络编程模式 BIO NIO AIO
BIO
同步阻塞 一个客户端连接对应一个处理线程
BIO示例代码(客户端和服务端)
package com.tuling.bio;import java.io.IOException;
import java.net.Socket;public class SocketClient {public static void main(String[] args) throws IOException, InterruptedException {Socket socket = new Socket("127.0.0.1", 9000);//向服务端发送数据socket.getOutputStream().write("HelloServer".getBytes());socket.getOutputStream().flush();System.out.println("向服务端发送数据结束");byte[] bytes = new byte[1024];//接收服务端回传的数据socket.getInputStream().read(bytes);System.out.println("接收到服务端的数据:" + new String(bytes));socket.close();}
}
package com.tuling.bio;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;public class SocketServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(9000);while (true) {System.out.println("等待连接。。");//阻塞方法Socket clientSocket = serverSocket.accept();System.out.println("有客户端连接了。。");new Thread(new Runnable() {@Overridepublic void run() {try {handler(clientSocket);} catch (IOException e) {e.printStackTrace();}}}).start();}}private static void handler(Socket clientSocket) throws IOException {byte[] bytes = new byte[1024];System.out.println("准备read。。");//接收客户端的数据,阻塞方法,没有数据可读时就阻塞int read = clientSocket.getInputStream().read(bytes);System.out.println("read完毕。。");if (read != -1) {System.out.println("接收到客户端的数据:" + new String(bytes, 0, read));}clientSocket.getOutputStream().write("HelloClient".getBytes());clientSocket.getOutputStream().flush();}
}
先启动服务端,再启动客户端,客户端和服务端直接可以互发消息
缺点:io代码里read操作是阻塞操作,如果连接不做数据读写操作会导致线程阻塞,浪费资源
如果线程很多,会导致服务器线程太多,压力太大
应用场景:连接数目小且固定的架构,对服务器资源要求比较高
NIO
同步非阻塞 一个线程可以处理多个请求。 客户端发送的请求可以注册到多路复用selector上,多路复用查询到IO请求就进行处理
应用场景 连接多且连接比较短的架构,如聊天服务器,编程比较复杂
nio示例代码
package com.tuling.nio;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class NioServer {// 保存客户端连接static List<SocketChannel> channelList = new ArrayList<>();public static void main(String[] args) throws IOException {// 创建NIO ServerSocketChannel,与BIO的serverSocket类似ServerSocketChannel serverSocket = ServerSocketChannel.open();serverSocket.socket().bind(new InetSocketAddress(9000));// 设置ServerSocketChannel为非阻塞serverSocket.configureBlocking(false);System.out.println("服务启动成功");while (true) {// 非阻塞模式accept方法不会阻塞,否则会阻塞// NIO的非阻塞是由操作系统内部实现的,底层调用了linux内核的accept函数SocketChannel socketChannel = serverSocket.accept();if (socketChannel != null) { // 如果有客户端进行连接System.out.println("连接成功");// 设置SocketChannel为非阻塞socketChannel.configureBlocking(false);// 保存客户端连接在List中channelList.add(socketChannel);}// 遍历连接进行数据读取Iterator<SocketChannel> iterator = channelList.iterator();while (iterator.hasNext()) {SocketChannel sc = iterator.next();ByteBuffer byteBuffer = ByteBuffer.allocate(128);// 非阻塞模式read方法不会阻塞,否则会阻塞int len = sc.read(byteBuffer);// 如果有数据,把数据打印出来if (len > 0) {System.out.println("接收到消息:" + new String(byteBuffer.array()));} else if (len == -1) { // 如果客户端断开,把socket从集合中去掉iterator.remove();System.out.println("客户端断开连接");}}}}
}
nio引入多路复用器示例代码
package com.tuling.nio;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;public class NioSelectorServer {public static void main(String[] args) throws IOException {// 创建NIO ServerSocketChannelServerSocketChannel serverSocket = ServerSocketChannel.open();serverSocket.socket().bind(new InetSocketAddress(9000));// 设置ServerSocketChannel为非阻塞serverSocket.configureBlocking(false);// 打开Selector处理Channel,即创建epollSelector selector = Selector.open();// 把ServerSocketChannel注册到selector上,并且selector对客户端accept连接操作感兴趣SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服务启动成功");while (true) {// 阻塞等待需要处理的事件发生selector.select();// 获取selector中注册的全部事件的 SelectionKey 实例Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();// 遍历SelectionKey对事件进行处理while (iterator.hasNext()) {SelectionKey key = iterator.next();// 如果是OP_ACCEPT事件,则进行连接获取和事件注册if (key.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel socketChannel = server.accept();socketChannel.configureBlocking(false);// 这里只注册了读事件,如果需要给客户端发送数据可以注册写事件SelectionKey selKey = socketChannel.register(selector, SelectionKey.OP_READ);System.out.println("客户端连接成功");} else if (key.isReadable()) { // 如果是OP_READ事件,则进行读取和打印SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer byteBuffer = ByteBuffer.allocateDirect(128);int len = socketChannel.read(byteBuffer);// 如果有数据,把数据打印出来if (len > 0) {System.out.println("接收到消息:" + new String(byteBuffer.array()));} else if (len == -1) { // 如果客户端断开连接,关闭SocketSystem.out.println("客户端断开连接");socketChannel.close();}}//从事件集合里删除本次处理的key,防止下次select重复处理iterator.remove();}}}
}
NIO三大核心组件: channel buffer selector
channel 通道 ,底层数组
buffer 缓冲
nio底层在jdk1.4版本基于linux内核函数select()或poll()来实现,类似于nioserver代码,每次都会轮询所有的channel。看哪个有读取事件即进行处理,没有继续遍历。 jdk1.5后引入epoll基于事件相应来优化nio
AIO 异步非阻塞 一般用于连接数多且连接时间较长的应用 jdk7支持
为什么netty 使用NIO而不是AIO
在linux系统,aio底层仍然使用epoll,没有很好实现AIO,性能没有明显优势,而且被jdk封装不易优化。
netty是异步非阻塞框架,对于aio做了很多异步封装