java中NIO的非阻塞通信

使用NIO完成網絡通信的三個要點:

	1)通道(Channel):負責連接
			
		   java.nio.channels.Channel 接口:
				|--SelectableChannel
					|--SocketChannel
					|--ServerSocketChannel
					|--DatagramChannel

					|--Pipe.SinkChannel
					|--Pipe.SourceChannel

	2)緩衝區(Buffer):負責數據的存取

	3)選擇器(Selector):是SelectableChannel的多路複用器,用於監控SelectableChannel的IO狀況。

	說明:
		1)非阻塞模式是針對網絡IO而言的。
		2)FileChannel不能切換成非阻塞模式。

		
舉例:

	阻塞式的網絡IO:

		import java.io.IOException;
		import java.net.InetSocketAddress;
		import java.nio.ByteBuffer;
		import java.nio.channels.FileChannel;
		import java.nio.channels.ServerSocketChannel;
		import java.nio.channels.SocketChannel;
		import java.nio.file.Paths;
		import java.nio.file.StandardOpenOption;
		import org.junit.Test;

		public class TestBlockingNIO {

			@Test
			public void client() throws IOException{
				//1. 獲取通道
				SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9999));
				//2. 分配指定大小的緩衝區
				ByteBuffer buf = ByteBuffer.allocate(1024);
				//3. 讀取本地文件,併發送到服務端
				FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
				while(inChannel.read(buf) != -1){
					buf.flip();
					sChannel.write(buf);
					buf.clear();
				}
				//4. 關閉通道
				inChannel.close();
				sChannel.close();
			}
			
			@Test
			public void server() throws IOException{
				//1. 獲取通道
				ServerSocketChannel ssChannel = ServerSocketChannel.open();
				//2. 綁定連接
				ssChannel.bind(new InetSocketAddress(9999));
				//3. 獲取客戶端連接的通道
				SocketChannel sChannel = ssChannel.accept(); // 阻塞式的等待遠程連接
				//4. 分配指定大小的緩衝區
				ByteBuffer buf = ByteBuffer.allocate(1024);
				//5. 接收客戶端的數據,並保存到本地
				FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
				while(sChannel.read(buf) != -1){
					buf.flip();
					outChannel.write(buf);
					buf.clear();
				}
				//6. 關閉通道
				sChannel.close();
				outChannel.close();
				ssChannel.close();
			}
		}

	
	非阻塞式的網絡IO:
	
		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.ServerSocketChannel;
		import java.nio.channels.SocketChannel;
		import java.util.Date;
		import java.util.Iterator;
		import java.util.Scanner;
		import org.junit.Test;

		public class TestNonBlockingNIO {
			
			@Test
			public void client() throws IOException{
				//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.next();
					buf.put((new Date().toString() + "\n" + str).getBytes());
					buf.flip();
					sChannel.write(buf);
					buf.clear();
				}
				//5. 關閉通道
				scan.close();
				sChannel.close();
			}

			@Test
			public void server() 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){
					
					//7. 獲取當前選擇器中所有註冊的“選擇鍵(已就緒的監聽事件)”
					Iterator<SelectionKey> 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()){
							//10. 獲取當前選擇器上“讀就緒”狀態的通道
							SocketChannel sChannel = (SocketChannel) sk.channel();
							
							//11. 讀取數據
							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. 取消選擇鍵 SelectionKey
						it.remove();
					}
				}
			}
		}


		
		
		
		
	
	
	

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章