日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

完成聊天室的私聊功能

發布時間:2023/12/10 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 完成聊天室的私聊功能 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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 {
  • ????//客戶端Socket
  • ????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 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 {
  • ????// 服務端Socket
  • ????private ServerSocket serverSocket;
  • ????// 所有客戶端輸出流,key為用戶的昵稱,value為該用戶的輸出流
  • ????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 {
  • ????//其他代碼,略
  • ????/**
  • ???? * 將輸出流存入共享集合,與下面兩個方法互斥,保證同步安全
  • ???? * @param out
  • ???? */
  • ????private synchronized void addOut(String nickName,PrintWriter out){
  • ????????allOut.put(nickName,out);
  • ????}
  • ????/**
  • ???? * 將給定輸出流從共享集合刪除
  • ???? * @param out
  • ???? */
  • ????private synchronized void removeOut(String nickName){
  • ????????allOut.remove(nickName);
  • ????}
  • }
  • 步驟八:為 Server 類定義sendMessage()方法

    定義sendMessage()方法,該方法用于遍歷Server的屬性allOut集合元素,將信息寫入每一個輸出流來完成廣播消息的功能,并使用synchronized關鍵字修飾,使該方法變為同步方法。代碼如下所示:

  • public class Server {
  • ????//其他代碼,略
  • ????/**
  • ???? * 將消息轉發給所有客戶端
  • ???? * @param message
  • ???? */
  • ????private synchronized void sendMessage(String message){
  • ????????for(PrintWriter o : allOut.values()){
  • ????????????o.println(message);
  • ????????}
  • ????}
  • }
  • 步驟九:為 Server 類定義sendMessageToOne() 方法

    定義sendMessageToOne()方法,該方法用于將消息發送給指定昵稱的客戶端來實現私聊功能。代碼如下所示:

  • public class Server {
  • ????//其他代碼,略
  • ????/**
  • ???? * 將消息發送給指定昵稱的客戶端
  • ???? * @param nickName
  • ???? * @param message
  • ???? */
  • ????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 {
  • ????????//其他代碼,略
  • ????????/**
  • ???????? * 獲取該用戶的昵稱
  • ???????? * @return
  • ???????? */
  • ????????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 {
  • ????// 服務端Socket
  • ????private ServerSocket serverSocket;
  • ????// 所有客戶端輸出流,key為用戶的昵稱,value為該用戶的輸出流
  • ????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();
  • ????????}
  • ????}
  • ????/**
  • ???? * 將輸出流存入共享集合,與下面兩個方法互斥,保證同步安全
  • ???? * @param out
  • ???? */
  • ????private synchronized void addOut(String nickName,PrintWriter out){
  • ????????allOut.put(nickName,out);
  • ????}
  • ????/**
  • ???? * 將給定輸出流從共享集合刪除
  • ???? * @param out
  • ???? */
  • ????private synchronized void removeOut(String nickName){
  • ????????allOut.remove(nickName);
  • ????}
  • ????/**
  • ???? * 將消息轉發給所有客戶端
  • ???? * @param message
  • ???? */
  • ????private synchronized void sendMessage(String message){
  • ????????for(PrintWriter o : allOut.values()){
  • ????????????o.println(message);
  • ????????}
  • ????}
  • ????/**
  • ???? * 將消息發送給指定昵稱的客戶端
  • ???? * @param nickName
  • ???? * @param 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();
  • ????????????????????}
  • ????????????????}
  • ????????????}
  • ????????}
  • ????????/**
  • ???????? * 獲取該用戶的昵稱
  • ???????? * @return
  • ???????? */
  • ????????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 {
  • ????//客戶端Socket
  • ????private Socket socket;
  • ????/**
  • ???? * 構造方法,用于初始化
  • ???? */
  • ????public Client(){
  • ????????try {
  • ????????????socket = new Socket("localhost",8088);
  • ????????} catch (Exception e) {
  • ????????????e.printStackTrace();
  • ????????}
  • ????}
  • ????/**
  • ???? * 客戶端工作方法
  • ???? */
  • ????public void start(){
  • ????????try {
  • ????????????//創建Scanner讀取用戶輸入內容
  • ????????????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

    總結

    以上是生活随笔為你收集整理的完成聊天室的私聊功能的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。