Nio入門實現簡單的服務端和客戶端通信

Java.nio全稱java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,爲所有的原始類型(boolean類型除外)提供緩存支持的數據容器,使用它可以提供非阻塞式的高伸縮性網絡。目前很多通信都是基於Nio來實現。下面分享一個入門的案例。

服務端的代碼如下:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

public class NioServer {

	public static void main(String[] args) throws IOException {
		NioServer nioServer = new NioServer();
		nioServer.start();
	}
	
	
	/**
	 * 啓動服務器
	 * @throws IOException 
	 */
	public void start() throws IOException {
		
		//1創建一個Selector
		Selector selector = Selector.open();
		
		//2通過ServerSocketChannel創建channel通道
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		
		//3爲channel通道綁定端口
		serverSocketChannel.bind(new InetSocketAddress(8010));
		
		//4設置channel爲非阻塞模式
		serverSocketChannel.configureBlocking(false);
		
		//5講channel註冊到selector上,監聽鏈接事件
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("服務器啓動成功...");
		
		//6循環,等待新進入的鏈接
		while(true){
			//是一個阻塞方法,只有監聽事件就緒了纔會返回,
			/**
			 * TODO 獲取可用的channel數量
			 */
			int readyChannels = selector.select();
			
			if(readyChannels == 0) {
				continue;
			}
			
			/**
			 * 獲取可用Channel的集合
			 */
			Set<SelectionKey> selectionKeys = selector.selectedKeys();
			Iterator<SelectionKey> iterable = selectionKeys.iterator();
			while(iterable.hasNext()) {
				/**
				 * selectionKey實例
				 */
				SelectionKey selectionKey = iterable.next();
				
				/**
				 * 移除當前的selectionKey
				 */
				iterable.remove();
				
				
				//7根據就緒狀態調用對應方法處理業務邏輯
				/**
				 * 如果是接入事件
				 */
				if(selectionKey.isAcceptable()) {
					acceptHandler(serverSocketChannel,selector);
				}
				/**
				 * 如果是可讀事件
				 */
				if(selectionKey.isReadable()) {
					readHandler(selectionKey, selector);
				}
			}
		}
	}
	
	
	/**
	 * 接入事件處理
	 * @throws IOException 
	 */
	private void acceptHandler(ServerSocketChannel serverSocketChannel,Selector selector) throws IOException {
		/**
		 * 如果是接入事件,創建socketChannel
		 */
		SocketChannel socketChannel = serverSocketChannel.accept();
		
		/**
		 * 將socketChannel設置爲非阻塞工作模式
		 */
		socketChannel.configureBlocking(false);
		
		/**
		 * 講channel註冊到selector上,監聽可讀事件
		 */
		socketChannel.register(selector, SelectionKey.OP_READ);
		
		/**
		 * 回覆客戶端提示信息
		 */
		socketChannel.write(Charset.forName("UTF-8").encode("您連接成功了"));
	}
	
	
	/**
	 * 可讀事件處理
	 * @throws IOException 
	 */
	private void readHandler(SelectionKey selectionKey,Selector selector) throws IOException {
		
		/**
		 * 要從selectionKey中獲取到已經就緒的channel
		 */
		SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
		
		/**
		 * 創建buffer
		 */
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		
		/**
		 * 將channel再次註冊到selector上,監聽他的可讀事件
		 */
		String request = "";
		while(socketChannel.read(buffer) > 1) {
			/**
			 * 切換爲讀模式
			 */
			buffer.flip();
			
			/**
			 * 讀取buffer中的內容
			 */
			request += Charset.forName("UTF-8").decode(buffer);
			
		}
		/**
		 * 將客戶端發送的請求信息,廣播給所有其他客戶端
		 */
		broadCast(selector,socketChannel,request);
		
		socketChannel.register(selector, SelectionKey.OP_READ);

		if(request.length() > 0) {
			System.out.println(request);
		}
		
		
	}
	
	/**
	 * 廣播
	 * @throws IOException 
	 */
	private void broadCast(Selector selector,SocketChannel sourceChannel,String request) throws IOException {
		/**
		 * 獲取所有已經接入的客戶端channel
		 */
		Set<SelectionKey> selectionKeys = selector.keys();
		Iterator<SelectionKey> it = selectionKeys.iterator();
		
		/**
		 * 循環向所有channel發送數據
		 */
		while(it.hasNext()) {
			SelectionKey selectionKey = it.next();
			Channel channel = selectionKey.channel();
			
			//剔除發消息的客戶端
			if(channel instanceof SocketChannel && channel != sourceChannel) {
				((SocketChannel)channel).write(Charset.forName("UTF-8").encode(request));
			}
		}
	}
}

客戶端的代碼如下:

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.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class NioClient {

	public static void main(String[] args) throws IOException {
		
		/**
		 * 連接服務器
		 */
		SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost",8010));
		System.out.println("客戶端啓動成功...");
		
		/**
		 * 接收服務器端的響應數據
		 * 新開一個線程
		 */
		Selector selector = Selector.open();
		socketChannel.configureBlocking(false);
		socketChannel.register(selector,SelectionKey.OP_READ);
		new Thread(new NioClientHandler(selector)).start();
		
		
		/**
		 * 向服務端發送數據
		 */
		Scanner scanner = new Scanner(System.in);
		while(scanner.hasNext()) {
			String request = scanner.nextLine();
			if(request != null && request.length() > 0) {
				socketChannel.write(Charset.forName("UTF-8").encode(request));
			}
		}
	}
}


class NioClientHandler implements Runnable{

	private Selector selector;
	
	public NioClientHandler(Selector selector) {
		this.selector = selector;
	}

	
	
	/**
	 * 可讀事件處理
	 * @throws IOException 
	 */
	private void readHandler(SelectionKey selectionKey,Selector selector) throws IOException {
		
		/**
		 * 要從selectionKey中獲取到已經就緒的channel
		 */
		SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
		
		/**
		 * 創建buffer
		 */
		ByteBuffer buffer = ByteBuffer.allocate(1024);
		
		/**
		 * 將channel再次註冊到selector上,監聽他的可讀事件
		 */
		String request = "";
		while(socketChannel.read(buffer) > 1) {
			/**
			 * 切換爲讀模式
			 */
			buffer.flip();
			
			/**
			 * 讀取buffer中的內容
			 */
			request += Charset.forName("UTF-8").decode(buffer);
		}
		/**
		 * 將服務器端的信息打印出來
		 */
		socketChannel.register(selector, SelectionKey.OP_READ);

		if(request.length() > 0) {
			System.out.println(request);
		}
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		
		while(true){
			//是一個阻塞方法,只有監聽事件就緒了纔會返回,
			/**
			 * TODO 獲取可用的channel數量
			 */
			int readyChannels = 0;
			try {
				readyChannels = selector.select();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			
			if(readyChannels == 0) {
				continue;
			}
			
			/**
			 * 獲取可用Channel的集合
			 */
			Set<SelectionKey> selectionKeys = selector.selectedKeys();
			Iterator<SelectionKey> iterable = selectionKeys.iterator();
			while(iterable.hasNext()) {
				/**
				 * selectionKey實例
				 */
				SelectionKey selectionKey = iterable.next();
				
				/**
				 * 移除當前的selectionKey
				 */
				iterable.remove();
				
				/**
				 * 如果是可讀事件
				 */
				if(selectionKey.isReadable()) {
					try {
						readHandler(selectionKey, selector);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
		}
	}
}

有問題可以在下面評論,技術問題可以私聊我哦。

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