生活随笔
收集整理的這篇文章主要介紹了
完成聊天室的私聊功能
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1 完成聊天室的私聊功能
完成聊天室私聊功能。私聊功能是指,客戶端之間可以實現一對一的聊天。
服務器端程序啟動后,將等待客戶端連接,界面效果如圖-1所示:
圖-1
客戶端程序運行時,需要用戶先輸入昵稱。用戶輸入昵稱之后,提示用戶可以開始聊天。界面效果如圖-2所示:
圖-2
另一個客戶端運行起來后,也需要輸入昵稱,界面效果如圖-3所示:
圖-3
此時,其他運行中的客戶端會收到昵稱為“jerry”的客戶端上線的消息。比如,之前運行起來的客戶端“mary”的界面效果如圖-4所示:
圖-4
其他客戶端可以通過輸入類似“\jerry:你好”這樣的字樣和昵稱為“jerry”的客戶端私聊。比如,昵稱為“mary”的客戶端可以輸入如圖-5所示的信息:
圖-5
注意:如果需要進行私聊,必需使用“\昵稱:信息”的格式發送消息。其中,“\昵稱:”為固定格式,“昵稱”表示要私聊的客戶端的昵稱;“信息”表示需要發送的消息。例如:"\jerry:你好",表示發送消息“你好”給昵稱為“jerry”的客戶端。
昵稱為“jerry”的客戶端將接收到客戶端“mary”發來的信息,界面效果如圖-6所示:
圖-6
如果某客戶端程序停止運行,其他客戶端程序可以接收到消息并顯示。例如,昵稱為“jerry”的客戶端停止運行,昵稱為“mary”的客戶端的界面效果如圖-7所示:
圖-7
對于服務器端而言,只要有客戶端連接,就會在界面輸出提示信息。界面效果如圖-8所示:
圖-8
參考答案
實現此案例需要按照如下步驟進行。
步驟一:創建客戶端類
新建名為com.tarena.homework的包,并在包下新建名為Client的類,用于表示客戶端。
在Client 類中聲明全局變量 socket 表示一個客戶端Socket對象,并在實例化 Client 類時使用構造方法“Socket(String ip,int port)”來創建Socket類的對象。此時,需要進行異常處理。代碼如下所示:
package com.tarena.homework; import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.Socket;import java.util.Scanner; public class Client {????????private Socket socket;????????public Client(){????????try {????????????socket = new Socket("localhost",8088);????????} catch (Exception e) {????????????e.printStackTrace();????????}????}} 步驟二:定義客戶端線程要執行的任務
在Client類中定義成員內部類ServerHander。該內部類需要實現Runnable接口并實現該接口的run() 方法。在該方法中實現線程要執行的任務,在此,線程要執行的任務為循環接收服務端的消息并打印到控制臺。代碼如下所示:
public class Client {????????????????private class ServerHander implements Runnable{????????@Override????????public void run() {????????????try {????????????????InputStream in = socket.getInputStream();????????????????InputStreamReader isr = new InputStreamReader(in, "UTF-8");????????????????BufferedReader br = new BufferedReader(isr);????????????????while(true){????????????????????System.out.println(br.readLine());????????????????}????????????} catch (Exception e) {????????????????e.printStackTrace();????????????}????????}????}} 步驟三:定義方法inputNickName(),用于輸入昵稱
為Client類定義方法inputNickName(),用于輸入昵稱。代碼如下所示:
public class Client {????????????????private void inputNickName(Scanner scanner)throws Exception{????????????????String nickName = null;????????????????PrintWriter pw = new PrintWriter(????????????????????????????new OutputStreamWriter(????????????????????????????????socket.getOutputStream(),"UTF-8")???????????????????????? ,true);????????????????BufferedReader br = new BufferedReader(????????????????????????????????new InputStreamReader(????????????????????????????????????????socket.getInputStream(),"UTF-8")???????????????????????? );????????????????while(true){????????????System.out.println("請輸入昵稱:");????????????nickName = scanner.nextLine();????????????if(nickName.trim().equals("")){????????????????System.out.println("昵稱不能為空");????????????}else{????????????????pw.println(nickName);????????????????String pass = br.readLine();????????????????if(pass!=null&&!pass.equals("OK")){????????????????????System.out.println("昵稱已被占用,請更換。");????????????????}else{????????????????????System.out.println("你好!"+nickName+",開始聊天吧!");????????????????????break;????????????????}????????????}????????}????}} 步驟四:創建客戶端工作方法 start()
為 Client 類創建客戶端工作方法 start()。在該方法中,首先調用方法inputNickName()得到用戶昵稱,然后啟動接收服務端信息的線程,接收數據后打印顯示。
代碼如下所示:
public class Client {???? ????????public void start(){????????try {????????????????????????Scanner scanner = new Scanner(System.in);????????????????????????inputNickName(scanner);????????????????????????????????????ServerHander handler = new ServerHander();????????????Thread t = new Thread(handler);????????????t.setDaemon(true);????????????t.start();????????????????????????OutputStream out = socket.getOutputStream();????????????????????????OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");????????????????????????PrintWriter pw = new PrintWriter(osw,true);????????????while(true){????????????????pw.println(scanner.nextLine());????????????}????????} catch (Exception e) {????????????e.printStackTrace();????????} finally{????????????if(socket != null){????????????????try {????????????????????socket.close();????????????????} catch (IOException e) {????????????????????e.printStackTrace();????????????????}????????????}????????}????}} 步驟五:為客戶端類定義 main() 方法
為類 Client 定義 main() 方法,并在該方法中,創建 Client 對象,調用上一步中所創建的 start() 方法。代碼如下所示:
public class Client {????????????public static void main(String[] args) {????????Client client = new Client();????????client.start();????}} 步驟六:定義 Server類
定義Server類,并在Server類中添加ExecutorService類型的屬性threadPool,并在構造方法中將其初始化。初始化時,使用固定大小的線程池,線程數量為40。這里使用Executors類的newFixedThreadPool(int threads)方法來創建固定大小的線程池。定義屬性serverSocket,其類型為ServerSocket,并在構造方法中將其初始化,申請的服務端口為8088。再定義屬性allOut,其類型為HashMap,其中key用于保存用戶昵稱,value用于保存該客戶端的輸出流,并在構造方法中初始化以便服務端可以轉發信息。
代碼如下所示:
package com.tarena.homework; import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import java.util.HashMap;import java.util.Map;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; public class Server {????????private ServerSocket serverSocket;????????private Map<String,PrintWriter> allOut;????????private ExecutorService threadPool;????????public Server() {????????try {????????????serverSocket = new ServerSocket(8088);????????????????????????allOut = new HashMap<String,PrintWriter>();????????????????????????threadPool = Executors.newFixedThreadPool(40);????????} catch (Exception e) {????????????e.printStackTrace();????????}????}} 步驟七:為 Server 類定義 addOut()和removeOut()方法
定義 addOut()方法,該方法向Server的屬性allOut集合中添加輸出流,并使用synchronized關鍵字修飾,使該方法變為同步方法。
再定義removeOut()方法,該方法從Server的屬性allOut集合中刪除輸出流,并使用synchronized關鍵字修飾,使該方法變為同步方法。
代碼如下所示:
public class Server {???? ????????private synchronized void addOut(String nickName,PrintWriter out){????????allOut.put(nickName,out);????}????????private synchronized void removeOut(String nickName){????????allOut.remove(nickName);????}} 步驟八:為 Server 類定義sendMessage()方法
定義sendMessage()方法,該方法用于遍歷Server的屬性allOut集合元素,將信息寫入每一個輸出流來完成廣播消息的功能,并使用synchronized關鍵字修飾,使該方法變為同步方法。代碼如下所示:
public class Server {???? ????????private synchronized void sendMessage(String message){????????for(PrintWriter o : allOut.values()){????????????o.println(message);????????}????}} 步驟九:為 Server 類定義sendMessageToOne() 方法
定義sendMessageToOne()方法,該方法用于將消息發送給指定昵稱的客戶端來實現私聊功能。代碼如下所示:
public class Server {???? ????????private synchronized void sendMessageToOne(String nickName,String message){????????PrintWriter out = allOut.get(nickName);????????if(out!=null){????????????out.println(message);????????}????}} 步驟十:創建內部類
創建 Server的內部類ClientHandler,在內部類中定義run()方法。在run()方法中,讀取用戶昵稱以發送用戶上線信息,并進行消息轉發,其中先判斷是否為私聊信息,若是則調用發送私聊信息的方法,否則向所有客戶端廣播消息 。代碼如下所示:
????????private class ClientHandler implements Runnable {????????????????private Socket socket;????????????????private String nickName; ????????public ClientHandler(Socket socket) {????????????this.socket = socket;????????} ????????@Override????????public void run() {????????????PrintWriter pw = null;????????????try {????????????????????????????????OutputStream out = socket.getOutputStream();????????????????OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");????????????????pw = new PrintWriter(osw,true);????????????????????????????????????????????????nickName = getNickName();????????????????addOut(nickName,pw);????????????????Thread.sleep(100);????????????????????????????????sendMessage(nickName+"上線了");????????????????????????????????InputStream in = socket.getInputStream();????????????????InputStreamReader isr = new InputStreamReader(in, "UTF-8");????????????????BufferedReader br = new BufferedReader(isr);????????????????????????????????String message = null;????????????????????????????????while ((message = br.readLine())!=null) {????????????????????????????????????????if(message.startsWith("\\")){????????????????????????????????????????????????????????????????????????int index = message.indexOf(":");????????????????????????if(index>=0){????????????????????????????????????????????????????????String name = message.substring(1,index);????????????????????????????????????????????????????????String info = message.substring(???????????????????????????????????????????????? index+1,message.length()???????????????????????????????????????????????? );????????????????????????????????????????????????????????info = nickName+"對你說:"+info;????????????????????????????????????????????????????????sendMessageToOne(name, info);????????????????????????????????????????????????????????continue;????????????????????????}????????????????????}????????????????????????????????????????sendMessage(nickName+"說:"+message);????????????????}????????????} catch (Exception e) {????????????????e.printStackTrace();????????????} finally {????????????????????????????????removeOut(nickName);????????????????????????????????sendMessage(nickName+"下線了");????????????????System.out.println("當前在線人數:"+allOut.size());????????????????if (socket != null) {????????????????????try {????????????????????????socket.close();????????????????????} catch (IOException e) {????????????????????????e.printStackTrace();????????????????????}????????????????}????????????}????????}} 步驟十一:為內部類定義方法getNickName()
為 Server的內部類ClientHandler定義方法getNickName(),用于獲取用戶的昵稱。代碼如下所示:
????private class ClientHandler implements Runnable {???????? ????????????????private String getNickName()throws Exception{????????????try {????????????????????????????????OutputStream out = socket.getOutputStream();????????????????OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");????????????????PrintWriter pw = new PrintWriter(osw,true);????????????????????????????????InputStream in = socket.getInputStream();????????????????InputStreamReader isr = new InputStreamReader(in, "UTF-8");????????????????BufferedReader br = new BufferedReader(isr);????????????????????????????????String nickName = br.readLine();????????????????while(true){????????????????????????????????????????if(nickName.trim().equals("")){????????????????????????pw.println("FAIL");????????????????????}????????????????????????????????????????if(allOut.containsKey(nickName)){????????????????????????pw.println("FAIL");????????????????????????????????????????}else{????????????????????????pw.println("OK");????????????????????????return nickName;????????????????????}????????????????????????????????????????nickName = br.readLine();????????????????}????????????} catch (Exception e) {????????????????throw e;????????????}????????????????????}????} 步驟十二:為 Server 類創建 start()方法
為 Server 類創建 start()方法。在該方法中,循環監聽8088端口,等待客戶端的連接,一旦一個客戶端連接后,向線程池申請一個線程來完成針對該客戶端的交互。代碼如下所示:
public class Server {???? ????????public void start() {????????try {????????????????????????while(true){????????????????System.out.println("等待客戶端連接...");????????????????????????????????Socket socket = serverSocket.accept();????????????????System.out.println("客戶端已連接!");????????????????????????????????????????????????ClientHandler handler = new ClientHandler(socket);????????????????threadPool.execute(handler);????????????}????????????????????} catch (Exception e) {????????????e.printStackTrace();????????}????}} 步驟十三:為 Server類定義 main() 方法
為 Server 類定義 main() 方法,并在 main() 方法中,創建 Server 對象,調用上一步中所創建的 start() 方法。代碼如下所示:
public class Server {???? ????public static void main(String[] args) {????????Server server = new Server();????????server.start();????}} 本案例中,類Server的完整代碼如下所示:
package com.tarena.homework; import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.ServerSocket;import java.net.Socket;import java.util.HashMap;import java.util.Map;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors; public class Server {????????private ServerSocket serverSocket;????????private Map<String,PrintWriter> allOut;????????private ExecutorService threadPool;????????public Server() {????????try {????????????serverSocket = new ServerSocket(8088);????????????????????????allOut = new HashMap<String,PrintWriter>();????????????????????????threadPool = Executors.newFixedThreadPool(40);????????} catch (Exception e) {????????????e.printStackTrace();????????}????} ????????public void start() {????????try {????????????????????????while(true){????????????????System.out.println("等待客戶端連接...");????????????????????????????????Socket socket = serverSocket.accept();????????????????System.out.println("客戶端已連接!");????????????????????????????????????????????????ClientHandler handler = new ClientHandler(socket);????????????????threadPool.execute(handler);????????????}????????????????????} catch (Exception e) {????????????e.printStackTrace();????????}????}????????private synchronized void addOut(String nickName,PrintWriter out){????????allOut.put(nickName,out);????}????????private synchronized void removeOut(String nickName){????????allOut.remove(nickName);????}????????private synchronized void sendMessage(String message){????????for(PrintWriter o : allOut.values()){????????????o.println(message);????????}????}????????private synchronized void sendMessageToOne(String nickName,String message){????????PrintWriter out = allOut.get(nickName);????????if(out!=null){????????????out.println(message);????????}????}????????public static void main(String[] args) {????????Server server = new Server();????????server.start();????} ????????private class ClientHandler implements Runnable {????????????????private Socket socket;????????????????private String nickName; ????????public ClientHandler(Socket socket) {????????????this.socket = socket;????????} ????????@Override????????public void run() {????????????PrintWriter pw = null;????????????try {????????????????????????????????OutputStream out = socket.getOutputStream();????????????????OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");????????????????pw = new PrintWriter(osw,true);????????????????????????????????????????????????nickName = getNickName();????????????????addOut(nickName,pw);????????????????Thread.sleep(100);????????????????????????????????sendMessage(nickName+"上線了");????????????????????????????????InputStream in = socket.getInputStream();????????????????InputStreamReader isr = new InputStreamReader(in, "UTF-8");????????????????BufferedReader br = new BufferedReader(isr);????????????????????????????????String message = null;????????????????????????????????while ((message = br.readLine())!=null) {????????????????????????????????????????if(message.startsWith("\\")){????????????????????????????????????????????????????????????????????????int index = message.indexOf(":");????????????????????????if(index>=0){????????????????????????????????????????????????????????String name = message.substring(1,index);????????????????????????????????????????????????????????String info = message.substring(index+1,message.length());????????????????????????????????????????????????????????info = nickName+"對你說:"+info;????????????????????????????????????????????????????????sendMessageToOne(name, info);????????????????????????????????????????????????????????continue;????????????????????????}????????????????????}????????????????????????????????????????sendMessage(nickName+"說:"+message);????????????????}????????????} catch (Exception e) {????????????????e.printStackTrace();????????????} finally {????????????????????????????????removeOut(nickName);????????????????????????????????sendMessage(nickName+"下線了");????????????????System.out.println("當前在線人數:"+allOut.size());????????????????if (socket != null) {????????????????????try {????????????????????????socket.close();????????????????????} catch (IOException e) {????????????????????????e.printStackTrace();????????????????????}????????????????}????????????}????????}????????????????private String getNickName()throws Exception{????????????try {????????????????????????????????OutputStream out = socket.getOutputStream();????????????????OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");????????????????PrintWriter pw = new PrintWriter(osw,true);????????????????????????????????InputStream in = socket.getInputStream();????????????????InputStreamReader isr = new InputStreamReader(in, "UTF-8");????????????????BufferedReader br = new BufferedReader(isr);????????????????????????????????String nickName = br.readLine();????????????????while(true){????????????????????????????????????????if(nickName.trim().equals("")){????????????????????????pw.println("FAIL");????????????????????}????????????????????????????????????????if(allOut.containsKey(nickName)){????????????????????????pw.println("FAIL");????????????????????????????????????????}else{????????????????????????pw.println("OK");????????????????????????return nickName;????????????????????}????????????????????????????????????????nickName = br.readLine();????????????????}????????????} catch (Exception e) {????????????????throw e;????????????}????????????????????}????}} ?
本案例中,類Client的完整代碼如下所示:
package com.tarena.homework; import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.OutputStream;import java.io.OutputStreamWriter;import java.io.PrintWriter;import java.net.Socket;import java.util.Scanner; public class Client {????????private Socket socket;????????public Client(){????????try {????????????socket = new Socket("localhost",8088);????????} catch (Exception e) {????????????e.printStackTrace();????????}????}????????public void start(){????????try {????????????????????????Scanner scanner = new Scanner(System.in);????????????????????????inputNickName(scanner);????????????????????????????????????ServerHander handler = new ServerHander();????????????Thread t = new Thread(handler);????????????t.setDaemon(true);????????????t.start();????????????????????????OutputStream out = socket.getOutputStream();????????????????????????OutputStreamWriter osw = new OutputStreamWriter(out,"UTF-8");????????????????????????PrintWriter pw = new PrintWriter(osw,true);????????????while(true){????????????????pw.println(scanner.nextLine());????????????}????????} catch (Exception e) {????????????e.printStackTrace();????????} finally{????????????if(socket != null){????????????????try {????????????????????socket.close();????????????????} catch (IOException e) {????????????????????e.printStackTrace();????????????????}????????????}????????}????}????????public static void main(String[] args) {????????Client client = new Client();????????client.start();????}????????????private void inputNickName(Scanner scanner)throws Exception{????????????????String nickName = null;????????????????PrintWriter pw = new PrintWriter(????????????????????????????new OutputStreamWriter(????????????????????????????????socket.getOutputStream(),"UTF-8")???????????????????????? ,true);????????????????BufferedReader br = new BufferedReader(????????????????????????????????new InputStreamReader(????????????????????????????????????????socket.getInputStream(),"UTF-8")???????????????????????? );????????????????while(true){????????????System.out.println("請輸入昵稱:");????????????nickName = scanner.nextLine();????????????if(nickName.trim().equals("")){????????????????System.out.println("昵稱不能為空");????????????}else{????????????????pw.println(nickName);????????????????String pass = br.readLine();????????????????if(pass!=null&&!pass.equals("OK")){????????????????????System.out.println("昵稱已被占用,請更換。");????????????????}else{????????????????????System.out.println("你好!"+nickName+",開始聊天吧!");????????????????????break;????????????????}????????????}????????}????}????????????private class ServerHander implements Runnable{????????@Override????????public void run() {????????????try {????????????????InputStream in = socket.getInputStream();????????????????InputStreamReader isr = new InputStreamReader(in, "UTF-8");????????????????BufferedReader br = new BufferedReader(isr);????????????????while(true){????????????????????System.out.println(br.readLine());????????????????}????????????} catch (Exception e) {????????????????e.printStackTrace();????????????}????????}????}} ?
轉載于:https://www.cnblogs.com/xyk1987/p/8330970.html
總結
以上是生活随笔為你收集整理的完成聊天室的私聊功能的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。