Java?NIO實現多人聊天室
更新時間:2021年11月24日 09:55:57 作者:RivenDong
這篇文章主要為大家詳細介紹了Java?NIO實現多人聊天室,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
本文實例為大家分享了Java NIO實現多人聊天室的具體代碼,供大家參考,具體內容如下
1. 服務器端代碼
ChatServer類:
package nio.test.server; import java.io.Closeable; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.charset.Charset; import java.util.Set; public class ChatServer { private static final int DEFAULT_PORT = 8888; private static final String QUIT = "quit"; private static final int BUFFER = 1024; private ServerSocketChannel serverSocketChannel; //服務器端用于處理IO的通道 private Selector selector; private ByteBuffer byteBufferReader = ByteBuffer.allocate(BUFFER); //用來讀取消息 private ByteBuffer byteBufferWriter = ByteBuffer.allocate(BUFFER); //用來轉發(fā)消息時寫入其他通道的緩沖區(qū) private Charset charset = Charset.forName("UTF-8"); //標準化編碼解碼 private int port; public ChatServer(){ this(DEFAULT_PORT); } public ChatServer(int port){ this.port = port; } private void start(){ try { serverSocketChannel = ServerSocketChannel.open(); //創(chuàng)建服務器套接字通道 serverSocketChannel.configureBlocking(false); //設置為非阻塞式調用 serverSocketChannel.socket().bind(new InetSocketAddress(port)); selector = Selector.open(); //打開選擇器 serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("啟動服務器,監(jiān)聽端口:" + port + "..."); while (true) { selector.select(); //selectionKeys包含了select()接收到的所有事件 Set<SelectionKey> selectionKeys = selector.selectedKeys(); for(SelectionKey key : selectionKeys){ //處理被觸發(fā)的事件 handles(key); } selectionKeys.clear(); //把集合清空 } } catch (IOException e) { e.printStackTrace(); }finally { close(selector);//啟到既關閉selector又關閉通道的作用 } } /** * 處理被觸發(fā)的事件 * @param key 每當通道被選擇器注冊時,都會創(chuàng)建一個選擇鍵 * @throws IOException */ private void handles(SelectionKey key) throws IOException { // 觸發(fā) ACCEPT事件 --- 和客戶端建立了連接 if(key.isAcceptable()){ ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); System.out.println(getClientName(client) + "已連接"); } // 觸發(fā) READ事件 --- 客戶端發(fā)送了消息給服務器端 else if(key.isReadable()){ SocketChannel client = (SocketChannel) key.channel(); String fwdMsg = receive(client); //讀取客戶端消息 if(fwdMsg.isEmpty()){ //客戶端異常 key.cancel(); //不再監(jiān)視這個通道上的read事件 selector.wakeup(); }else { forwardMessage(client, fwdMsg); //轉發(fā)客戶端消息 // 檢查用戶是否退出 if(readyToQuit(fwdMsg)){ key.cancel();//解除監(jiān)聽 selector.wakeup(); System.out.println(getClientName(client) + "已斷開"); } } } } /** * 用于轉發(fā)消息 * @param client * @param fwdMsg * @throws IOException */ private void forwardMessage(SocketChannel client, String fwdMsg) throws IOException { for(SelectionKey key : selector.keys()){ Channel connectedClient = key.channel(); if(connectedClient instanceof ServerSocketChannel) continue; if(key.isValid() && !client.equals(connectedClient)) { byteBufferWriter.clear(); byteBufferWriter.put(charset.encode((getClientName(client)) + ":" + fwdMsg)); byteBufferWriter.flip(); //寫轉讀 while(byteBufferWriter.hasRemaining()){ ((SocketChannel)connectedClient).write(byteBufferWriter); } } } } private String receive(SocketChannel client) throws IOException { byteBufferReader.clear(); while(client.read(byteBufferReader) > 0); byteBufferReader.flip(); return String.valueOf(charset.decode(byteBufferReader)); } private String getClientName(SocketChannel client){ return "客戶端[" + client.socket().getPort() + "]"; } private boolean readyToQuit(String msg){ return QUIT.equals(msg); } private void close(Closeable closeable){ if(closeable != null){ try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) { ChatServer chatServer = new ChatServer(6666); chatServer.start(); } }
2. 客戶端代碼
ChatClient類:
package nio.test.client; import java.io.Closeable; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.util.Set; public class ChatClient { private static final String DEFAULT_SERVER_HOST = "127.0.0.1"; private static final int DEFAULT_SERVER_PORT = 6666; private static final String QUIT = "quit"; private static final int BUFFER = 1024; private String host; private int port; private SocketChannel client; private ByteBuffer byteBufferReader = ByteBuffer.allocate(BUFFER); private ByteBuffer byteBufferWriter = ByteBuffer.allocate(BUFFER); private Selector selector; private Charset charset = Charset.forName("UTF-8"); public ChatClient(){ this(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT); } public ChatClient(String host, int port){ this.host = host; this.port = port; } public boolean readyToQuit(String msg){ return QUIT.equals(msg); } private void close(Closeable closeable){ if(closeable != null){ try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } } private void start(){ try { client = SocketChannel.open(); client.configureBlocking(false); selector = Selector.open(); client.register(selector, SelectionKey.OP_CONNECT); client.connect(new InetSocketAddress(host, port)); while(true){ selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); for(SelectionKey key : selectionKeys){ handles(key); } selectionKeys.clear(); } } catch (IOException e) { e.printStackTrace(); } catch (ClosedSelectorException e){ //用戶正常退出 }finally { close(selector); } } private void handles(SelectionKey key) throws IOException { // CONNECT事件 連接就緒事件 if(key.isConnectable()){ SocketChannel client = (SocketChannel)key.channel(); if(client.isConnectionPending()){//連接處于就緒狀態(tài) client.finishConnect(); // 處理用戶的輸入信息 new Thread(new UserInputHandler(this)).start(); } client.register(selector, SelectionKey.OP_READ); } // READ事件 服務器轉發(fā)消息 else if(key.isReadable()){ SocketChannel client = (SocketChannel)key.channel(); String msg = receive(client); if(msg.isEmpty()){ // 服務器出現異常 close(selector); }else{ System.out.println(msg); } } } public void send(String msg) throws IOException { if(msg.isEmpty()){ return ; }else{ byteBufferWriter.clear(); byteBufferWriter.put(charset.encode(msg)); byteBufferWriter.flip(); while(byteBufferWriter.hasRemaining()){ client.write(byteBufferWriter); } //檢查用戶是否準備退出 if(readyToQuit(msg)){ close(selector); } } } private String receive(SocketChannel client) throws IOException { byteBufferReader.clear(); while(client.read(byteBufferReader) > 0); byteBufferReader.flip(); return String.valueOf(charset.decode(byteBufferReader)); } public static void main(String[] args) { ChatClient chatClient = new ChatClient(); chatClient.start(); } }
UserInputHandler類:
package nio.test.client; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class UserInputHandler implements Runnable{ private ChatClient chatclient; public UserInputHandler(ChatClient chatClient){ this.chatclient = chatClient; } /**r * */ @Override public void run() { try { //等待用戶輸入的消息 BufferedReader consoleReader = new BufferedReader( new InputStreamReader(System.in) ); while(true){ String input = consoleReader.readLine(); //向服務器發(fā)送消息 chatclient.send(input); //檢查用戶是否準備退出 if(chatclient.readyToQuit(input)){ break; } } } catch (IOException e) { e.printStackTrace(); } } }
3. 執(zhí)行效果截圖
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
詳解Java多線程編程中LockSupport類的線程阻塞用法
LockSupport類提供了park()和unpark()兩個方法來實現線程的阻塞和喚醒,下面我們就來詳解Java多線程編程中LockSupport類的線程阻塞用法:2016-07-07Spring Boot 會員管理系統(tǒng)之處理文件上傳功能
Spring Boot會員管理系統(tǒng)的中,需要涉及到Spring框架,SpringMVC框架,Hibernate框架,thymeleaf模板引擎。這篇文章主要介紹了Spring Boot會員管理系統(tǒng)之處理文件上傳功能,需要的朋友可以參考下2018-03-03