JAva nioとioの比較

11265 ワード

参照
第一部:NIOの簡単な紹介
    サーバが適切な時間内に多数のクライアントの要求を処理する能力は、サーバがI/Oストリームを使用する効率に依存し、同時に何百人ものクライアントにサービスを提供するサーバが同時にI/Oサービスを使用できる必要がある.
    Java言語で書かれたサーバは、そのスレッドとクライアントの比がほぼ1対1であるため、大量のスレッドオーバーヘッドの影響を受けやすく、その結果、パフォーマンスの問題を引き起こし、伸縮性に欠けます.
    Java.nioパッケージの導入はスレッドオーバーヘッドの問題を解決し、パッケージの中で最も重要なのは新しいSelectableChannelクラスとSelectorクラスです.チャネル(channel)は、クライアントとサーバとの間の通信方式である.セレクタ(selector)は、Windowsメッセージループと同様に、異なるクライアントから様々なイベントをキャプチャし、対応するイベントハンドラに割り当てます.この文書では、Javaプラットフォームの非ブロックI/Oメカニズムを作成するために、この2つのクラスがどのように連携して動作するかを示します.主に4つのクラスがあります.
1.Buffer:データが含まれ、読み書きに使用される線形テーブル構造です.メモリマッピングファイルのI/O操作用の特殊なクラスも用意されています.
2.Charset:Unicode文字列がバイトシーケンスに投影され、逆投影される操作を提供します.
3.Channels:socket、file、pipeの3つのパイプを含み、実際には双方向交流の通路です.
4.Selector:多重非同期I/O操作を1つまたは複数のスレッドに集約します(Unixのselect()関数またはWin 32のWaitForSingleEvent()関数のオブジェクト向けバージョンと見なすことができます).
第二部分:伝統IO
サーバを作成する各ステップを分析できます.まず、Server Socketを作成します.
ServerSocket server=new ServerSocket(10000);
その後、新しい接続要求を受け入れます.
Socket newConnection=server.accept();
ACceptメソッドの呼び出しは、サーバソケットが接続要求を受信するまでブロックされます.接続要求が受け入れられると、サーバはクライアントsocketの要求を読み取ることができる.
InputStream in = newConnection.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
BufferedReader buffer = new BufferedReader(reader);
Request request = new Request();
while(!request.isComplete()) {
String line = buffer.readLine();
request.addLine(line);
}
このような操作には2つの問題があります.まず、BufferedReaderクラスのreadLine()メソッドは、バッファが満たされていない場合にスレッドがブロックされ、バッファが満たされているか、お客様がソケットを閉じている場合にのみ、メソッドが返されます.次に、大量のゴミが発生し、BufferedReaderはバッファを作成して顧客ソケットからデータを読み込むが、同じように文字列を作成してデータを格納する.BufferedReader内部ではStringBufferがこの問題を処理していますが、すべてのStringはすぐにゴミ回収になります.
同様の問題は送信応答コードにも存在する
Response response = request.generateResponse();
OutputStream out = newConnection.getOutputStream();
InputStream in = response.getInputStream();
int ch;
while(-1 != (ch = in.read())) {
out.write(ch);
}
newConnection.close();
同様に、読み書き操作がブロックされ、ストリームに1文字ずつ書き込むと効率が低下するため、バッファを使用する必要がありますが、バッファを使用すると、ストリームはより多くのゴミを発生します.
従来の解決策
通常、JavaでブロックI/Oを処理するにはスレッド(大量のスレッド)が使用されます.一般に、図2のような要求を処理するためのスレッドプールを実装する.
第3部:新IO:NIO
1. Buffer
従来のI/Oはオブジェクトリソースを絶えず浪費している(通常はString).新しいI/Oは、Bufferを使用してデータを読み書きすることで、リソースの浪費を回避します.Bufferオブジェクトは、カテゴリに基づいて一意のデータ型のみを含む線形で秩序あるデータセットです.
JAva.nio.Bufferクラスの説明
JAva.nio.ByteBufferにはバイトタイプが含まれています.ReadableByteChannelからWritableByteChannelでの書き込みを読むことができます
JAva.nio.MappedByteBufferは、メモリのある領域に直接マッピングされるバイトタイプを含む
JAva.nio.CharBufferは文字タイプが含まれており、チャネルに書き込めません
JAva.nio.DoubleBufferにはdoubleタイプが含まれており、チャネルに書き込めません
JAva.nio.FloatBufferにはfloatタイプが含まれています
JAva.nio.IntBufferはintタイプを含む
JAva.nio.LongBufferはlongタイプを含む
JAva.nio.ShortBufferはshortタイプを含む
Bufferはallocate(int capacity)メソッドまたはallocateDirect(int capacity)メソッドを呼び出すことによって割り当てることができる.特に、FileChannel.map(int mode,long position,int size)を呼び出すことで、MappedBytesBufferを作成できます.直接(direct)bufferは、メモリに連続したブロックを割り当て、ローカルアクセスメソッドを使用してデータを読み書きします.非直接(nondirect)bufferは、Javaの配列を使用してコード読み書きデータにアクセスします.ByteBuffer.wrap(byte[])などの任意のwrapメソッドを使用してJava配列に基づいてbufferを作成するなど、非直接バッファを使用する必要がある場合があります.
2.文字コード
ByteBufferにデータを格納するには、バイトの順序と文字変換の2つの問題があります.ByteBuffer内部ではByteOrderクラスでバイト順の問題が処理されているが,文字変換は処理されていない.実際、ByteBufferはStringを読み書きする方法を提供していません.
Java.nio.charset.Charsetは文字変換の問題を処理します.CharsetEncoderとCharsetDecoderを構築することで、文字シーケンスをバイトと逆変換に変換します.
3.チャンネル(Channel)
既存のjava.ioクラスには読み書き可能なBufferタイプがないことに気づくかもしれませんが、NIOではチャネルクラスがBufferを読み書きするために提供されています.チャネルは、特定のデバイス、プログラム、またはネットワークへの接続とみなすことができる.チャネルのクラスレベル構造図は図3の通りである.
図中のReadableByteChannelとWritableByteChannelはそれぞれ読み書きに使用される.
GatheringByteChannelは、複数のBufferのデータを一度にチャネルに書き込むことができ、逆にScatteringByteChannelは、複数のBufferに一度にデータを読み込むことができる.また、チャネルをブロックまたは非ブロックI/O操作サービスに設定することもできます.
チャネルを従来のI/Oクラスと適合させるために、チャネルクラスは静的方法でStreamまたはReaderを作成する
4. Selector
従来のブロックI/Oでは、streamが準備が整うまでメソッド呼び出しが返されるため、streamにいつ読み書きできるかが一般的に知られています.しかし、非ブロックチャネルを使用するには、チャネルがいつ準備されているかを知る方法が必要です.NIOパッケージでは、Selectorをデザインするのがその目的です.SelectableChannelは、イベントが発生したときにアプリケーションに通知したり、チャネルがイベントを追跡したりするのではなく、特定のイベントを登録することができます.次に、Selector上の任意のselectionメソッドを呼び出すと、登録されたチャネルが興味のあるイベントが発生したかどうかを確認します.図4はselectorと2つの登録済みチャネルの例である
すべてのチャネルがすべての操作をサポートしているわけではありません.SelectionKeyクラスは、2回使用される可能性のあるすべての操作ビットを定義します.まず、SelectableChannel.register(Selector sel,int op)メソッドを呼び出してチャネルを登録すると、必要な操作が2番目のパラメータとしてメソッドに渡されます.そして、selectionKeyが選択されると、selectionKeyのreadyOps()メソッドは、すべてのチャネルが動作をサポートするビットの和を返します.SelectableChannelのvalidOpsメソッドは、各チャネルで許可された動作を返します.登録チャネルでサポートされていない操作により、IllegalArgumentException例外が発生します.次の表に、SelectableChannelサブクラスでサポートされているアクションを示します.
ServerSocketChannel OP_ACCEPT
SocketChannel OP_CONNECT, OP_READ, OP_WRITE
DatagramChannel OP_READ, OP_WRITE
Pipe.SourceChannel OP_READ
Pipe.SinkChannel OP_WRITE

