Java實(shí)現(xiàn)局域網(wǎng)聊天室功能(私聊、群聊)
本文實(shí)例為大家分享了Java實(shí)現(xiàn)局域網(wǎng)聊天室功能的具體代碼,供大家參考,具體內(nèi)容如下
Server 服務(wù)端
import java.io.IOException; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket; ? /** ?* 服務(wù)端 ?*/ public class Server { ? ? ? private static final int SERVER_PORT=8080; ? ? //使用CrazyitMap對象來保存每個(gè)客戶名字和對應(yīng)輸出流之間的對應(yīng)關(guān)系 ? ? public static CrazyitMap<String,PrintStream> clients=new CrazyitMap<>(); ? ? ? public void init(){ ? ? ? ? try( //建立監(jiān)聽的ServerSocket ? ? ? ? ? ? ?ServerSocket ss=new ServerSocket(SERVER_PORT)) ? ? ? ? { ? ? ? ? ? ? //采用死循環(huán)來不斷地接收來自客戶端的請求 ? ? ? ? ? ? while(true){ ? ? ? ? ? ? ? ? Socket socket=ss.accept(); ? ? ? ? ? ? ? ? new ServerThread(socket).start(); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? //如果拋出異常 ? ? ? ? catch (IOException ex){ ? ? ? ? ? ? System.out.println("服務(wù)器啟動(dòng)失敗,是否端口"+SERVER_PORT+"已被占用"); ? ? ? ? } ? ? } ? ? public static void main(String[] args){ ? ? ? ? Server server=new Server(); ? ? ? ? server.init(); ? ? } }
ServerThread 服務(wù)端線程
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; ? /** ?* 服務(wù)端線程 ?*/ public class ServerThread extends Thread { ? ? private Socket socket; ? ? BufferedReader br = null; ? ? PrintStream ps = null; ? ? ? //定義一個(gè)構(gòu)造器,用于接收一個(gè)Socket來創(chuàng)建ServerThread線程 ? ? public ServerThread(Socket socket) { ? ? ? ? this.socket = socket; ? ? } ? ? ? public void run() { ? ? ? ? try { ? ? ? ? ? ? //獲取該Socket對應(yīng)的輸入流 ? ? ? ? ? ? br = new BufferedReader(new InputStreamReader(socket.getInputStream())); ? ? ? ? ? ? //獲取該Socket對應(yīng)的輸出流 ? ? ? ? ? ? ps = new PrintStream(socket.getOutputStream()); ? ? ? ? ? ? String line = null; ? ? ? ? ? ? while ((line = br.readLine()) != null) { ? ? ? ? ? ? ? ? //如果讀到的行以CrazyitProtocol.USER_ROUND開始,并以其結(jié)束 ? ? ? ? ? ? ? ? //則可以確定讀到的是用戶登錄的用戶名 ? ? ? ? ? ? ? ? if (line.startsWith(CrazyitProtocol.USER_ROUND) && line.endsWith(CrazyitProtocol.USER_ROUND)) { ? ? ? ? ? ? ? ? ? ? //得到真實(shí)消息 ? ? ? ? ? ? ? ? ? ? String userName = getRealMsg(line); ? ? ? ? ? ? ? ? ? ? //如果用戶名重復(fù) ? ? ? ? ? ? ? ? ? ? if (Server.clients.map.containsKey(userName)) { ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("重復(fù)"); ? ? ? ? ? ? ? ? ? ? ? ? ps.println(CrazyitProtocol.NAME_REP); ? ? ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? ? ? System.out.println(userName+"上線"); ? ? ? ? ? ? ? ? ? ? ? ? ps.println(CrazyitProtocol.LOGIN_SUCCESS); ? ? ? ? ? ? ? ? ? ? ? ? Server.clients.put(userName, ps); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? //如果讀到的行以CrazyitProtocol.PRIVATE_ROUND開始, ? ? ? ? ? ? ? ? //則可以確定是私聊信息,私聊信息只向特定的輸入流發(fā)送 ? ? ? ? ? ? ? ? else if (line.startsWith(CrazyitProtocol.PRIVATE_ROUND) && line.endsWith(CrazyitProtocol.PRIVATE_ROUND)) { ? ? ? ? ? ? ? ? ? ? //得到真實(shí)的消息 ? ? ? ? ? ? ? ? ? ? String userAndMsg = getRealMsg(line); ? ? ? ? ? ? ? ? ? ? //以SPLIT_SIGN分割字符串,前半是私聊用戶,后半是聊天信息 ? ? ? ? ? ? ? ? ? ? String user = userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[0]; ? ? ? ? ? ? ? ? ? ? String msg = userAndMsg.split(CrazyitProtocol.SPLIT_SIGN)[1]; ? ? ? ? ? ? ? ? ? ? //獲取私聊用戶對應(yīng)的輸出流,并發(fā)送私聊信息 ? ? ? ? ? ? ? ? ? ? Server.clients.map.get(user).println(Server.clients.getKeyByValue(ps) + "悄悄對你說:" + msg); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? //公聊要向每一個(gè)Socket發(fā)送 ? ? ? ? ? ? ? ? else { ? ? ? ? ? ? ? ? ? ? //得到真實(shí)消息 ? ? ? ? ? ? ? ? ? ? String msg = getRealMsg(line); ? ? ? ? ? ? ? ? ? ? //遍歷clients中的每個(gè)輸出流 ? ? ? ? ? ? ? ? ? ? for (PrintStream clientPs : Server.clients.valueSet()) { ? ? ? ? ? ? ? ? ? ? ? ? clientPs.println(Server.clients.getKeyByValue(ps) + "說:" + msg); ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? //捕獲到異常后,表明Socket對應(yīng)的客戶端已經(jīng)出現(xiàn)了問題 ? ? ? ? //所以程序?qū)⑵鋵?yīng)的輸出流從Map中刪除 ? ? ? ? catch (IOException e) { ? ? ? ? ? ? Server.clients.removeByValue(ps); ? ? ? ? ? ? System.out.println(Server.clients.map.size()); ? ? ? ? ? ? //關(guān)閉網(wǎng)絡(luò),IO資源 ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? if (br != null) { ? ? ? ? ? ? ? ? ? ? br.close(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if (ps != null) { ? ? ? ? ? ? ? ? ? ? ps.close(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if (socket != null) { ? ? ? ? ? ? ? ? ? ? socket.close(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } catch (IOException ex) { ? ? ? ? ? ? ? ? ex.printStackTrace(); ? ? ? ? ? ? } ? ? ? ? } ? ? } ? ? ? //將讀到的內(nèi)容去掉前后協(xié)議字符,恢復(fù)成真實(shí)數(shù)據(jù) ? ? 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)); ? ? ? ? ? ? //連接到服務(wù)器端 ? ? ? ? ? ? socket=new Socket("127.0.0.1",SERVER_PORT); ? ? ? ? ? ? //獲取該Socket對應(yīng)的輸入流和輸出流 ? ? ? ? ? ? ps=new PrintStream(socket.getOutputStream()); ? ? ? ? ? ? brServer=new BufferedReader(new InputStreamReader(socket.getInputStream())); ? ? ? ? ? ? String tip=" "; ? ? ? ? ? ? //采用不斷循環(huán)地彈出對話框要求輸入用戶名 ? ? ? ? ? ? while(true){ ? ? ? ? ? ? ? ? String userName= JOptionPane.showInputDialog(tip+"輸入用戶名"); ? ? ? ? ? ? ? ? //用戶輸入的用戶名前后增加協(xié)議字符串后發(fā)送 ? ? ? ? ? ? ? ? ps.println(CrazyitProtocol.USER_ROUND+userName+CrazyitProtocol.USER_ROUND); ? ? ? ? ? ? ? ? //讀取服務(wù)器端的響應(yīng) ? ? ? ? ? ? ? ? String result=brServer.readLine(); ? ? ? ? ? ? ? ? //如果用戶名重復(fù),則開始下一次循環(huán) ? ? ? ? ? ? ? ? if (result.equals(CrazyitProtocol.NAME_REP)){ ? ? ? ? ? ? ? ? ? ? tip="用戶名重復(fù),請重試"; ? ? ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? //服務(wù)器端登錄成功 ? ? ? ? ? ? ? ? if (result.equals(CrazyitProtocol.LOGIN_SUCCESS)){ ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? //捕獲到異常,關(guān)閉網(wǎng)絡(luò)資源,并退出該程序 ? ? ? ? catch (UnknownHostException ex){ ? ? ? ? ? ? System.out.println("找不到遠(yuǎn)程服務(wù)器,請確定服務(wù)器已經(jīng)啟動(dòng)"); ? ? ? ? ? ? closeRs(); ? ? ? ? ? ? System.exit(1); ? ? ? ? } ? ? ? ? catch(IOException ex){ ? ? ? ? ? ? System.out.println("網(wǎng)絡(luò)異常,請重新登錄"); ? ? ? ? ? ? closeRs(); ? ? ? ? ? ? System.exit(1); ? ? ? ? } ? ? ? ? //以該Socket對應(yīng)的輸入流啟動(dòng)ClientThread線程 ? ? ? ? new ClientThread(brServer).start(); ? ? } ? ? //定義一個(gè)讀取鍵盤輸出,并以網(wǎng)絡(luò)發(fā)送的方法 ? ? private void readAndSend(){ ? ? ? ? try ? ? ? ? { ? ? ? ? ? ? //不斷讀取鍵盤輸入 ? ? ? ? ? ? String line=null; ? ? ? ? ? ? while ((line=keyIn.readLine())!=null){ ? ? ? ? ? ? ? ? //如果發(fā)送的信號中有冒號,并以//開頭,則認(rèn)為想發(fā)送私聊信息 ? ? ? ? ? ? ? ? if (line.indexOf(":")>0&&line.startsWith("http://")){ ? ? ? ? ? ? ? ? ? ? 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("網(wǎng)絡(luò)通信異常!請重新登錄"); ? ? ? ? ? ? closeRs(); ? ? ? ? ? ? System.exit(1); ? ? ? ? } ? ? } ? ? //關(guān)閉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 { ? ? ? //該客戶端線程負(fù)責(zé)處理輸入流 ? ? BufferedReader br=null; ? ? //使用一個(gè)網(wǎng)絡(luò)輸入流來創(chuàng)建客戶端線程 ? ? public ClientThread(BufferedReader br){ ? ? ? ? this.br=br; ? ? } ? ? public void run(){ ? ? ? ? try ? ? ? ? { ? ? ? ? ? ? String line=null; ? ? ? ? ? ? //不斷地從輸入流中讀取數(shù)據(jù),并將這些數(shù)據(jù)打印輸出 ? ? ? ? ? ? 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> { ? ? ? //創(chuàng)建一個(gè)線程安全的HashMap ? ? public Map<k,v> map= Collections.synchronizedMap(new HashMap<k,v>()); ? ? //根據(jù)value來刪除指定項(xiàng) ? ? 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; ? ? } ? ? //根據(jù)value查找key ? ? public synchronized k getKeyByValue(v value){ ? ? ? ? //遍歷所有key組成的集合 ? ? ? ? for (k key:map.keySet()){ ? ? ? ? ? ? //如果指定key對應(yīng)的value與被搜索的value相同,則返回對應(yīng)的key ? ? ? ? ? ? if(map.get(key)==value||map.get(key).equals(value)){ ? ? ? ? ? ? ? ? return key; ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? return null; ? ? } ? ? //實(shí)現(xiàn)put()方法,該方法不允許value重復(fù) ? ? public synchronized v put(k key,v value){ ? ? ? ? //遍歷所有value組成的集合 ? ? ? ? for (v val:valueSet()){ ? ? ? ? ? ? //如果某個(gè)value與試圖放入集合的value相同 ? ? ? ? ? ? //則拋出一個(gè)RuntimeException異常 ? ? ? ? ? ? if (val.equals(value)&&val.hashCode()==value.hashCode()){ ? ? ? ? ? ? ? ? throw new RuntimeException("MyMap實(shí)例不允許有重復(fù)的value"); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? return map.put(key,value); ? ? } }
CrazyitProtocol 接口
/** ?* 自定義接口 ?*/ public interface CrazyitProtocol { ? ? ? //定義協(xié)議字符串的長度 ? ? int PROTOCOL_LEN=2; ? ? //下面是一些協(xié)議字符串,服務(wù)器端和客戶端交換的信息都應(yīng)該在前后添加這種特殊字符串 ? ? String MSG_ROUND="ηθ"; ? ? String USER_ROUND="∏∑"; ? ? String LOGIN_SUCCESS="1"; ? ? String NAME_REP="-1"; ? ? String PRIVATE_ROUND="★【"; ? ? String SPLIT_SIGN="卐"; }
測試
1、啟動(dòng)服務(wù)端
2、啟動(dòng)客戶端 (上線)
3 、群發(fā)
4、私聊 (//名字:)
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java實(shí)現(xiàn)局域網(wǎng)聊天小程序
- Java用局域網(wǎng)實(shí)現(xiàn)聊天室功能
- java?socket實(shí)現(xiàn)局域網(wǎng)聊天
- Java創(chuàng)建多線程局域網(wǎng)聊天室實(shí)例
- Java實(shí)現(xiàn)簡單局域網(wǎng)聊天室
- Java多線程局域網(wǎng)聊天室的實(shí)現(xiàn)
- java局域網(wǎng)聊天小程序
- java實(shí)現(xiàn)簡易局域網(wǎng)聊天功能
- 基于java編寫局域網(wǎng)多人聊天室
- java+socket實(shí)現(xiàn)簡易局域網(wǎng)聊天室
相關(guān)文章
深入解析Java編程中面向字節(jié)流的一些應(yīng)用
這篇文章主要介紹了Java編程中面向字節(jié)流的一些應(yīng)用,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-10-10如何使用JFrame完成動(dòng)態(tài)模擬時(shí)鐘
本文介紹了如何使用JFrame完成動(dòng)態(tài)模擬時(shí)鐘,需要的朋友可以參考下2015-08-08使用jvisualvm配合Visual GC插件監(jiān)控Java程序詳細(xì)總結(jié)
本節(jié)將會(huì)介紹一下jvisualvm的特性及作用、各個(gè)功能是如何使用的、最后會(huì)介紹jvisualvm的插件Visual GC的安裝及使用2021-09-09IntelliJ IDEA 2019.2 x64的安裝、應(yīng)用與簡單配置(圖文)
這篇文章主要介紹了IntelliJ IDEA 2019.2 x64的安裝、應(yīng)用與簡單配置,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10SpringBoot實(shí)現(xiàn)接口校驗(yàn)簽名調(diào)用的項(xiàng)目實(shí)踐
在以SpringBoot開發(fā)后臺API接口時(shí),會(huì)存在哪些接口不安全的因素呢?通常如何去解決的呢?本文主要介紹了SpringBoot實(shí)現(xiàn)接口校驗(yàn)簽名調(diào)用的項(xiàng)目實(shí)踐,感興趣的可以了解一下2023-09-09Java數(shù)據(jù)結(jié)構(gòu)篇之實(shí)現(xiàn)二叉搜索樹的核心方法
二叉搜索樹是一種常用的數(shù)據(jù)結(jié)構(gòu),它是一棵二叉樹,且每個(gè)節(jié)點(diǎn)的值都大于其左子樹中任何節(jié)點(diǎn)的值,而小于其右子樹中任何節(jié)點(diǎn)的值,這篇文章主要給大家介紹了關(guān)于Java數(shù)據(jù)結(jié)構(gòu)篇之實(shí)現(xiàn)二叉搜索樹的核心方法,需要的朋友可以參考下2023-12-12