【Java高級】JavaにおけるNIO非遮断socket通信

8023 ワード

伝統的なブロックIO
    従来の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.java
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(200, 124));

		for (int i = 0; i < 10; i++) {

			SumClient sumClient2 = new SumClient();
			System.out.println("      :" + sumClient2.getSum(0, i));

		}

	}
}