利用NIO構造簡單的聊天功能


public class SocketSelector {

	private final static int PORT = 1234;
	
	public static void main(String[] args) throws IOException {
		
		new SocketSelector().init(args);
		
	}
	
	public void init(String... args) throws IOException{
		
		int port = PORT;
		
		if (args.length != 0) {
			port = Integer.parseInt(args[0]);
		}
		
		Selector selector = Selector.open();
		
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		serverSocketChannel.configureBlocking(false);
		serverSocketChannel.socket().bind(new InetSocketAddress(port));
		
		System.out.println("1111");
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		System.out.println("wille to cycle..");
		while(true){
			
			int n = selector.select();
	
			if (n == 0) {
				continue;
			}
		<span style="white-space:pre">	</span>System.out.println(n);
			Iterator it = selector.selectedKeys().iterator();
			
			while(it.hasNext()){
				
				SelectionKey selectionKey = (SelectionKey) it.next();
				it.remove();//如果去掉這句話,在acceptable裏,sayhello函數裏socketchannel會是個null值。我猜測每個key只能使用一次
				System.out.println(selectionKey.interestOps());	
				if(selectionKey.isAcceptable()){
					
					ServerSocketChannel serverSocketChannel2 = (ServerSocketChannel) selectionKey.channel();
					System.out.println(selectionKey.interestOps());
					SocketChannel socketChannel = serverSocketChannel2.accept();
					
					registerChannel(selector,socketChannel,SelectionKey.OP_READ);
		
					sayHello(socketChannel);
					
				}
				
				if(selectionKey.isReadable()){
					//System.out.println("有讀的來了");
					readDataFromChannel(selectionKey);
					
				}
				
				
			}
			
		}
		
	}

	private ByteBuffer buf = ByteBuffer.allocate(1024);
	
	protected void readDataFromChannel(SelectionKey selectionKey) throws IOException {

		SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
		buf.clear();
		//buf.flip();取消註釋,單個客戶端發送一條數據,服務器端就無限死循環,輸出“1 1 有讀的來了”;第一個1爲int n的值,第二個1是key.interestOps的值,1代表Readable
		int count = 0;
		while((count=socketChannel.read(buf))>0){
			System.out.println("count:"+count+",buf.size:"+buf.position());
			//while(buf.hasRemaining()){
				System.out.println("hasRemaining:"+buf.hasRemaining()+",buf.size:"+buf.position());
				byte[] data = buf.array();
				String str = new String(data);
				
				System.out.println(str);
				
			//}
			
			buf.clear();
			
		}
		
		if (count < 0) {
<span style="white-space:pre">			System.out.println("close the channel...");</span>
			socketChannel.close();
		}
		
	}

	private void sayHello(SocketChannel socketChannel) throws IOException {

		buf.clear();
		buf.put("hellp!\r\n".getBytes());
		buf.flip();
		System.out.println("will to send...");
		socketChannel.write(buf);
		System.out.println("achieve to send...");
		buf.clear();
		
	}

	private void registerChannel(Selector selector,
			SocketChannel socketChannel, int opRead) throws IOException {

		if (socketChannel == null) {
			return;
		}
		
		socketChannel.configureBlocking(false);
		socketChannel.register(selector, opRead);
		System.out.println("註冊成功——讀");		

	}
	
}




public class ClientSocketSelector {

	private static final int PORT = 1234;
	private static final String IP = "10.20.4.122";
	
	
	public static void main(String[] args) throws IOException {
		
		new ClientSocketSelector().init(args);
		
	}
	
	public void init(String...args) throws IOException{
		
		Selector selector = Selector.open();
		
		int port = PORT;
		String ip = IP;
		
		if(args.length != 0){
			
			ip = args[0];
			port = Integer.parseInt(args[1]);
						System.out.println(ip);
			System.out.println(port);
			
		}
		
		SocketChannel socketChannel = SocketChannel.open();
		
		
		socketChannel.configureBlocking(false);
		socketChannel.connect(new InetSocketAddress("localhost", port));//不能用socketChannel.socket().connect();親身實踐證明!用了就不能正常通信
		socketChannel.register(selector, SelectionKey.OP_CONNECT);
		while(true){
			
			int n = selector.select();
			if (n == 0) {
				continue;
			}
			
			Iterator it = selector.selectedKeys().iterator();
			
			while(it.hasNext()){
				
				SelectionKey key = (SelectionKey) it.next();
				it.remove();
				if (key.isConnectable()) {
					
					SocketChannel sc = (SocketChannel) key.channel();
					System.out.println("isConnectionPending:"+sc.isConnectionPending());
					if (sc.isConnectionPending()) 
						sc.finishConnect();
					System.out.println("will to send...");
					sc.write(ByteBuffer.wrap("你好".getBytes()));
					System.out.println("achieve to send...");
					sc.register(selector, SelectionKey.OP_READ);
					
				}
				if (key.isReadable()) {
					
					readDataFromChannel(key);
					
				}
				
				
				
			}
			
		}
		
	}

	private ByteBuffer buf = ByteBuffer.allocate(1024);
	
	protected void readDataFromChannel(SelectionKey key) throws UnsupportedEncodingException, IOException {

		SocketChannel sc = (SocketChannel) key.channel();
		
		buf.clear();
		
		if (!sc.isConnected()) {
			return;
		}
		
		int count = 0;
		while((count = sc.read(buf)) > 0){
			System.out.println(buf.position());
			String str = new String(buf.array());
			System.out.println(str);
		
		}
		
	}
	
}

原本以爲只要一直讓通道處於就緒狀態就能不斷的接收數據(IsReadable),測試後發現,一直處於被選擇的鍵中,不能夠接收到最新的數據。

只有將該鍵從已選擇的鍵中取消,才能重新獲得數據。

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