Java實現局域網聊天室功能(私聊、群聊)
本文實例為大傢分享瞭Java實現局域網聊天室功能的具體代碼,供大傢參考,具體內容如下
Server 服務端
import java.io.IOException; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; /** * 服務端 */ public class Server { private static final int SERVER_PORT=8080; //使用CrazyitMap對象來保存每個客戶名字和對應輸出流之間的對應關系 public static CrazyitMap<String,PrintStream> clients=new CrazyitMap<>(); public void init(){ try( //建立監聽的ServerSocket ServerSocket ss=new ServerSocket(SERVER_PORT)) { //采用死循環來不斷地接收來自客戶端的請求 while(true){ Socket socket=ss.accept(); new ServerThread(socket).start(); } } //如果拋出異常 catch (IOException ex){ System.out.println("服務器啟動失敗,是否端口"+SERVER_PORT+"已被占用"); } } public static void main(String[] args){ Server server=new Server(); server.init(); } }
ServerThread 服務端線程
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; /** * 服務端線程 */ public class ServerThread extends Thread { private Socket socket; BufferedReader br = null; PrintStream ps = null; //定義一個構造器,用於接收一個Socket來創建ServerThread線程 public ServerThread(Socket socket) { this.socket = socket; } public void run() { try { //獲取該Socket對應的輸入流 br = new BufferedReader(new InputStreamReader(socket.getInputStream())); //獲取該Socket對應的輸出流 ps = new PrintStream(socket.getOutputStream()); String line = null; while ((line = br.readLine()) != null) { //如果讀到的行以CrazyitProtocol.USER_ROUND開始,並以其結束 //則可以確定讀到的是用戶登錄的用戶名 if (line.startsWith(CrazyitProtocol.USER_ROUND) && line.endsWith(CrazyitProtocol.USER_ROUND)) { //得到真實消息 String userName = getRealMsg(line); //如果用戶名重復 if (Server.clients.map.containsKey(userName)) { System.out.println("重復"); ps.println(CrazyitProtocol.NAME_REP); } else { System.out.println(userName+"上線"); ps.println(CrazyitProtocol.LOGIN_SUCCESS); Server.clients.put(userName, ps); } } //如果讀到的行以CrazyitProtocol.PRIVATE_ROUND開始, //則可以確定是私聊信息,私聊信息隻向特定的輸入流發送 else if (line.startsWith(CrazyitProtocol.PRIVATE_ROUND) && line.endsWith(CrazyitProtocol.PRIVATE_ROUND)) { //得到真實的消息 String userAndMsg = getRealMsg(line); //以SPLIT_SIGN分割字符串,前半是私聊用戶,後半是聊天信息 String user = userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[0]; String msg = userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[1]; //獲取私聊用戶對應的輸出流,並發送私聊信息 Server.clients.map.get(user).println(Server.clients.getKeyByValue(ps) + "悄悄對你說:" + msg); } //公聊要向每一個Socket發送 else { //得到真實消息 String msg = getRealMsg(line); //遍歷clients中的每個輸出流 for (PrintStream clientPs : Server.clients.valueSet()) { clientPs.println(Server.clients.getKeyByValue(ps) + "說:" + msg); } } } } //捕獲到異常後,表明Socket對應的客戶端已經出現瞭問題 //所以程序將其對應的輸出流從Map中刪除 catch (IOException e) { Server.clients.removeByValue(ps); System.out.println(Server.clients.map.size()); //關閉網絡,IO資源 try { if (br != null) { br.close(); } if (ps != null) { ps.close(); } if (socket != null) { socket.close(); } } catch (IOException ex) { ex.printStackTrace(); } } } //將讀到的內容去掉前後協議字符,恢復成真實數據 private String getRealMsg(String line) { return line.substring(CrazyitProtocol.PROTOCOL_LEN,line.length()-CrazyitProtocol.PROTOCOL_LEN); } }
Client 客戶端
import javax.swing.*; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; import java.net.UnknownHostException; /** * 客戶端 */ public class Client { private static final int SERVER_PORT=8080; private Socket socket; private PrintStream ps; private BufferedReader brServer; private BufferedReader keyIn; public void init(){ try { //初始化代表鍵盤的輸入流 keyIn=new BufferedReader(new InputStreamReader(System.in)); //連接到服務器端 socket=new Socket("127.0.0.1",SERVER_PORT); //獲取該Socket對應的輸入流和輸出流 ps=new PrintStream(socket.getOutputStream()); brServer=new BufferedReader(new InputStreamReader(socket.getInputStream())); String tip=" "; //采用不斷循環地彈出對話框要求輸入用戶名 while(true){ String userName= JOptionPane.showInputDialog(tip+"輸入用戶名"); //用戶輸入的用戶名前後增加協議字符串後發送 ps.println(CrazyitProtocol.USER_ROUND+userName+CrazyitProtocol.USER_ROUND); //讀取服務器端的響應 String result=brServer.readLine(); //如果用戶名重復,則開始下一次循環 if (result.equals(CrazyitProtocol.NAME_REP)){ tip="用戶名重復,請重試"; continue; } //服務器端登錄成功 if (result.equals(CrazyitProtocol.LOGIN_SUCCESS)){ break; } } } //捕獲到異常,關閉網絡資源,並退出該程序 catch (UnknownHostException ex){ System.out.println("找不到遠程服務器,請確定服務器已經啟動"); closeRs(); System.exit(1); } catch(IOException ex){ System.out.println("網絡異常,請重新登錄"); closeRs(); System.exit(1); } //以該Socket對應的輸入流啟動ClientThread線程 new ClientThread(brServer).start(); } //定義一個讀取鍵盤輸出,並以網絡發送的方法 private void readAndSend(){ try { //不斷讀取鍵盤輸入 String line=null; while ((line=keyIn.readLine())!=null){ //如果發送的信號中有冒號,並以//開頭,則認為想發送私聊信息 if (line.indexOf(":")>0&&line.startsWith("//")){ line=line.substring(2); ps.println(CrazyitProtocol.PRIVATE_ROUND+line.split(":")[0]+CrazyitProtocol.SPLIT_SIGN+line.split(":")[1]+CrazyitProtocol.PRIVATE_ROUND); } else{ ps.println(CrazyitProtocol.MSG_ROUND+line+CrazyitProtocol.MSG_ROUND); } } } catch (IOException ex){ System.out.println("網絡通信異常!請重新登錄"); closeRs(); System.exit(1); } } //關閉Socket,輸入流,輸出流的方法 private void closeRs(){ try { if (keyIn!=null){ ps.close(); } if (brServer!=null){ ps.close(); } if (ps!=null){ ps.close(); } if (socket!=null){ keyIn.close(); } } catch (IOException ex){ ex.printStackTrace(); } } public static void main(String[] args){ Client client=new Client(); client.init(); client.readAndSend(); } }
ClientThread 客戶端線程
import java.io.BufferedReader; import java.io.IOException; /** * 客戶端線程 */ public class ClientThread extends Thread { //該客戶端線程負責處理輸入流 BufferedReader br=null; //使用一個網絡輸入流來創建客戶端線程 public ClientThread(BufferedReader br){ this.br=br; } public void run(){ try { String line=null; //不斷地從輸入流中讀取數據,並將這些數據打印輸出 while((line=br.readLine())!=null){ System.out.println(line); } } catch (IOException ex){ ex.printStackTrace(); } finally { try { if (br!=null){ br.close(); } } catch (IOException ex){ ex.printStackTrace(); } } } }
CrazyitMap 容器
import java.util.*; /** * map容器 * * @param <k> * @param <v> */ public class CrazyitMap<k,v> { //創建一個線程安全的HashMap public Map<k,v> map= Collections.synchronizedMap(new HashMap<k,v>()); //根據value來刪除指定項 public synchronized void removeByValue(Object value){ for (Object key:map.keySet()){ if (map.get(key)==value){ map.remove(key); break; } } } //獲取所有value組成的Set集合 public synchronized Set<v> valueSet(){ Set<v> result=new HashSet<v>(); //將map中的所有value添加到result集合中 map.forEach((key,value)->result.add(value)); return result; } //根據value查找key public synchronized k getKeyByValue(v value){ //遍歷所有key組成的集合 for (k key:map.keySet()){ //如果指定key對應的value與被搜索的value相同,則返回對應的key if(map.get(key)==value||map.get(key).equals(value)){ return key; } } return null; } //實現put()方法,該方法不允許value重復 public synchronized v put(k key,v value){ //遍歷所有value組成的集合 for (v val:valueSet()){ //如果某個value與試圖放入集合的value相同 //則拋出一個RuntimeException異常 if (val.equals(value)&&val.hashCode()==value.hashCode()){ throw new RuntimeException("MyMap實例不允許有重復的value"); } } return map.put(key,value); } }
CrazyitProtocol 接口
/** * 自定義接口 */ public interface CrazyitProtocol { //定義協議字符串的長度 int PROTOCOL_LEN=2; //下面是一些協議字符串,服務器端和客戶端交換的信息都應該在前後添加這種特殊字符串 String MSG_ROUND="ηθ"; String USER_ROUND="∏∑"; String LOGIN_SUCCESS="1"; String NAME_REP="-1"; String PRIVATE_ROUND="★【"; String SPLIT_SIGN="卐"; }
測試
1、啟動服務端
2、啟動客戶端 (上線)
3 、群發
4、私聊 (//名字:)
以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。