使用java nio 編寫簡易聊天室
阿新 • • 發佈:2018-11-10
伺服器端:相當於是一個接收客戶端訊息的分發器,為了簡單,直接在接收到客戶端的訊息後,
直接傳送給所有的客戶端
package chatroom.chatserver; import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; 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.Iterator; import java.util.Set; public class ChatServer { public static void main(String[] args) { new ChatServer().serverRun(); } public void serverRun() { Selector selector = null; try { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); ServerSocket socket = serverSocketChannel.socket(); socket.bind(new InetSocketAddress(8888)); selector = Selector.open(); // 這裡只是將該channel的accept設定為了selector感興趣的, // 此時並沒有準備好,只有當客戶端有連線請求的時候,這個channel的 // 感興趣事件才會準備好 SelectionKey register = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("啟動監聽:8888埠"); while (true) { // 這裡只有存在準備好的事件的時候,才會返回,否則會一直阻塞 // 怎麼才算準備好的? (當有相應的事件到來的時候,就說這個channel的這個事件準備好了) // accept 當客戶端有連線請求時,才會準備好,不過這裡要想能獲取到, // 必須之前註冊了accept事件,即設定為了感興趣的事件 // read 當客戶端有訊息到來時,才會準備好,前提也是設定了read的感興趣事件 // write 如果設定了write為感興趣事件,那麼每次都能在這裡獲取到,並執行寫事件, // 因為寫事件是服務端向客戶端寫事件,所以如果設定成了write事件,不修改成別的 // 事件的話,這裡將會一直執行write事件。 int select = selector.select(); System.out.println("監聽中。。。" + select); if (select > 0) { // 這裡獲取到準備好的SelectionKey的集合 // SelectionKey 我理解的就是一個pojo,它裡面封裝了該channel中selector感興趣的事件, // 準備好的事件,各種狀態,選擇器和channel等資訊,我們可以通過它獲取各種需要的資訊 Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); if (key.isValid() && key.isAcceptable()) { accept(key); } else if (key.isValid() && key.isReadable()) { read(key); } else if (key.isValid() && key.isWritable()) { write(key); } // 這裡一定要移除,不然會一直存在之前準備好的事件,出現異常 iterator.remove(); } } } } catch (IOException e) { e.printStackTrace(); } } private void write(SelectionKey key) { // 為了簡單,並沒有通過SelectionKey.OP_WRITE進行像所有客戶端分發訊息 } private void read(SelectionKey key) { SocketChannel channel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); try { int read = channel.read(buffer); if (read > 0) { System.out.println(read); buffer.flip(); String info = new String(buffer.array(), 0, read); System.out.println("伺服器接收到訊息:" + info); // 將收到的資訊發給聊天室的所有人 // 這裡獲取到的是所有的channel的Keys集合 Set<SelectionKey> selectedKeys = key.selector().keys(); if (!selectedKeys.isEmpty()) { Iterator<SelectionKey> iterator = selectedKeys.iterator(); while (iterator.hasNext()) { System.out.println("回寫資料"); SocketChannel channel2 = null; try { SelectionKey key1 = iterator.next(); if (key1.isValid() && !key1.isReadable()) { continue; } channel2 = (SocketChannel) key1.channel(); channel2.configureBlocking(false); channel2.write(buffer); buffer.rewind(); } catch (IOException e) { if (channel2 != null) { try { channel2.close(); } catch (IOException e1) { } System.out.println("關閉連線"); } } } } } } catch (IOException e) { if (channel != null) { try { channel.close(); } catch (IOException e1) { } System.out.println("關閉連線"); } } } private void accept(SelectionKey key) { ServerSocketChannel ssc = null; SocketChannel channel = null; try { ssc = (ServerSocketChannel) key.channel(); channel = ssc.accept(); channel.configureBlocking(false); channel.register(key.selector(), SelectionKey.OP_READ); } catch (IOException e) { if (channel != null) { try { channel.close(); } catch (IOException e1) { e1.printStackTrace(); } } if (ssc != null) { try { ssc.close(); } catch (IOException e1) { e1.printStackTrace(); } } } } }
客戶端:
package chatroom.chatserver; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.Scanner; public class ChatClient { public static void main(String[] args) { new ChatClient().clientRun(); } public void clientRun() { try { SocketChannel socketChannel = SocketChannel.open(); Socket socket = socketChannel.socket(); socket.connect(new InetSocketAddress("127.0.0.1", 8888)); socketChannel.configureBlocking(false); // 建立一個新執行緒進行伺服器訊息的監聽 new Thread(new receive(socketChannel)).start(); ByteBuffer buffer = ByteBuffer.allocate(1024); Scanner scanner = new Scanner(System.in); while (true) { System.out.println("請輸入:"); buffer.clear(); buffer.put(scanner.nextLine().getBytes()); // 這裡寫之前一定要flip,因為上面的put已經把buffer的position等資訊修改了, // 將快取資料寫出去的話,要從buffer的0開始 buffer.flip(); socketChannel.write(buffer); } } catch (IOException e) { e.printStackTrace(); } } class receive implements Runnable { private SocketChannel socketChannel; public receive(SocketChannel socketChannel) { this.socketChannel = socketChannel; } @Override public void run() { System.out.println("開始監聽資料"); ByteBuffer buffer = ByteBuffer.allocate(1024); while (true) { buffer.clear(); int read; try { read = socketChannel.read(buffer); if (read > 0) { buffer.flip(); System.out.println("接收到資料:" + new String(buffer.array(), 0, read)); } } catch (IOException e) { if (socketChannel != null) { try { socketChannel.close(); } catch (IOException e1) { System.out.println("連線關閉"); } } } } } } }