NIO网络编程实战之简单多人聊天室
生活随笔
收集整理的這篇文章主要介紹了
NIO网络编程实战之简单多人聊天室
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
NIO網絡編程實戰
利用NIO編程知識,實現多人聊天室。
1. NIO編程實現步驟
第一步:創建Selector
第二步:創建ServerSocketChannel,并綁定監聽端口
第三步:將Channel設置為非阻塞模式
第四步:將Channel注冊到Selector上,監聽連接事件
第五步:循環調用Selector的select方法,檢測就緒情況
第六步:調用selectedKeys方法獲取就緒channel集合
第七步:判斷就緒事件種類,調用業務處理方法
第八步:根據業務需要決定是否再次注冊監聽事件,重復執行第三步操作
2. NIO網絡編程實戰
利用NIO編程知識,實現多人聊天室。
2.1 程序結構目錄
2.2 服務器端程序
package com.xuxin.niochatroom;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.charset.Charset; import java.util.Iterator; import java.util.Set;/*** NIO服務器端*/ public class NioServer {/*** 啟動*/public void start() throws IOException {// 1. 創建SelectorSelector selector = Selector.open();// 2. 通過ServerSocketChannel創建channel通道ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 3. 為channel通道綁定監聽端口serverSocketChannel.bind(new InetSocketAddress(8000));// 4. 設置channel為非阻塞模式serverSocketChannel.configureBlocking(false);// 5. 將channel注冊到selector上,監聽連接事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服務器啟動成功!");// 6. 循環等待新接入的連接for (;;) {// 獲取可用channel的數量int readyChannels = selector.select();if (readyChannels == 0) {continue;}// 獲取可用channel的集合Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator iterator = selectionKeys.iterator();while(iterator.hasNext()) {// selectionKey實例SelectionKey selectionKey = (SelectionKey) iterator.next();// 移除Set中的當前selectionKeyiterator.remove();// 7. 根據就緒狀態,調用對應方法處理業務邏輯// 如果是 接入事件if (selectionKey.isAcceptable()) {acceptHandler(serverSocketChannel, selector);}// 如果是 可讀事件if (selectionKey.isReadable()) {readHandler(selectionKey, selector);}}}}/*** 接入事件處理器*/private void acceptHandler(ServerSocketChannel serverSocketChannel,Selector selector) throws IOException {// 如果要是接入事件,創建socketChannelSocketChannel socketChannel = serverSocketChannel.accept();// 將socketChannel設置為非阻塞工作模式socketChannel.configureBlocking(false);// 將channel注冊到selector上,監聽 可讀事件socketChannel.register(selector, SelectionKey.OP_READ);// 回復客戶端提示信息socketChannel.write(Charset.forName("UTF-8").encode("你與聊天室里其他人都不是朋友關系,請注意隱私安全"));}/*** 可讀事件處理器*/private void readHandler(SelectionKey selectionKey, Selector selector)throws IOException {// 要從selectionKey中獲取到已經就緒的channelSocketChannel socketChannel = (SocketChannel) selectionKey.channel();// 創建bufferByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 循環讀取客戶端請求信息String request = "";while (socketChannel.read(byteBuffer) > 0) {// 切換buffer為讀模式byteBuffer.flip();// 讀取buffer中的內容request += Charset.forName("UTF-8").decode(byteBuffer);}// 將channel再次注冊到selector上,監聽他的可讀事件socketChannel.register(selector, selectionKey.OP_READ);// 將客戶端發送的請求信息 廣播給其他客戶端if (request.length() > 0) {// 廣播給其他客戶端broadCast(selector, socketChannel, request);}}/*** 廣播給其他客戶端*/private void broadCast(Selector selector, SocketChannel sourceChannel, String request) {// 獲取到所有已接入的客戶端channelSet<SelectionKey> selectionKeySet = selector.keys();// 循環向所有channel廣播信息selectionKeySet.forEach(selectionKey -> {Channel targetChannel = selectionKey.channel();// 剔除發消息的客戶端if ((targetChannel instanceof SocketChannel) && (targetChannel != sourceChannel)) {try {// 將信息群發給target的客戶端((SocketChannel) targetChannel).write(Charset.forName("UTF-8").encode(request));} catch (IOException e) {e.printStackTrace();}}});}/*** 主程序* @param args*/public static void main(String[] args) throws IOException {NioServer nioServer = new NioServer();nioServer.start();} }2.3 客戶端程序
package com.xuxin.niochatroom;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.util.Scanner;/*** NIO客戶端*/ public class NioClient {/*** 啟動*/public void start(String nickName) throws IOException {// 連接服務器SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8000));// 接收服務器端響應// 創建線程,專門負責接收服務器端的響應數據// selector socketChannel 注冊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.hasNextLine()) {String request = scanner.nextLine();if ((request != null) && (request.length() > 0)) {socketChannel.write(Charset.forName("UTF-8").encode(nickName + " : " + request));}}} }2.4 客戶端處理服務器端響應的線程
package com.xuxin.niochatroom;import java.io.IOException; 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.Set;public class NioClientHandler implements Runnable {private Selector selector;public NioClientHandler(Selector selector) {this.selector = selector;}@Overridepublic void run() {try {// 1. 循環等待服務器端響應for (; ; ) {// 獲取可用channel的數量int readyChannels = selector.select();if (readyChannels == 0) {continue;}// 獲取可用channel的集合Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator iterator = selectionKeys.iterator();while (iterator.hasNext()) {// selectionKey實例SelectionKey selectionKey = (SelectionKey) iterator.next();// 移除Set中的當前selectionKeyiterator.remove();// 7. 根據就緒狀態,調用對應方法處理業務邏輯// 如果是 可讀事件if (selectionKey.isReadable()) {readHandler(selectionKey, selector);}}}} catch (IOException e) {e.printStackTrace();}}/*** 可讀事件處理器*/private void readHandler(SelectionKey selectionKey, Selector selector)throws IOException {// 要從selectionKey中獲取到已經就緒的channelSocketChannel socketChannel = (SocketChannel) selectionKey.channel();// 創建bufferByteBuffer byteBuffer = ByteBuffer.allocate(1024);// 循環讀取服務器端的響應數據String reponse = "";while (socketChannel.read(byteBuffer) > 0) {// 切換buffer為讀模式byteBuffer.flip();// 讀取buffer中的內容reponse += Charset.forName("UTF-8").decode(byteBuffer);}// 將channel再次注冊到selector上,監聽他的可讀事件socketChannel.register(selector, selectionKey.OP_READ);// 將服務器端的響應數據打印到本地if (reponse.length() > 0) {System.out.println(":: " + reponse);}} }2.5 測試
2.5.1 創建用于測試的客戶端類
ClientA:
package com.xuxin.niochatroom;import java.io.IOException;public class ClientA {public static void main(String[] args) throws IOException {new NioClient().start("ClientA");} }ClientB:
package com.xuxin.niochatroom;import java.io.IOException;public class ClientB {public static void main(String[] args) throws IOException {new NioClient().start("ClientB");} }ClientC:
package com.xuxin.niochatroom;import java.io.IOException;public class ClientC {public static void main(String[] args) throws IOException {new NioClient().start("ClientC");} }2.5.2 結果
啟動服務器端
啟動ClientA、ClientB、ClientC,并先由ClientA開始輸入聊天信息,再由ClientB開始輸入聊天信息,最后由ClientC開始輸入聊天信息
2.6 NIO網絡編程缺陷
麻煩
NIO類庫和API繁雜。需要了解ServerSocketChannel、SocketChannel、ByteBuffer等核心類庫的使用
問題
可靠性能力補齊,工作量和難度都非常大。
客戶端的斷連、重連、網絡閃斷、失敗緩存、網絡阻塞和異常碼流等問題。
有坑
Selector空輪詢,導致CPU100%。
總結
以上是生活随笔為你收集整理的NIO网络编程实战之简单多人聊天室的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NIO中的SelectionKey
- 下一篇: VSCode + Latex 配置