【Java高級】JavaにおけるNIO非遮断socket通信
8023 ワード
伝統的なブロックIO
従来のIO通信では、サーバを作成する各具体的なステップを分析することができる。まずServerSocketを作成します。
ブロックなしNIO
1.Buffer 従来のI/Oは対象資源の無駄が絶えない(通常はStering)。新しいI/OはBufferを使ってデータを読み書きすることによって資源の浪費を免れました。Bufferオブジェクトは線形で、順序正しいデータセットであり、そのクラスによっては唯一のデータタイプしか含まれない。java.nio.Buffer類の説明 java.nio.ByteBufferはバイトタイプを含みます。ReadableByteChanelからWritable ByteChanelに書いてもいいです。 java.nio.MappedByteBufferはバイトタイプを含んで、直接メモリのある領域にマッピングします。 java.nio.ハーバーバーは文字タイプが含まれていますので、通路に書き込めません。 java.nio.DoubleBufferはdoubleタイプを含んでいます。チャンネルに書き込めません。 java.nio.Float Bufferはfloatタイプを含みます。 java.nio.IntBufferにはintタイプが含まれています。 java.nio.LongBufferはlongタイプを含みます。 java.nio.ShotBufferはショートタイプを含みます。 allocate(int capacity)方法またはallocateDirect(int capacity)方法を呼び出すことにより、一つのBufferを割り当てることができる。特に、MappedBytes Bfferを作成して、FileChanel.mapを呼び出すことができます。直接(direct)bufferはメモリに一連のブロックを割り当て、ローカルアクセス方法を使ってデータを読み書きます。直接ではない(nondirect)bufferは、Javaの配列アクセスコードを使ってデータを読み書きします。例えばByteBuffer.wrap(byte[])などの任意のwrap方法を使用して、Java配列に基づいてbufferを作成しなければならない場合がある。2.文字コード ByteBufferにデータを格納することは、バイトの順序と文字の変換に関する2つの問題がある。ByteBuffer内部ではByteOrderクラスでバイト順の問題を処理していますが、文字変換は処理されていません。実はByteBufferはStringを読む方法を提供していません。Java.nio.chaset.hasetは文字変換の問題を処理しました。これは、CharrsetEncoderとCharetDecoderを作成することにより、文字列をバイトと逆変換に変換します。3.通路(Chanel) 既存のjava.io類にはBufferタイプが一つもないことに気づくかもしれませんので、NIOではChanel類を提供してBufferを読みます。チャネルは、特定のデバイス、プログラム、またはネットワークへの接続と考えられてもよい。 GatheringByteChanelは複数のBufferのデータを一度に使ってチャネルに書き込むことができます。反対に、ScanteringByteChanelは一回に複数のBufferにデータをチャネルから読み込むことができます。チャンネルを設定してもいいです。つまり、I/Oをブロックしたり、ブロックしたりしないようにしてください。チャネルを従来のI/Oクラスに適合させるために、Chanelクラスは、スタティック方法を提供してStreamまたはReader 4.Selectorを作成する。 過去の渋滞I/Oでは、streamにいつ読んだり書いたりできるかをよく知っています。方法が呼出されて、streamが準備されたら戻ってきます。しかし、非閉塞通路を使うには、いつ通路が用意されているかを知る方法が必要です。NIOバッグの中で、Selectorを設計するのはこの目的のためです。Selectable Chanelは、イベント発生時にアプリケーションに通知するのではなく、特定のイベントを登録することができます。チャネル追跡イベント。そして、Selector上の任意のセレクション方法を適用すると、登録されたチャネルを確認し、関心のあるイベントが発生したかどうかを確認する。 すべてのチャンネルがすべての操作をサポートしているわけではない。Selection Keyクラスは、可能なすべての操作ビットを定義し、2回使用する。まず、Selectable Chanel.register(Selector sel,int op)方法を適用してチャネルを登録すると、必要な操作を第二のパラメータとして方法に伝達する。その後、セレクションキーが選択されると、Selection KeyのreadyOps()方法は、全チャネルのサポート動作の数ビットの和を返す。SelectabeChanelのvalidOps方法は、各チャネルで許可された動作を返します。登録チャネルがサポートされていない操作は、IllegargmentException異常を引き起こします。以下の表には、Selectable Channelクラスでサポートされている操作を示します。ServerSocketChanel OP_ACCEPT SocketChanel OP_CONNET、OP_READ、OP_WRITE Datagram Chanel OP_READ、OP_WRITE Pipe.SourceChanel OP_READ Pipe.Sink Chanel OP_WRITE
次はnioのデモです。
Sum Server.java
従来のIO通信では、サーバを作成する各具体的なステップを分析することができる。まずServerSocketを作成します。
ServerSocket server=new ServerSocket(10000);
新しい接続要求を受け付けます。 Socket newConnection=server.accept();
// accept , ServerSocket 。 , 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);
}
このような操作には二つの問題があります。まずBufferedReader類のreadline()方法はそのバッファが満杯していない時にスレッドが詰まり、一定のデータだけがバッファを満たしています。またはクライアントがソケットを閉じています。方法は戻ります。第二に、大量のゴミが発生します。バッファルダはバッファを作成して、顧客ソケットからデータを読み込みますが、これらのデータを格納する文字列を作成しました。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();
同様に、読み書きの操作がブロックされていて、流れの中に一度に文字を書き込むと効率が悪くなりますので、バッファを使うべきですが、バッファを使うとまた多くのゴミが出ます。従来の解決法は通常、JavaでブロックI/Oを処理するためにスレッド(大量のスレッド)を使用する。要求を処理するためのスレッドプールが一般的に実装されているが、スレッドはサーバに複数の接続を処理することができるようにしているが、それらも同様に多くの問題を引き起こしている。各スレッドは自分のスタックスペースを持ち、CPUの時間を占用しています。時間はかかりますが、ブロックされたI/Oの操作に無駄が多く、CPUを有効に利用していません。ブロックなしNIO
1.Buffer 従来のI/Oは対象資源の無駄が絶えない(通常はStering)。新しいI/OはBufferを使ってデータを読み書きすることによって資源の浪費を免れました。Bufferオブジェクトは線形で、順序正しいデータセットであり、そのクラスによっては唯一のデータタイプしか含まれない。java.nio.Buffer類の説明 java.nio.ByteBufferはバイトタイプを含みます。ReadableByteChanelからWritable ByteChanelに書いてもいいです。 java.nio.MappedByteBufferはバイトタイプを含んで、直接メモリのある領域にマッピングします。 java.nio.ハーバーバーは文字タイプが含まれていますので、通路に書き込めません。 java.nio.DoubleBufferはdoubleタイプを含んでいます。チャンネルに書き込めません。 java.nio.Float Bufferはfloatタイプを含みます。 java.nio.IntBufferにはintタイプが含まれています。 java.nio.LongBufferはlongタイプを含みます。 java.nio.ShotBufferはショートタイプを含みます。 allocate(int capacity)方法またはallocateDirect(int capacity)方法を呼び出すことにより、一つのBufferを割り当てることができる。特に、MappedBytes Bfferを作成して、FileChanel.mapを呼び出すことができます。直接(direct)bufferはメモリに一連のブロックを割り当て、ローカルアクセス方法を使ってデータを読み書きます。直接ではない(nondirect)bufferは、Javaの配列アクセスコードを使ってデータを読み書きします。例えばByteBuffer.wrap(byte[])などの任意のwrap方法を使用して、Java配列に基づいてbufferを作成しなければならない場合がある。2.文字コード ByteBufferにデータを格納することは、バイトの順序と文字の変換に関する2つの問題がある。ByteBuffer内部ではByteOrderクラスでバイト順の問題を処理していますが、文字変換は処理されていません。実はByteBufferはStringを読む方法を提供していません。Java.nio.chaset.hasetは文字変換の問題を処理しました。これは、CharrsetEncoderとCharetDecoderを作成することにより、文字列をバイトと逆変換に変換します。3.通路(Chanel) 既存のjava.io類にはBufferタイプが一つもないことに気づくかもしれませんので、NIOではChanel類を提供してBufferを読みます。チャネルは、特定のデバイス、プログラム、またはネットワークへの接続と考えられてもよい。 GatheringByteChanelは複数のBufferのデータを一度に使ってチャネルに書き込むことができます。反対に、ScanteringByteChanelは一回に複数のBufferにデータをチャネルから読み込むことができます。チャンネルを設定してもいいです。つまり、I/Oをブロックしたり、ブロックしたりしないようにしてください。チャネルを従来のI/Oクラスに適合させるために、Chanelクラスは、スタティック方法を提供してStreamまたはReader 4.Selectorを作成する。 過去の渋滞I/Oでは、streamにいつ読んだり書いたりできるかをよく知っています。方法が呼出されて、streamが準備されたら戻ってきます。しかし、非閉塞通路を使うには、いつ通路が用意されているかを知る方法が必要です。NIOバッグの中で、Selectorを設計するのはこの目的のためです。Selectable Chanelは、イベント発生時にアプリケーションに通知するのではなく、特定のイベントを登録することができます。チャネル追跡イベント。そして、Selector上の任意のセレクション方法を適用すると、登録されたチャネルを確認し、関心のあるイベントが発生したかどうかを確認する。 すべてのチャンネルがすべての操作をサポートしているわけではない。Selection Keyクラスは、可能なすべての操作ビットを定義し、2回使用する。まず、Selectable Chanel.register(Selector sel,int op)方法を適用してチャネルを登録すると、必要な操作を第二のパラメータとして方法に伝達する。その後、セレクションキーが選択されると、Selection KeyのreadyOps()方法は、全チャネルのサポート動作の数ビットの和を返す。SelectabeChanelのvalidOps方法は、各チャネルで許可された動作を返します。登録チャネルがサポートされていない操作は、IllegargmentException異常を引き起こします。以下の表には、Selectable Channelクラスでサポートされている操作を示します。ServerSocketChanel OP_ACCEPT SocketChanel OP_CONNET、OP_READ、OP_WRITE Datagram Chanel OP_READ、OP_WRITE Pipe.SourceChanel OP_READ Pipe.Sink Chanel OP_WRITE
次はnioのデモです。
Sum Server.java
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));
_serverChannel.configureBlocking(false);//
System.out.println(" ");
}
// private void waitForConnection() throws IOException {
// while (true) {
// _clientChannel = _serverChannel.accept();
// if (_clientChannel != null) {
// System.out.println(" ");
// processRequest();
// _clientChannel.close();
// }
// System.out.println("buttom");
// }
// }
private void waitForConnection() throws IOException {
Selector acceptSelector = SelectorProvider.provider().openSelector();
/*
* selector accept 。
* Selector, accept ready , , I/O 。
*/
SelectionKey acceptKey = _serverChannel.register(acceptSelector,
SelectionKey.OP_ACCEPT);
int keysAdded = 0;
/* select */
while ((keysAdded = acceptSelector.select()) > 0) {
// I/O , ready
System.out.println("wait---1");
Set readyKeys = acceptSelector.selectedKeys();
Iterator i = readyKeys.iterator();
// ready ,
while (i.hasNext()) {
System.out.println("wait---2");
SelectionKey sk = (SelectionKey) i.next();
i.remove();
System.out.println("wait---3");
ServerSocketChannel nextReady = (ServerSocketChannel) sk
.channel();
System.out.println("wait---4");
//
_clientChannel = nextReady.accept().socket().getChannel();
processRequest();
System.out.println("wait---5");
_clientChannel.close();
System.out.println("waitup");
}
System.out.println("wait");
}
}
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();
}
}
SumCient.javapublic 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(200, 124));
for (int i = 0; i < 10; i++) {
SumClient sumClient2 = new SumClient();
System.out.println(" :" + sumClient2.getSum(0, i));
}
}
}