Java NIO示例:多人网络聊天室
生活随笔
收集整理的這篇文章主要介紹了
Java NIO示例:多人网络聊天室
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一個多客戶端聊天室,支持多客戶端聊天,有如下功能:
- 功能1: 客戶端通過Java NIO連接到服務端,支持多客戶端的連接
- 功能2:客戶端初次連接時,服務端提示輸入昵稱,如果昵稱已經有人使用,提示重新輸入,如果昵稱唯一,則登錄成功,之后發送消息都需要按照規定格式帶著昵稱發送消息
- 功能3:客戶端登錄后,發送已經設置好的歡迎信息和在線人數給客戶端,并且通知其他客戶端該客戶端上線
- 功能4:服務器收到已登錄客戶端輸入內容,轉發至其他登錄客戶端。
- 功能5 TODO:客戶端下線檢測 ?方案是:客戶端在線的時候發送心跳,服務端用TimeCacheMap自動刪除過期對象,同時通知線上用戶刪掉的用戶下線。
下面演示下效果,代碼見附錄!
?
啟動服務器,監聽某個端口
服務器console
Server is listening now...?
啟動一個客戶端,連接服務器
服務器console
Server is listening now... Server is listening from client :/127.0.0.1:50206客戶端console
Please input your name.?
客戶端輸入一個昵稱
服務端console
Server is listening now... Server is listening from client :/127.0.0.1:50206 Server is listening from client /127.0.0.1:50206 data rev is: byr1#@#客戶端console
Please input your name. byr1 welcome byr1 to chat room! Online numbers:1?
再啟動另外一個客戶端,并輸入昵稱
服務端console
Server is listening now... Server is listening from client :/127.0.0.1:50206 Server is listening from client /127.0.0.1:50206 data rev is: byr1#@# Server is listening from client :/127.0.0.1:50261 Server is listening from client /127.0.0.1:50261 data rev is: byr2#@#客戶端byr1 console
Please input your name. byr1 welcome byr1 to chat room! Online numbers:1 welcome byr2 to chat room! Online numbers:2客戶端byr2 console
Please input your name. byr2 welcome byr2 to chat room! Online numbers:2?
客戶端byr1發送一個消息,客戶byr2回一條消息
服務端console
Server is listening now... Server is listening from client :/127.0.0.1:50206 Server is listening from client /127.0.0.1:50206 data rev is: byr1#@# Server is listening from client :/127.0.0.1:50261 Server is listening from client /127.0.0.1:50261 data rev is: byr2#@# Server is listening from client /127.0.0.1:50206 data rev is: byr1#@#hello byr2, a nice day, isn't it? Server is listening from client /127.0.0.1:50261 data rev is: byr2#@#fine客戶端byr1 console
Please input your name. byr1 welcome byr1 to chat room! Online numbers:1 welcome byr2 to chat room! Online numbers:2 hello byr2, a nice day, isn't it? byr2 say fine客戶端byr2 console
Please input your name. byr2 welcome byr2 to chat room! Online numbers:2 byr1 say hello byr2, a nice day, isn't it? fine?
附錄:server和client代碼
server代碼
package com.huahuiyang.channel;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.Channel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /*** 網絡多客戶端聊天室* 功能1: 客戶端通過Java NIO連接到服務端,支持多客戶端的連接* 功能2:客戶端初次連接時,服務端提示輸入昵稱,如果昵稱已經有人使用,提示重新輸入,如果昵稱唯一,則登錄成功,之后發送消息都需要按照規定格式帶著昵稱發送消息* 功能3:客戶端登錄后,發送已經設置好的歡迎信息和在線人數給客戶端,并且通知其他客戶端該客戶端上線* 功能4:服務器收到已登錄客戶端輸入內容,轉發至其他登錄客戶端。* * TODO 客戶端下線檢測*/ public class ChatRoomServer {private Selector selector = null;static final int port = 9999;private Charset charset = Charset.forName("UTF-8");//用來記錄在線人數,以及昵稱private static HashSet<String> users = new HashSet<String>();private static String USER_EXIST = "system message: user exist, please change a name";//相當于自定義協議格式,與客戶端協商好private static String USER_CONTENT_SPILIT = "#@#";private static boolean flag = false;public void init() throws IOException{selector = Selector.open();ServerSocketChannel server = ServerSocketChannel.open();server.bind(new InetSocketAddress(port));//非阻塞的方式server.configureBlocking(false);//注冊到選擇器上,設置為監聽狀態 server.register(selector, SelectionKey.OP_ACCEPT);System.out.println("Server is listening now...");while(true) {int readyChannels = selector.select();if(readyChannels == 0) continue; Set selectedKeys = selector.selectedKeys(); //可以通過這個方法,知道可用通道的集合Iterator keyIterator = selectedKeys.iterator();while(keyIterator.hasNext()) {SelectionKey sk = (SelectionKey) keyIterator.next();keyIterator.remove();dealWithSelectionKey(server,sk);}}}public void dealWithSelectionKey(ServerSocketChannel server,SelectionKey sk) throws IOException {if(sk.isAcceptable()){SocketChannel sc = server.accept();//非阻塞模式sc.configureBlocking(false);//注冊選擇器,并設置為讀取模式,收到一個連接請求,然后起一個SocketChannel,并注冊到selector上,之后這個連接的數據,就由這個SocketChannel處理 sc.register(selector, SelectionKey.OP_READ);//將此對應的channel設置為準備接受其他客戶端請求 sk.interestOps(SelectionKey.OP_ACCEPT);System.out.println("Server is listening from client :" + sc.getRemoteAddress());sc.write(charset.encode("Please input your name."));}//處理來自客戶端的數據讀取請求if(sk.isReadable()){//返回該SelectionKey對應的 Channel,其中有數據需要讀取SocketChannel sc = (SocketChannel)sk.channel(); ByteBuffer buff = ByteBuffer.allocate(1024);StringBuilder content = new StringBuilder();try{while(sc.read(buff) > 0){buff.flip();content.append(charset.decode(buff));}System.out.println("Server is listening from client " + sc.getRemoteAddress() + " data rev is: " + content);//將此對應的channel設置為準備下一次接受數據 sk.interestOps(SelectionKey.OP_READ);}catch (IOException io){sk.cancel();if(sk.channel() != null){sk.channel().close();}}if(content.length() > 0){String[] arrayContent = content.toString().split(USER_CONTENT_SPILIT);//注冊用戶if(arrayContent != null && arrayContent.length ==1) {String name = arrayContent[0];if(users.contains(name)) {sc.write(charset.encode(USER_EXIST));} else {users.add(name);int num = OnlineNum(selector);String message = "welcome "+name+" to chat room! Online numbers:"+num;BroadCast(selector, null, message);}} //注冊完了,發送消息else if(arrayContent != null && arrayContent.length >1){String name = arrayContent[0];String message = content.substring(name.length()+USER_CONTENT_SPILIT.length());message = name + " say " + message;if(users.contains(name)) {//不回發給發送此內容的客戶端 BroadCast(selector, sc, message);}}}}}//TODO 要是能檢測下線,就不用這么統計了public static int OnlineNum(Selector selector) {int res = 0;for(SelectionKey key : selector.keys()){Channel targetchannel = key.channel();if(targetchannel instanceof SocketChannel){res++;}}return res;}public void BroadCast(Selector selector, SocketChannel except, String content) throws IOException {//廣播數據到所有的SocketChannel中for(SelectionKey key : selector.keys()){Channel targetchannel = key.channel();//如果except不為空,不回發給發送此內容的客戶端if(targetchannel instanceof SocketChannel && targetchannel!=except){SocketChannel dest = (SocketChannel)targetchannel;dest.write(charset.encode(content));}}}public static void main(String[] args) throws IOException {new ChatRoomServer().init();} }?
client代碼
package com.huahuiyang.channel;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.Charset; import java.util.Iterator; import java.util.Scanner; import java.util.Set;public class ChatRoomClient {private Selector selector = null;static final int port = 9999;private Charset charset = Charset.forName("UTF-8");private SocketChannel sc = null;private String name = "";private static String USER_EXIST = "system message: user exist, please change a name";private static String USER_CONTENT_SPILIT = "#@#";public void init() throws IOException{selector = Selector.open();//連接遠程主機的IP和端口sc = SocketChannel.open(new InetSocketAddress("127.0.0.1",port));sc.configureBlocking(false);sc.register(selector, SelectionKey.OP_READ);//開辟一個新線程來讀取從服務器端的數據new Thread(new ClientThread()).start();//在主線程中 從鍵盤讀取數據輸入到服務器端Scanner scan = new Scanner(System.in);while(scan.hasNextLine()){String line = scan.nextLine();if("".equals(line)) continue; //不允許發空消息if("".equals(name)) {name = line;line = name+USER_CONTENT_SPILIT;} else {line = name+USER_CONTENT_SPILIT+line;}sc.write(charset.encode(line));//sc既能寫也能讀,這邊是寫 }}private class ClientThread implements Runnable{public void run(){try{while(true) {int readyChannels = selector.select();if(readyChannels == 0) continue; Set selectedKeys = selector.selectedKeys(); //可以通過這個方法,知道可用通道的集合Iterator keyIterator = selectedKeys.iterator();while(keyIterator.hasNext()) {SelectionKey sk = (SelectionKey) keyIterator.next();keyIterator.remove();dealWithSelectionKey(sk);}}}catch (IOException io){}}private void dealWithSelectionKey(SelectionKey sk) throws IOException {if(sk.isReadable()){//使用 NIO 讀取 Channel中的數據,這個和全局變量sc是一樣的,因為只注冊了一個SocketChannel//sc既能寫也能讀,這邊是讀SocketChannel sc = (SocketChannel)sk.channel();ByteBuffer buff = ByteBuffer.allocate(1024);String content = "";while(sc.read(buff) > 0){buff.flip();content += charset.decode(buff);}//若系統發送通知名字已經存在,則需要換個昵稱if(USER_EXIST.equals(content)) {name = "";}System.out.println(content);sk.interestOps(SelectionKey.OP_READ);}}}public static void main(String[] args) throws IOException{new ChatRoomClient().init();} }?
?
總結
以上是生活随笔為你收集整理的Java NIO示例:多人网络聊天室的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html 最初级学习笔记一
- 下一篇: 第十一天:find