IOとNIO差
IOは、元のストリーム指向のブロッキング、NIOはブロック指向、非ブロッキングです。
1. IOストリームは、すべてのバイトが読み込まれるまで、彼らは他の場所にキャッシュされていない、と、データ・ストリームに移動することはできませんIOストリームは、ストリーム毎時間1バイト以上から読み込まれ、必要に応じてストリームから前後に移動しました読書の教示によれば、それは最初のキャッシュバッファにそれを解放する必要があります。ガイドバッファのJava NIOの方法が若干異なります。リードデータがバッファ場合ゆうに記載の後処理、可動バックするためのバッファと。これは、プロセスの柔軟性を向上させます。しかし、あなたはまた、バッファはあなたが衣服の数に対処するために必要なすべてが含まれているかどうかを確認する必要があります。また、より多くのデータをバッファに読み込まれるように、バッファ内のデータが処理されていない上書きしないことを確認してください。
2. JavaのIOがブロックされている様々な流れ。スレッドの呼び出しは()()または読み書きするときそこにいくつかのデータが読み出される、またはデータが完全に書き込まれるまで、スレッドがブロックされ、この手段。その間にスレッドが再び任意のものをそれを行うことはできません。JavaのNIO非ブロックモード、スレッドがチャネルからデータを読み出し要求を送信し、それだけのデータが現在利用可能な取得することができ、現在入手可能なデータが存在しない場合は、それが何かを得ることはありません。しかし、いないブロックされたスレッドを維持する前に、データがなるまで、スレッドは他のことをし続けることができます読み取ることができます。ノンブロッキング書き込みが真実です。チャンネルにいくつかのデータを書き込むために、それが完全に書き込まれるのを待つ必要はありません、このスレッドが同時に何かを行うことができますスレッドを要求。スレッドは、通常、他のチャネル上で入出力操作を実行するためのIOアイドル時間を非ブロックされるので、単一のスレッドは、現在の入力および出力チャネル(チャネル)を複数管理することができます。、
チャンネルチャンネル
NIO流路似ていますが、次のようにいくつかの違いがあります:
1.チャンネルを同時に読み取ることができる、いずれかの読み取りまたは書き込みを流れます。
2.非同期チャネルは、データを読み書きすることができます
前記データチャネルがバッファから読み取ることができ、データがバッファに書き込まれてもよいです。
バッファバッファクラス
バッファは、4つの基本的な属性を持つオブジェクトであり、仁王は読み取りと書き込みが実現するのに使用するバッファである、要するに、データバッファの読み出しを開始読み取り及び書き込みデータが第一バッファに書き込まれます。私たちは、最も一般的に使用されているJavaでのプリミティブ型の実装クラスのByteBufferは、などCharBufferの、のDoubleBufferとして、対応するバッファに対応する実装クラスを持っています
淀1 :前記の意味は、次の4つの属性である
データ要素の最大数は、バッファ内に収容することができる:容量(キャパシティ)。この容量は、バッファゾーンの作成に設定され、変更することはできません。
最初の読み取りまたは書き込むことができない要素バッファー:上部(リミット)に結合しました。または、バッファ要素内の既存の数。
次のインデックスは、要素を読み書きする:場所(位置)。場所は、それぞれのget()およびプット()関数によって自動的に更新されます。
マーク(マーク):次のインデックスには、要素を読んだり、書き込まれます。場所は、それぞれのget()およびプット()関数によって自動的に更新されます。
共通バッファ方法2ディアンは、次のとおりです。
フリップ():リードモードへの書き込みモード
巻き戻し():位置が0にリセットされ、それは一般的に読み取る繰り返し使用されます。
クリア():
コンパクト():リードデータがバッファの先頭部にコピーされません。
マーク():リセット():マークの位置をマークすることができ、リセット位置にリセットすることができます
3ディアン読み出し動作
1のFileInputStream InputStreamが= 新しい新規( "\\ A.TXT E"のFileInputStreamは)、 2 / ** 3。 *チャネルを取得 4。 * / 5 のFileChannelチャネル= inputStream.getChannel(); 6。 7。 / ** 8。 バッファを作成します* 。9 * / 10 のByteBufferバッファ= ByteBuffer.allocate(1024 ) 。11 12である / ** 13で データがバッファ読み取るために* 14 * / 15 channel.read(バッファ); 16 。17 buffer.flip(); 18れている 。19 、一方(buffer.remaining()> 0 ){ 20 バイト = B BUFFER.GET()。 21 のSystem.out.println(((CHAR )b)参照)。 22 } 23 / ** 24 *关闭流 25 * / 26 inputStream.close()。
4ディアン書き込み操作
1つの 静的 プライベート 最終 バイトメッセージ[] = {83,83,83,83,83,83 }。 2 3 静的 パブリック ボイドメイン(文字列引数は、[])スロー例外{ 4 のFileOutputStream FOUT = 新たFileOutputStream( "E:\\ A.TXTを" )。 5 6 のFileChannel FC = fout.getChannel()。 7 8 のByteBufferバッファ= ByteBuffer.allocate(1024 )。 9 10 のための(INTが I = 0 ++; I <message.length I){ 11 buffer.put(メッセージ[I])。 12 } 13 14 buffer.flip()。 15 16 fc.write(バッファ)。 17 18 fout.close()。 19 }
セレクタセレクタ
あなたは、イベントが読み取りまたは書き込みする準備ができているかどうかを確認するために、複数のNIOチャネルを検出することができます。その他のイベントとしてチャンネルは可能なスレッドが複数の要求を達成するように、セレクタに登録することができます。
非ブロックIO書き込みサーバ・プロセスでのNIOの使用、3つのステップがあります
1.レジスタセレクタオブジェクトへの関心のイベント
セレクタから目的のイベントで2取得
3.さまざまなイベントのために適切な治療
シンプルなAPIの紹介
セレクタの作成:オープン
可能なチャンネルのコレクションは:selectKeysを
選択:準備ができてチャンネルを選択します
シンプルなチャットルームは、コードのアイデアを実現しました
サーバ・コード
1 public class NioServer { 2 3 4 public void start() throws Exception { 5 /** 6 * 1.创建selector 7 */ 8 Selector selector = Selector.open(); 9 /** 10 * 2.通过ServerSocketChannel创建channel 11 */ 12 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 13 14 /** 15 * 3.为channel通道绑定监听端口 16 */ 17 serverSocketChannel.bind(new InetSocketAddress(8000)); 18 /** 19 * 4.设置channel 为非阻塞模式 20 */ 21 serverSocketChannel.configureBlocking(false); 22 /** 23 * 5.将channel 注册到selector,监听连接 24 */ 25 serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT); 26 System.out.println("服务器启动成功"); 27 /** 28 * 6.循环等待新接入的连接 29 */ 30 for(;;){ 31 int select = selector.select(); 32 if (select == 0){ 33 continue; 34 } 35 36 /** 37 * 7.获取可用channel的集合 38 */ 39 Set<SelectionKey> keys = selector.selectedKeys(); 40 Iterator<SelectionKey> iterator = keys.iterator(); 41 while (iterator.hasNext()){ 42 SelectionKey selectionKey = (SelectionKey) iterator.next(); 43 /** 44 * 8.移除selctionKey 45 */ 46 iterator.remove(); 47 /** 48 * 处理具体业务逻辑 49 */ 50 /** 51 * 接入事件 52 */ 53 if (selectionKey.isAcceptable()){ 54 acceptHandler(serverSocketChannel,selector); 55 } 56 /** 57 * 可读事件 58 */ 59 if(selectionKey.isReadable()){ 60 readHandler(selectionKey, selector); 61 } 62 } 63 64 65 } 66 67 68 /** 69 * 根据就绪状态,调用对应方法处理业务逻辑 70 */ 71 72 } 73 74 /** 75 * 接入事件处理器 76 */ 77 private void acceptHandler(ServerSocketChannel serverSocketChannel, Selector selector) throws Exception { 78 /** 79 * 如果是接入事件 创建serverSocket 80 */ 81 SocketChannel socketChannel = serverSocketChannel.accept(); 82 /** 83 * 设置非阻塞 84 */ 85 socketChannel.configureBlocking(false); 86 /** 87 * 注册进selector中 88 */ 89 socketChannel.register(selector, SelectionKey.OP_READ); 90 /** 91 * 回复服务端信息 92 */ 93 socketChannel.write(Charset.forName("UTF-8").encode("你与聊天室里其他人都不是朋友关系,请注意隐私安全")); 94 95 } 96 97 private void readHandler(SelectionKey selectionKey, Selector selector) throws Exception{ 98 /** 99 * 要用selectionKey中获取已经就绪的channel 100 */ 101 SocketChannel channel = (SocketChannel)selectionKey.channel(); 102 /** 103 * 创建buffer 104 */ 105 ByteBuffer buffer = ByteBuffer.allocate(1024); 106 /** 107 * 循环读取客户端数据 108 */ 109 String request = ""; 110 while (channel.read(buffer) > 0){ 111 /** 112 * 切换读模式 113 */ 114 buffer.flip(); 115 /** 116 * 读取buffer中的内容 117 */ 118 request += Charset.forName("UTF-8").decode(buffer); 119 120 } 121 /** 122 * 讲channel注册到selector上 123 */ 124 channel.register(selector, SelectionKey.OP_READ); 125 /** 126 * 讲客户端发送的请求信息,广播给其他客户端 127 */ 128 if (request.length() > 0){ 129 broadCast(selector, channel, request); 130 } 131 } 132 133 private void broadCast(Selector selector, SocketChannel socketChannel, String request){ 134 /** 135 * 获取到已接入的客户端hannel 136 */ 137 Set<SelectionKey> selectionKeys = selector.keys(); 138 selectionKeys.forEach(selectionKey -> { 139 Channel channel = selectionKey.channel(); 140 if (channel instanceof SocketChannel && 141 channel != socketChannel){ 142 try { 143 //将信息发送到channel客户端 144 ((SocketChannel) channel).write(Charset.forName("UTF-8").encode(request)); 145 } catch (IOException e) { 146 e.printStackTrace(); 147 } 148 } 149 }); 150 /** 151 * 循环向所有channel广播信息 152 */ 153 } 154 /** 155 * 156 * @param args 157 */ 158 public static void main(String[] args) throws Exception { 159 NioServer server = new NioServer(); 160 server.start(); 161 } 162 }
客户端代码
1 public class NioClient { 2 3 4 public void start() throws Exception { 5 /** 6 * 连接服务器 7 */ 8 SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8000)); 9 /** 10 * 接收服务器地址 11 */ 12 Selector selector = Selector.open(); 13 socketChannel.configureBlocking(false); 14 socketChannel.register(selector, SelectionKey.OP_READ); 15 new Thread(new NioClientHandler(selector)).start(); 16 /** 17 * 向服务器发送数据 18 */ 19 Scanner scanner = new Scanner(System.in); 20 while (scanner.hasNextLine()){ 21 String next = scanner.nextLine(); 22 if (StringUtils.isNotBlank(next)){ 23 socketChannel.write(Charset.forName("UTF-8").encode(next)); 24 } 25 } 26 } 27 28 public static void main(String[] args) throws Exception { 29 new NioClient().start(); 30 } 31 }
客户端线程类
1 public class NioClientHandler implements Runnable { 2 3 private Selector selector; 4 5 public NioClientHandler(Selector selector) { 6 this.selector = selector; 7 } 8 9 @Override 10 public void run() { 11 /** 12 * 循环等待新接入的连接 13 */ 14 try { 15 for(;;){ 16 int select = 0; 17 select = selector.select(); 18 19 if (select == 0){ 20 continue; 21 } 22 23 /** 24 * 获取可用channel的集合 25 */ 26 Set<SelectionKey> keys = selector.selectedKeys(); 27 Iterator<SelectionKey> iterator = keys.iterator(); 28 while (iterator.hasNext()){ 29 SelectionKey selectionKey = (SelectionKey) iterator.next(); 30 /** 31 * 移除selctionKey 32 */ 33 iterator.remove(); 34 /** 35 * 可读事件 36 */ 37 if(selectionKey.isReadable()){ 38 readHandler(selectionKey, selector); 39 } 40 } 41 } 42 } catch (Exception e) { 43 e.printStackTrace(); 44 } 45 46 47 } 48 49 private void readHandler(SelectionKey selectionKey, Selector selector) throws Exception{ 50 /** 51 * 要用selectionKey中获取已经就绪的channel 52 */ 53 SocketChannel channel = (SocketChannel)selectionKey.channel(); 54 /** 55 * 创建buffer 56 */ 57 ByteBuffer buffer = ByteBuffer.allocate(1024); 58 /** 59 * 循环读取客户端数据 60 */ 61 String request = ""; 62 while (channel.read(buffer) > 0){ 63 /** 64 * 切换读模式 65 */ 66 buffer.flip(); 67 /** 68 * 读取buffer中的内容 69 */ 70 request += Charset.forName("UTF-8").decode(buffer); 71 72 } 73 /** 74 * 讲channel注册到selector上 75 */ 76 channel.register(selector, SelectionKey.OP_READ); 77 /** 78 * 讲客户端发送的请求信息,广播给其他客户端 79 */ 80 if (request.length() > 0){ 81 System.out.println(request); 82 } 83 } 84 }