//1.         
//        , SocketChannelReader  SocketChannel        HTML // 。
package examples.nio;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.net.InetSocketAddress;
import java.io.IOException;
public class SocketChannelReader{

private Charset charset=Charset.forName("UTF-8");//  UTF-8   
private SocketChannel channel;
public void getHTMLContent(){
try{
connect();
sendRequest();
readResponse();
}catch(IOException e){
System.err.println(e.toString());
}finally{
if(channel!=null){
try{
channel.close();
}catch(IOException e){}
}
}
}
private void connect()throws IOException{//   CSDN
InetSocketAddress socketAddress=
new InetSocketAddress("http://www.csdn.net",80/);
channel=SocketChannel.open(socketAddress);
//      open    channel           
//   SocketChannel.open().connect(socketAddress);  
}
private void sendRequest()throws IOException{
channel.write(charset.encode("GET "
+"/document"
+"\r
\r
"));// GET CSDN // channel.write , CharByte , //Charset.encode(String) 。 } private void readResponse()throws IOException{// ByteBuffer buffer=ByteBuffer.allocate(1024);// 1024 while(channel.read(buffer)!=-1){ buffer.flip();//flip 。 System.out.println(charset.decode(buffer)); // Charset.decode buffer.clear();// } } public static void main(String [] args){ new SocketChannelReader().getHTMLContent(); }

//2.             
//     
package examples.nio;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.net.InetSocketAddress;
import java.io.IOException;
/**
* SumServer.java
*
*
*
* * @version 1.0
*/
public class SumServer {
private ByteBuffer _buffer=ByteBuffer.allocate(8);
private IntBuffer _intBuffer=_buffer.asIntBuffer();
private SocketChannel _clientChannel=null;
private ServerSocketChannel _serverChannel=null;
public void start(){
try{
openChannel();
waitForConnection();
}catch(IOException e){
System.err.println(e.toString());
}
}
private void openChannel()throws IOException{
_serverChannel=ServerSocketChannel.open();
_serverChannel.socket().bind(new InetSocketAddress(10000));
System.out.println("         ");
}
private void waitForConnection()throws IOException{
while(true){
_clientChannel=_serverChannel.accept();
if(_clientChannel!=null){
System.out.println("      ");
processRequest();
_clientChannel.close();
}
}
}
private void processRequest()throws IOException{
_buffer.clear();
_clientChannel.read(_buffer);
int result=_intBuffer.get(0)+_intBuffer.get(1);
_buffer.flip();
_buffer.clear();
_intBuffer.put(0,result);
_clientChannel.write(_buffer);
}
public static void main(String [] args){
new SumServer().start();
}
} // SumServer
//    
package examples.nio;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.SocketChannel;
import java.net.InetSocketAddress;
import java.io.IOException;
/**
* SumClient.java
*
*
* @version 1.0
*/
public class SumClient {
private ByteBuffer _buffer=ByteBuffer.allocate(8);
private IntBuffer _intBuffer;
private SocketChannel _channel;
public SumClient() {
_intBuffer=_buffer.asIntBuffer();
} // SumClient constructor

public int getSum(int first,int second){
int result=0;
try{
_channel=connect();
sendSumRequest(first,second);
result=receiveResponse();
}catch(IOException e){System.err.println(e.toString());
}finally{
if(_channel!=null){
try{
_channel.close();
}catch(IOException e){}
}
}
return result;
}
private SocketChannel connect()throws IOException{
InetSocketAddress socketAddress=
new InetSocketAddress("localhost",10000);
return SocketChannel.open(socketAddress);
}

private void sendSumRequest(int first,int second)throws IOException{
_buffer.clear();
_intBuffer.put(0,first);
_intBuffer.put(1,second);
_channel.write(_buffer);
System.out.println("       "+first+"+"+second);
}

private int receiveResponse()throws IOException{
_buffer.clear();
_channel.read(_buffer);
return _intBuffer.get(0);
}
public static void main(String [] args){
SumClient sumClient=new SumClient();
System.out.println("      :"+sumClient.getSum(100,324));
}
} // SumClient


//3.          
//   openChannel       
//_serverChannel.configureBlocking(false);//         
//  WaitForConnection       ,        
private void waitForConnection()throws IOException{
Selector acceptSelector = SelectorProvider.provider().openSelector();
/*          selector      accept     。
    Selector,      accept        ready 
 ,  ,       I/O  。*/
SelectionKey acceptKey = ssc.register(acceptSelector, 
SelectionKey.OP_ACCEPT);
int keysAdded = 0;

/*select                  */
while ((keysAdded = acceptSelector.select()) > 0) {
//             I/O   ,   ready   
Set readyKeys = acceptSelector.selectedKeys();
Iterator i = readyKeys.iterator();
//   ready   ,       
while (i.hasNext()) {
SelectionKey sk = (SelectionKey)i.next();
i.remove();
ServerSocketChannel nextReady = 
(ServerSocketChannel)sk.channel();
//           
_clientSocket = nextReady.accept().socket();
processRequest();
_clientSocket.close();
}
}
}