网站是灰色系的网站,济宁一建建设集团有限公司,公司网站建设外包流程图,注册公司核名的流程一#xff0c;概述
原本的java是基于同步阻塞式的i/o通信#xff08;bio) 性能低下#xff0c;所以出现了nio这种非阻塞式的
二#xff0c;Java 的I/O演进之路
2.1 i/o模型基本说明
i/o模型#xff1a;就是用什么样的通道或者说通信模式和架构进行数据的传输和接收概述
原本的java是基于同步阻塞式的i/o通信bio) 性能低下所以出现了nio这种非阻塞式的
二Java 的I/O演进之路
2.1 i/o模型基本说明
i/o模型就是用什么样的通道或者说通信模式和架构进行数据的传输和接收很大程度上决定了程序通信的性能java支持的3种网络编程的io模型BIONIOAIO
2.2 I/O模型
BIO
一个连接是一个线程
NIO
同步非阻塞的客户端发送的连接请求都会注册到多路复用器上多路复用器查询到连接有i/o请求就会进行处理
AIO
异步非阻塞
2.3 BIO,NIO,AIO 使用场景分析
bio适合连接数目小的且固定的架构nio适合连接数目多且连接比较短的架构聊天AIO方式使用于连接数目多且连接比较长(重操作的架构比如相册服务器充
四 NIO
4.1 NIO 基本介绍
面向缓冲区基于通道的在java.nio包下以及子包下有三大核心 **Channel通道Buffer缓冲区Selector选择器)如果通道里面没有数据就会去做其他事情不会去等待
4.2 NIO 和 BIO 比较
BIO 以流的方式,NIO 以块的方式,效率更高NIO是非阻塞的BIO 是基于字节流和字符流进行操作,而NIO是基于Channel(通道)和Buffer(缓冲区)进行操作,数据总是由缓冲区写入通道或者从通道读入缓冲区,选择器用于监听
4.3 NIO 三大核心原理
三大核心分别为 Channel(通道) , Buffer(缓冲区),Selector(选择器) Buffer缓冲区 Channel(通道) 通道可以非阻塞读取和写入通道通道可以支持读取或写入缓冲区也支持异步地读写,而流是单向的
Selector(选择器) 可以检测多个通道 程序切换到哪个channel是由事件决定的
4.4 缓冲区(Buffer)
一个容器,由java.nio包定义,所有缓冲区都是Buffer抽象类的子类,
Buffer类与其子类
ByteBufferCharBufferShortBufferIntBufferLongBufferFloatBufferDoubleBuffer
static xxxBuffer allocate(int capacity): 创建一个容量为capicity的 buffer对象 buffer.wrap() 数据已知 ByteBuffer.allocate() 构建ByteBuffer对象参数实际上是指底层的字节数组的容量
缓冲区的基本属性
容量 创建后不能改变限制 (limit) limit后面不能读写,不能为负,不能大于其容量,写入模式,限制等于buffer的容量,读取模式下,limit等与写入的数据量位置(position): 下一个读取或写入的数据的索引,索引库的位置不能为负,并且不能大于其限制标记(mark)与重置(reset):标记是一个索引,通过Buffer中的mark() 方法指定Buffer中一个特定的position,之后通过调用reset()方法恢复到这个position 标记、位置、限制、容量遵守以下不变式: 0mark position limit capacity
Buffer常见方法
Buffer clear() 洁空缓冲区并返回对缓冲区的引用(只是把position变为0位置)
Buffer flip(为将缓冲区的界限设置为当前位置并将当前位置设值为0
使用wrap() 类java.nio.IntBuffer中的方法可以将int数组包装到缓冲区中。此方法需要一个参数即将数组包装到缓冲区中并返回创建的新缓冲区。如果返回的缓冲区被修改则数组的内容也将被修改反之亦然。
int capacity(返回 Buffer 的capacity大小
boolean hasRemaining(判断缓区中是否还有元素
int limit()返同Buffer的界限(limit)的位置
Buffer limit(int n)将设置缓冲区界限为n并返回一个具有新limit 的缓冲区对象
Buffer mark()对缓冲区设置标记
int position()返回缓冲区的当前位置position
Buffer position(int n)将设置缓冲区的当前位置为n 并返回修改后的 Buffer对象
int remaining()返回position和 limit 之间的元素个数
Buffer reset()将位置position转到以前设置的 mark所在的位置
Buffer rewind()将位置设为为节、取消设置的markBuffer所有子类提供了两个用于数据操作的方法:
get()put()方法取获取Buffer中的数据
get():读取单个字节
get(byte[] dst):批量读取多个字节到dst中
get(int index):读取指定索引位置的字节(不会移动position)
放到入数据到 Buffer 中十
put (byte b):将给定单个字节写入缓冲区的当前位置
put(byte[] src);将 src中的字节写入缓冲区的当前位置
put(int indexbyte b):将指定字节写入缓冲区的索引位置(不会移动 position)缓冲区的数据操作
public class BufferTest { Test public void test01(){ //1.分配一个缓冲区,容量设置10 ByteBuffer buffer ByteBuffer.allocate(10); System.out.println(buffer.position()); System.out.println(buffer.limit()); System.out.println(buffer.capacity()); System.out.println(---------------------); //2.添加数据 String name itheima; buffer.put(name.getBytes(StandardCharsets.UTF_8)); System.out.println(buffer.position()); System.out.println(buffer.limit()); System.out.println(buffer.capacity()); System.out.println(---------------------); buffer.flip(); //为将缓冲区的界限设置为当前位置并将当前位置设值为0 System.out.println(buffer.position()); System.out.println(buffer.limit()); System.out.println(buffer.capacity()); System.out.println(---------------------); //4,. get数据的读取 char b (char)buffer.get(); System.out.println(b); System.out.println(buffer.position()); System.out.println(buffer.limit()); System.out.println(buffer.capacity()); }
}0
10
10
---------------------
7
10
10
---------------------
0
7
10
---------------------
i
1
7
10直接与非直接缓冲区
什么是直接内存与非直接内存根据官方文档的描述: byte byffer可以是两种类型一种是基于直接内存也就是非堆内存)﹔另一种是非直接内存也就是堆内存)。对于直接内存来说JVM将会在IO操作上具有更高的性能因为它直接作用于本地系统的IO操作。而非直接内存也就是堆内存中的数据如果要作IO操作会先从本进程内存复制到直接内存再利用本地IO处理。 从数据流的角度非直接内存是下面这样的作用链: 本地IO--直接内存--非直接内存--→直按内存--本地IO
而直接内存是: 本地IO--直接内存--本地IO 很明显在做IO处理时比如网络发送大量数据时直接内存会具有更高的效率。直接内存使用allocateDirect创建但是它比申请普通的堆内存需要耗费更高的性能。不过这部分的数据是在IVM之外的因此它不会占用应用的内存。所以呢当你有很大的数据要缓存并且它的生命周期又很长那么就比较适合使用直接内存。只是一般来说如果不是能带来很明显的性能提升还是推荐直接使用堆内存。字节缓冲区是直接缓冲区还是非直接缓冲区可通过调用其isDirect()方法来确定。
4.5 NIO核心二:通道(Channel)通道Channe概述
通道(Channel):由java.nio.channels包定义的, Channel表示I0源与目标打开的连接。Channel类似于传统的“流。只不过Channel本身不能直接访问数据channel只能与Buffer进行交互。 1、NIO的通道类似于流但有些区别如下:
通道可以同时进行读写而流只能读或者只能写通道可以实现异步读写数据通道可以从缓冲读数据也可以写数据到缓冲: 2、Bl0中的stream是单向的例如FilelnputStream对象只能进行读取数据的操作而NIO中的通道(Channel)是双向的可以读操作也可以写操作。 3、Channel在NIO中是一个接口 public interface channe1 extends closeable{} 常用的Channel实现类FileChannel:用于读取、写入、映射和操作文件的通道。DatagramChannel:通过UDP读写网络中的数据通道。SocketChannel:通过TCP读写网络中的数据。ServerSocketChannel:可以监听新进来的TCP 连接对每一个新进来的连接都会创建一个SocketChannel。【ServerSocketChanne类似ServerSocket , SocketChannel类似Socket】
FileChannel 类
获取通道的一种方式是对支持通道的对象调用getChannel() 方法。支持通道的类如下
FileInputStreamFileOutputStreamRandomAccessFileDatagramSocketSocketServerSocket 获取通道的其他方式是使用 Files 类的静态方法 newByteChannel() 获取字节通道。或者通过通道的静态方法 open() 打开并返回指定通道
FileChannel的常用方法
int read(ByteBuffer dst) 从 从 Channel 到 中读取数据到 ByteBuffer long read(ByteBuffer[] dsts) 将 将 Channel 到 中的数据“分散”到 ByteBuffer[] int write(ByteBuffer src) 将 将 ByteBuffer 到 中的数据写入到 Channel long write(ByteBuffer[] srcs) 将 将 ByteBuffer[] 到 中的数据“聚集”到 Channel long position() 返回此通道的文件位置 FileChannel position(long p) 设置此通道的文件位置 long size() 返回此通道的文件的当前大小 FileChannel truncate(long s) 将此通道的文件截取为给定大小 void force(boolean metaData) 强制将所有对此通道的文件更新写入到存储设备中
案例1-本地文件写数据
需求使用前面学习后的 ByteBuffer(缓冲) 和 FileChannel(通道) 将 “hello,黑马Java程序员” 写入到 data.txt 中.
package com.itheima;
import org.junit.Test;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ChannelTest { Test public void write(){ try { // 1、字节输出流通向目标文件 FileOutputStream fos new FileOutputStream(data01.txt); // 2、得到字节输出流对应的通道Channel FileChannel channel fos.getChannel(); // 3、分配缓冲区 ByteBuffer buffer ByteBuffer.allocate(1024); buffer.put(hello,黑马Java程序员.getBytes()); // 4、把缓冲区切换成写出模式 buffer.flip(); channel.write(buffer); channel.close(); System.out.println(写数据到文件中); } catch (Exception e) { e.printStackTrace(); } }
}案例2-本地文件读数据
需求使用前面学习后的 ByteBuffer(缓冲) 和 FileChannel(通道) 将 data01.txt 中的数据读入到程序并显示在控制台屏幕
public class ChannelTest {Testpublic void read() throws Exception {// 1、定义一个文件字节输入流与源文件接通FileInputStream is new FileInputStream(data01.txt);// 2、需要得到文件字节输入流的文件通道FileChannel channel is.getChannel();// 3、定义一个缓冲区ByteBuffer buffer ByteBuffer.allocate(1024);// 4、读取数据到缓冲区channel.read(buffer);buffer.flip();// 5、读取出缓冲区中的数据并输出即可String rs new String(buffer.array(),0,buffer.remaining());System.out.println(rs);}案例3-使用Buffer完成文件复制
使用 FileChannel(通道) 完成文件的拷贝。
Test
public void copy() throws Exception {// 源文件File srcFile new File(C:\\Users\\dlei\\Desktop\\BIO,NIO,AIO\\文件\\壁纸.jpg);File destFile new File(C:\\Users\\dlei\\Desktop\\BIO,NIO,AIO\\文件\\壁纸new.jpg);// 得到一个字节字节输入流FileInputStream fis new FileInputStream(srcFile);// 得到一个字节输出流FileOutputStream fos new FileOutputStream(destFile);// 得到的是文件通道FileChannel isChannel fis.getChannel();FileChannel osChannel fos.getChannel();// 分配缓冲区ByteBuffer buffer ByteBuffer.allocate(1024);while(true){// 必须先清空缓冲然后再写入数据到缓冲区buffer.clear();// 开始读取一次数据int flag isChannel.read(buffer);if(flag -1){break;}// 已经读取了数据 把缓冲区的模式切换成可读模式buffer.flip();// 把数据写出到osChannel.write(buffer);}isChannel.close();osChannel.close();System.out.println(复制完成);
}案例4-分散 (Scatter) 和聚集 (Gather)
分散读取Scatter :是指把Channel通道的数据读入到多个缓冲区中去
聚集写入Gathering 是指将多个 Buffer 中的数据“聚集”到 Channel。
//分散和聚集
Test
public void test() throws IOException{RandomAccessFile raf1 new RandomAccessFile(1.txt, rw);//1. 获取通道FileChannel channel1 raf1.getChannel();//2. 分配指定大小的缓冲区ByteBuffer buf1 ByteBuffer.allocate(100);ByteBuffer buf2 ByteBuffer.allocate(1024);//3. 分散读取ByteBuffer[] bufs {buf1, buf2};channel1.read(bufs);for (ByteBuffer byteBuffer : bufs) {byteBuffer.flip();}System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));System.out.println(-----------------);System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));//4. 聚集写入RandomAccessFile raf2 new RandomAccessFile(2.txt, rw);FileChannel channel2 raf2.getChannel();channel2.write(bufs);
}案例5-transferFrom()
从目标通道中去复制原通道数据
Test
public void test02() throws Exception {// 1、字节输入管道FileInputStream is new FileInputStream(data01.txt);FileChannel isChannel is.getChannel();// 2、字节输出流管道FileOutputStream fos new FileOutputStream(data03.txt);FileChannel osChannel fos.getChannel();// 3、复制osChannel.transferFrom(isChannel,isChannel.position(),isChannel.size());isChannel.close();osChannel.close();
}案例6-transferTo()
把原通道数据复制到目标通道
Test
public void test02() throws Exception {// 1、字节输入管道FileInputStream is new FileInputStream(data01.txt);FileChannel isChannel is.getChannel();// 2、字节输出流管道FileOutputStream fos new FileOutputStream(data04.txt);FileChannel osChannel fos.getChannel();// 3、复制isChannel.transferTo(isChannel.position() , isChannel.size() , osChannel);isChannel.close();osChannel.close();
}4.6 NIO核心三选择器(Selector)
选择器(Selector)概述
选择器Selector 是 SelectableChannle 对象的多路复用器Selector 可以同时监控多个 SelectableChannel 的 IO 状况也就是说利用 Selector可使一个单独的线程管理多个 Channel。Selector 是非阻塞 IO 的核心 Java 的 NIO用非阻塞的 IO 方式。可以用一个线程处理多个的客户端连接就会使用到 Selector(选择器)Selector 能够检测多个注册的通道上是否有事件发生(注意:多个 Channel 以事件的方式可以注册到同一个 Selector)如果有事件发生便获取事件然后针对每个事件进行相应的处理。这样就可以只用一个单线程去管 理多个通道也就是管理多个连接和请求。只有在 连接/通道 真正有读写事件发生时才会进行读写就大大地减少了系统开销并且不必为每个连接都 创建一个线程不用去维护多个线程避免了多线程之间的上下文切换导致的开销
选择 器Selector的应用
创建 Selector 通过调用 Selector.open() 方法创建一个 Selector。
Selector selector Selector.open();向选择器注册通道SelectableChannel.register(Selector sel, int ops)
//1. 获取通道
ServerSocketChannel ssChannel ServerSocketChannel.open();
//2. 切换非阻塞模式
ssChannel.configureBlocking(false);
//3. 绑定连接
ssChannel.bind(new InetSocketAddress(9898));
//4. 获取选择器
Selector selector Selector.open();
//5. 将通道注册到选择器上, 并且指定“监听接收事件”
ssChannel.register(selector, SelectionKey.OP_ACCEPT);当调用 register(Selector sel, int ops) 将通道注册选择器时选择器对通道的监听事件需要通过第二个参数 ops 指定。可以监听的事件类型用 可使用 SelectionKey 的四个常量 表示
读 : SelectionKey.OP_READ 1写 : SelectionKey.OP_WRITE 4连接 : SelectionKey.OP_CONNECT 8接收 : SelectionKey.OP_ACCEPT 16若注册时不止监听一个事件则可以使用“位或”操作符连接。
int interestSet SelectionKey.OP_READ|SelectionKey.OP_WRITE 4.7 NIO非阻塞式网络通信原理分析
Selector 示意图和特点说明
Selector可以实现 一个 I/O 线程可以并发处理 N 个客户端连接和读写操作这从根本上解决了传统同步阻塞 I/O 一连接一线程模型架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。 服务端流程 1、当客户端连接服务端时服务端会通过 ServerSocketChannel 得到 SocketChannel1. 获取通道 ServerSocketChannel ssChannel ServerSocketChannel.open();2、切换非阻塞模式 ssChannel.configureBlocking(false);3、绑定连接 ssChannel.bind(new InetSocketAddress(9999));4、 获取选择器 Selector selector Selector.open();5、 将通道注册到选择器上, 并且指定“监听接收事件” ssChannel.register(selector, SelectionKey.OP_ACCEPT);轮询式的获取选择器上已经“准备就绪”的事件 //轮询式的获取选择器上已经“准备就绪”的事件while (selector.select() 0) {System.out.println(轮一轮);//7. 获取当前选择器中所有注册的“选择键(已就绪的监听事件)”IteratorSelectionKey it selector.selectedKeys().iterator();while (it.hasNext()) {//8. 获取准备“就绪”的是事件SelectionKey sk it.next();//9. 判断具体是什么事件准备就绪if (sk.isAcceptable()) {//10. 若“接收就绪”获取客户端连接SocketChannel sChannel ssChannel.accept();//11. 切换非阻塞模式sChannel.configureBlocking(false);//12. 将该通道注册到选择器上sChannel.register(selector, SelectionKey.OP_READ);} else if (sk.isReadable()) {//13. 获取当前选择器上“读就绪”状态的通道SocketChannel sChannel (SocketChannel) sk.channel();//14. 读取数据ByteBuffer buf ByteBuffer.allocate(1024);int len 0;while ((len sChannel.read(buf)) 0) {buf.flip();System.out.println(new String(buf.array(), 0, len));buf.clear();}}//15. 取消选择键 SelectionKeyit.remove();}}}客户端流程 获取通道 SocketChannel sChannel SocketChannel.open(new InetSocketAddress(127.0.0.1, 9999));切换非阻塞模式 sChannel.configureBlocking(false);分配指定大小的缓冲区 ByteBuffer buf ByteBuffer.allocate(1024);发送数据给服务端 Scanner scan new Scanner(System.in);while(scan.hasNext()){String str scan.nextLine();buf.put((new SimpleDateFormat(yyyy/MM/dd HH:mm:ss).format(System.currentTimeMillis()) \n str).getBytes());buf.flip();sChannel.write(buf);buf.clear();}//关闭通道sChannel.close();4.8 NIO非阻塞式网络通信入门案例
需求服务端接收客户端的连接请求并接收多个客户端发送过来的事件。
代码案例
/**客户端*/
public class Client {public static void main(String[] args) throws Exception {//1. 获取通道SocketChannel sChannel SocketChannel.open(new InetSocketAddress(127.0.0.1, 9999));//2. 切换非阻塞模式sChannel.configureBlocking(false);//3. 分配指定大小的缓冲区ByteBuffer buf ByteBuffer.allocate(1024);//4. 发送数据给服务端Scanner scan new Scanner(System.in);while(scan.hasNext()){String str scan.nextLine();buf.put((new SimpleDateFormat(yyyy/MM/dd HH:mm:ss).format(System.currentTimeMillis()) \n str).getBytes());buf.flip();sChannel.write(buf);buf.clear();}//5. 关闭通道sChannel.close();}
}/**服务端*/
public class Server {public static void main(String[] args) throws IOException {//1. 获取通道ServerSocketChannel ssChannel ServerSocketChannel.open();//2. 切换非阻塞模式ssChannel.configureBlocking(false);//3. 绑定连接ssChannel.bind(new InetSocketAddress(9999));//4. 获取选择器Selector selector Selector.open();//5. 将通道注册到选择器上, 并且指定“监听接收事件”ssChannel.register(selector, SelectionKey.OP_ACCEPT);//6. 轮询式的获取选择器上已经“准备就绪”的事件while (selector.select() 0) {System.out.println(轮一轮);//7. 获取当前选择器中所有注册的“选择键(已就绪的监听事件)”IteratorSelectionKey it selector.selectedKeys().iterator();while (it.hasNext()) {//8. 获取准备“就绪”的是事件SelectionKey sk it.next();//9. 判断具体是什么事件准备就绪if (sk.isAcceptable()) {//10. 若“接收就绪”获取客户端连接SocketChannel sChannel ssChannel.accept();//11. 切换非阻塞模式sChannel.configureBlocking(false);//12. 将该通道注册到选择器上sChannel.register(selector, SelectionKey.OP_READ);} else if (sk.isReadable()) {//13. 获取当前选择器上“读就绪”状态的通道SocketChannel sChannel (SocketChannel) sk.channel();//14. 读取数据ByteBuffer buf ByteBuffer.allocate(1024);int len 0;while ((len sChannel.read(buf)) 0) {buf.flip();System.out.println(new String(buf.array(), 0, len));buf.clear();}}//15. 取消选择键 SelectionKeyit.remove();}}}
}
4.9 NIO 网络编程应用实例-群聊系统
目标
需求:进一步理解 NIO 非阻塞网络编程机制实现多人群聊
编写一个 NIO 群聊系统实现客户端与客户端的通信需求非阻塞服务器端可以监测用户上线离线并实现消息转发功能客户端通过 channel 可以无阻塞发送消息给其它所有客户端用户同时可以接受其它客户端用户通过服务端转发来的消息
服务端代码实现
public class Server {//定义属性private Selector selector;private ServerSocketChannel ssChannel;private static final int PORT 9999;//构造器//初始化工作public Server() {try {// 1、获取通道ssChannel ServerSocketChannel.open();// 2、切换为非阻塞模式ssChannel.configureBlocking(false);// 3、绑定连接的端口ssChannel.bind(new InetSocketAddress(PORT));// 4、获取选择器Selectorselector Selector.open();// 5、将通道都注册到选择器上去并且开始指定监听接收事件ssChannel.register(selector , SelectionKey.OP_ACCEPT);}catch (IOException e) {e.printStackTrace();}}//监听public void listen() {System.out.println(监听线程: Thread.currentThread().getName());try {while (selector.select() 0){System.out.println(开始一轮事件处理~~~);// 7、获取选择器中的所有注册的通道中已经就绪好的事件IteratorSelectionKey it selector.selectedKeys().iterator();// 8、开始遍历这些准备好的事件while (it.hasNext()){// 提取当前这个事件SelectionKey sk it.next();// 9、判断这个事件具体是什么if(sk.isAcceptable()){// 10、直接获取当前接入的客户端通道SocketChannel schannel ssChannel.accept();// 11 、切换成非阻塞模式schannel.configureBlocking(false);// 12、将本客户端通道注册到选择器System.out.println(schannel.getRemoteAddress() 上线 );schannel.register(selector , SelectionKey.OP_READ);//提示}else if(sk.isReadable()){//处理读 (专门写方法..)readData(sk);}it.remove(); // 处理完毕之后需要移除当前事件}}}catch (Exception e) {e.printStackTrace();}finally {//发生异常处理....}}//读取客户端消息private void readData(SelectionKey key) {//取到关联的channleSocketChannel channel null;try {//得到channelchannel (SocketChannel) key.channel();//创建bufferByteBuffer buffer ByteBuffer.allocate(1024);int count channel.read(buffer);//根据count的值做处理if(count 0) {//把缓存区的数据转成字符串String msg new String(buffer.array());//输出该消息System.out.println(form 客户端: msg);//向其它的客户端转发消息(去掉自己), 专门写一个方法来处理sendInfoToOtherClients(msg, channel);}}catch (IOException e) {try {System.out.println(channel.getRemoteAddress() 离线了..);e.printStackTrace();//取消注册key.cancel();//关闭通道channel.close();}catch (IOException e2) {e2.printStackTrace();;}}}//转发消息给其它客户(通道)private void sendInfoToOtherClients(String msg, SocketChannel self ) throws IOException{System.out.println(服务器转发消息中...);System.out.println(服务器转发数据给客户端线程: Thread.currentThread().getName());//遍历 所有注册到selector 上的 SocketChannel,并排除 selffor(SelectionKey key: selector.keys()) {//通过 key 取出对应的 SocketChannelChannel targetChannel key.channel();//排除自己if(targetChannel instanceof SocketChannel targetChannel ! self) {//转型SocketChannel dest (SocketChannel)targetChannel;//将msg 存储到bufferByteBuffer buffer ByteBuffer.wrap(msg.getBytes());//将buffer 的数据写入 通道dest.write(buffer);}}}public static void main(String[] args) {//创建服务器对象Server groupChatServer new Server();groupChatServer.listen();}
}客户端代码实现
package com.itheima.chat;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.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;public class Client {//定义相关的属性private final String HOST 127.0.0.1; // 服务器的ipprivate final int PORT 9999; //服务器端口private Selector selector;private SocketChannel socketChannel;private String username;//构造器, 完成初始化工作public Client() throws IOException {selector Selector.open();//连接服务器socketChannel socketChannel.open(new InetSocketAddress(127.0.0.1, PORT));//设置非阻塞socketChannel.configureBlocking(false);//将channel 注册到selectorsocketChannel.register(selector, SelectionKey.OP_READ);//得到usernameusername socketChannel.getLocalAddress().toString().substring(1);System.out.println(username is ok...);}//向服务器发送消息public void sendInfo(String info) {info username 说 info;try {socketChannel.write(ByteBuffer.wrap(info.getBytes()));}catch (IOException e) {e.printStackTrace();}}//读取从服务器端回复的消息public void readInfo() {try {int readChannels selector.select();if(readChannels 0) {//有可以用的通道IteratorSelectionKey iterator selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key iterator.next();if(key.isReadable()) {//得到相关的通道SocketChannel sc (SocketChannel) key.channel();//得到一个BufferByteBuffer buffer ByteBuffer.allocate(1024);//读取sc.read(buffer);//把读到的缓冲区的数据转成字符串String msg new String(buffer.array());System.out.println(msg.trim());}}iterator.remove(); //删除当前的selectionKey, 防止重复操作} else {//System.out.println(没有可以用的通道...);}}catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) throws Exception {//启动我们客户端Client chatClient new Client();//启动一个线程, 每个3秒读取从服务器发送数据new Thread() {public void run() {while (true) {chatClient.readInfo();try {Thread.currentThread().sleep(3000);}catch (InterruptedException e) {e.printStackTrace();}}}}.start();//发送数据给服务器端Scanner scanner new Scanner(System.in);while (scanner.hasNextLine()) {String s scanner.nextLine();chatClient.sendInfo(s);}}
}小结
第五章 JAVA AIO深入剖析
5.1 AIO编程
Java AIO(NIO.2) 异步非阻塞服务器实现模式为一个有效请求一个线程客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
AIO 异步非阻塞基于NIO的可以称之为NIO2.0 BIO NIO AIO Socket SocketChannel AsynchronousSocketChannel ServerSocket ServerSocketChannel AsynchronousServerSocketChannel
与NIO不同当进行读写操作时只须直接调用API的read或write方法即可, 这两种方法均为异步的对于读操作而言当有流可读取时操作系统会将可读的流传入read方法的缓冲区,对于写操作而言当操作系统将write方法传递的流写入完毕时操作系统主动通知应用程序
即可以理解为read/write方法都是异步的完成后会主动调用回调函数。在JDK1.7中这部分内容被称作NIO.2主要在Java.nio.channels包下增加了下面四个异步通道
AsynchronousSocketChannel
AsynchronousServerSocketChannel
AsynchronousFileChannel
AsynchronousDatagramChannel第六章 BIO,NIO,AIO课程总结
BIO、NIO、AIO Java BIO 同步并阻塞服务器实现模式为一个连接一个线程即客户端有连接请求时服务器端就需要启动一个线程进行处理如果这个连接不做任何事情会造成不必要的线程开销当然可以通过线程池机制改善。 Java NIO 同步非阻塞服务器实现模式为一个请求一个线程即客户端发送的连接请求都会注册到多路复用器上多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。 Java AIO(NIO.2) 异步非阻塞服务器实现模式为一个有效请求一个线程客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
BIO、NIO、AIO适用场景分析: BIO方式适用于连接数目比较小且固定的架构这种方式对服务器资源要求比较高并发局限于应用中JDK1.4以前的唯一选择但程序直观简单易理解。 NIO方式适用于连接数目多且连接比较短轻操作的架构比如聊天服务器并发局限于应用中编程比较复杂JDK1.4开始支持。 AIO方式使用于连接数目多且连接比较长重操作的架构比如相册服务器充分调用OS参与并发操作编程比较复杂JDK7开始支持。Netty!