JAVA NIO實(shí)現(xiàn)簡(jiǎn)單聊天室功能
本文實(shí)例為大家分享了JAVA NIO實(shí)現(xiàn)簡(jiǎn)單聊天室功能的具體代碼,供大家參考,具體內(nèi)容如下
服務(wù)端
初始化一個(gè)ServerSocketChannel,綁定端口,然后使用Selector監(jiān)聽(tīng)accept事件。
當(dāng)有accept發(fā)生時(shí),表示有客戶(hù)端連接進(jìn)來(lái)了,獲取客戶(hù)端的SocketChannel,然后注冊(cè)其read事件;用來(lái)接收客戶(hù)端發(fā)送的消息。
package chatroom; 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.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; /** * 服務(wù)端 * * @author wenei * @date 2021-07-20 20:36 */ public class Server { private static final Logger log = Logger.getLogger(Server.class.getName()); private int port; private List<SocketChannel> clientChannelList = new ArrayList<>(); public Server(int port) { this.port = port; } public void start() throws IOException { // 初始化服務(wù)端channel ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(port)); serverSocketChannel.configureBlocking(false); // 新建Selector Selector selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { final int selectCount = selector.select(); if (selectCount <= 0) { continue; } final Set<SelectionKey> selectionKeys = selector.selectedKeys(); final Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { final SelectionKey key = iterator.next(); iterator.remove(); if (key.isAcceptable()) { // 當(dāng)有accept事件時(shí),將新的連接加入Selector ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel(); SocketChannel accept = serverChannel.accept(); accept.configureBlocking(false); clientChannelList.add(accept); accept.register(selector, SelectionKey.OP_READ); log.log(Level.INFO, "新連接 " + accept); } else if (key.isReadable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); log.log(Level.INFO, "可讀連接 " + socketChannel); ByteBuffer buffer = ByteBuffer.allocate(60); try { /** * 當(dāng)客戶(hù)端非正常退出時(shí),read拋出異常,屬于被動(dòng)性關(guān)閉; * 當(dāng)客戶(hù)端正常返回時(shí),返回-1,但也是readable信號(hào),所以需要處理 */ final int read = socketChannel.read(buffer); if (read == -1) { log.log(Level.INFO, "連接主動(dòng)關(guān)閉:" + socketChannel); clientChannelList.remove(socketChannel); socketChannel.close(); continue; } } catch (IOException e) { log.log(Level.INFO, "連接被動(dòng)關(guān)閉:" + socketChannel); clientChannelList.remove(socketChannel); socketChannel.close(); continue; } buffer.flip(); byte[] bytes = new byte[60]; int index = 0; while (buffer.hasRemaining()) { bytes[index++] = buffer.get(); } bytes[index] = '\0'; log.log(Level.INFO, "接受數(shù)據(jù): " + new String(bytes, StandardCharsets.UTF_8).trim()); // 廣播 clientChannelList.forEach(channel -> { if (channel != socketChannel) { buffer.flip(); try { channel.write(buffer); } catch (IOException e) { e.printStackTrace(); } } }); // buffer.clear(); } } } } public static void main(String[] args) throws IOException { new Server(10022).start(); } }
客戶(hù)端
使用主線(xiàn)程獲取鍵盤(pán)輸入,然后傳給服務(wù)端。
使用子線(xiàn)程接收服務(wù)端發(fā)送的信息并顯示。
package chatroom; 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.StandardCharsets; import java.util.Iterator; import java.util.Scanner; import java.util.Set; /** * 客戶(hù)端 * * @author wenei * @date 2021-07-21 9:14 */ public class Client { /** * 客戶(hù)端接收信息線(xiàn)程 */ static class ClientReceiveThread implements Runnable { /** * 客戶(hù)端socket */ private SocketChannel socketChannel; public ClientReceiveThread(SocketChannel socketChannel) { this.socketChannel = socketChannel; } @Override public void run() { try { Selector selector = Selector.open(); socketChannel.register(selector, SelectionKey.OP_READ); while (true) { final int selectCount = selector.select(100); if (Thread.currentThread().isInterrupted()) { System.out.println("連接關(guān)閉"); socketChannel.close(); return; } if (selectCount <= 0) { continue; } final Set<SelectionKey> selectionKeys = selector.selectedKeys(); final Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { final SelectionKey key = iterator.next(); iterator.remove(); if (key.isReadable()) { ByteBuffer recvBuffer = ByteBuffer.allocate(60); socketChannel.read(recvBuffer); recvBuffer.flip(); byte[] bytes = new byte[60]; int index = 0; while (recvBuffer.hasRemaining()) { bytes[index++] = recvBuffer.get(); } bytes[index] = '\0'; System.out.println("接受數(shù)據(jù): " + new String(bytes, StandardCharsets.UTF_8).trim()); } } } } catch (IOException e) { e.printStackTrace(); } } } private int port; public Client(int port) { this.port = port; } public void start() throws IOException { SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(port)); socketChannel.configureBlocking(false); Scanner scanner = new Scanner(System.in); ByteBuffer buffer = ByteBuffer.allocate(60); Thread thread = new Thread(new ClientReceiveThread(socketChannel)); thread.start(); while (true) { String data = scanner.nextLine(); if (data.equals("exit")) { break; } System.out.println("輸入數(shù)據(jù):" + data); buffer.put(data.getBytes(StandardCharsets.UTF_8)); buffer.flip(); socketChannel.write(buffer); buffer.clear(); } thread.interrupt(); } public static void main(String[] args) throws IOException { new Client(10022).start(); } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Java建造者模式構(gòu)建復(fù)雜對(duì)象的最佳實(shí)踐
建造者模式,是一種對(duì)象構(gòu)建模式?它可以將復(fù)雜對(duì)象的建造過(guò)程抽象出來(lái),使這個(gè)抽象過(guò)程的不同實(shí)現(xiàn)方法可以構(gòu)造出不同表現(xiàn)的對(duì)象。本文將通過(guò)示例講解建造者模式,需要的可以參考一下2023-04-04jdk8使用stream實(shí)現(xiàn)兩個(gè)list集合合并成一個(gè)(對(duì)象屬性的合并)
本文主要介紹了jdk8使用stream實(shí)現(xiàn)兩個(gè)list集合合并成一個(gè)(對(duì)象屬性的合并),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01java使用多線(xiàn)程找出最大隨機(jī)數(shù)
這篇文章主要為大家詳細(xì)介紹了java使用多線(xiàn)程找出最大隨機(jī)數(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07Jrebel License Server 激活 IDEA-Jrebel-在線(xiàn)-
這篇文章主要介紹了Jrebel License Server 激活 IDEA-Jrebel-在線(xiàn)-離線(xiàn)-均適用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12nas實(shí)現(xiàn)java開(kāi)發(fā)的環(huán)境詳解
這篇文章主要為大家介紹了nas實(shí)現(xiàn)java開(kāi)發(fā)的環(huán)境詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11