欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java實現(xiàn)多線程聊天室

 更新時間:2022年09月15日 16:38:02   作者:#define微光  
這篇文章主要為大家詳細(xì)介紹了Java實現(xiàn)多線程聊天室,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下

本文實例為大家分享了Java實現(xiàn)多線程聊天室的具體代碼,供大家參考,具體內(nèi)容如下

之前呢已經(jīng)用單線程的方式來實現(xiàn)了聊天室,但其實它的功能并不齊全,下面用多線程來實現(xiàn),功能會比單線程聊天室更加齊全,也更人性化一點

多線程版本的聊天室

1. 功能分析:

  • 實現(xiàn)用戶注冊,上線,下線
  • 實現(xiàn)群聊和私聊
  • 統(tǒng)計當(dāng)前在線人數(shù)

2. 服務(wù)端實現(xiàn)

1.維護(hù)所有的在線用戶

2.注冊功能:客戶端名稱,添加到服務(wù)器的客戶端集合里

3.群聊功能:客戶端發(fā)送消息,所有的客戶端都能接收到

4.私聊功能:客戶端與指定客戶端進(jìn)發(fā)送和接收消息

5.退出功能: 從服務(wù)器客戶端集合中移除客戶端

3. 客戶端實現(xiàn)

1.注冊功能:創(chuàng)建Socket對象,給服務(wù)器發(fā)送注冊執(zhí)行(消息)

2.群聊功能:客戶端發(fā)送和接收數(shù)據(jù)

3.私聊功能:客戶端指定客戶端(用戶),發(fā)送和接收數(shù)據(jù)

4.退出功能:給服務(wù)器發(fā)送退出指令(消息)

5.命令行的交互式輸入輸出 

4.實現(xiàn)思路: 

首先,要實現(xiàn)服務(wù)端與客戶端之間的連接

這里是使用套接字建立TCP連接:

(1)服務(wù)器端先實例化一個描述服務(wù)器端口號的ServerSocket對象

(2)客戶端要創(chuàng)建Socket對象來連接指定的服務(wù)器端

(3)服務(wù)器端調(diào)用ServerSocket類的accept()方法來監(jiān)聽連接到服務(wù)器端的客戶端信息

(4)若服務(wù)器端與客戶端連接成功,雙方將返回一個Socket對象,此時雙方可以進(jìn)行通信

(5)服務(wù)器端與客戶端使用I/O流進(jìn)行連接,服務(wù)端的輸出流連接客戶端的輸入流,客戶端的輸出流連接服務(wù)端的輸入流

(6)使用close()方法關(guān)閉套接字(一定要記得關(guān)閉)

2.因為是擁有一個服務(wù)端來實現(xiàn)多個客戶端的連接,此處還要解決的是多線程的問題。

每個客戶端需要兩個線程,來分別處理向服務(wù)端發(fā)送消息和向服務(wù)端接收消息

而服務(wù)端,當(dāng)每增加一個客戶端與服務(wù)端連接,服務(wù)端都要多創(chuàng)建一個線程來處理與客戶端的連接

5. 圖解析 

6. 服務(wù)端代碼實現(xiàn)

Server類

package test.Server;
 
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
/**
 * package:test.Server
 * Description:服務(wù)器端
 * @date:2019/8/14
 * @Author:weiwei
 **/
public class server {
    public static void main(String[] args) {
        try {
            int port = 6666;
 
            ServerSocket serverSocket = new ServerSocket(port);
 
            System.out.println("服務(wù)器啟動..." + serverSocket.getLocalSocketAddress());  //服務(wù)器啟動,打印本地地址
 
            //線程池
            ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
 
            while (true) {  //死循環(huán)
                Socket client = serverSocket.accept();
                System.out.println("有客戶端連接到服務(wù)器:" + client.getRemoteSocketAddress());
                executorService.execute(new HandlerClient(client));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

HandlerClient類

package test.Server;
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
 
 
/**
 * Author:weiwei
 * description:HandlerClient
 * Creat:2019/3/12
 **/
public class HandlerClient implements Runnable {
 
    /**
     * 維護(hù)所有的連接到服務(wù)端的客戶端對象
     */
    private static final Map<String,Socket> ONLINE_CLIENT_MAP =
            new ConcurrentHashMap<String, Socket>();  //靜態(tài)是為了不讓對象變化,final不讓對象被修改,ConcurrentHashMap是線程安全的類
                                        //static final修飾后變量名應(yīng)該用常量--大寫字母加下劃線分隔
    private final Socket client;
    public HandlerClient(Socket client) {  //HandlerClient在多線程環(huán)境下調(diào)用,所以會產(chǎn)生資源競爭,用一個并發(fā)的HashMap
        this.client = client;          //為了防止變量被修改,用final修飾
    }
 
    //@Override
    public void run() {
        try {
            InputStream clientInput=client.getInputStream(); //獲取客戶端的數(shù)據(jù)流
            Scanner scanner = new Scanner(clientInput); //字節(jié)流轉(zhuǎn)字符流
 
            /**
             *消息是按行讀取
             * 1.register:<username> 例如: register:張三
             * 2.群聊: groupChat:<message> 例如:groupChat:大家好
             * 3.私聊: privateChat:張三:你好,還錢
             * 4.退出:bye
             */
 
            while(true){
                String data = scanner.nextLine();  //讀數(shù)據(jù),按行讀
                if(data.startsWith("register:")){
                    //注冊
                    String userName = data.split(":")[1];//冒號分隔,取第一個
                    register(userName);
                    continue;
                }
 
                if(data.startsWith("groupChat:")){
                    String message = data.split(":")[1];
                    groupChat(message);
                    continue;
                }
 
                if(data.startsWith("privateChat:")){
                    String [] segments = data.split(":");
                    String targetUserName = segments[1].split("\\-")[0]; //取目標(biāo)用戶名
                    String message = segments[1].split("\\-")[1];   //因為要取兩次,所以用數(shù)組 //取發(fā)送的消息內(nèi)容
                    privateChat(targetUserName,message);
                    continue;
                }
 
                if(data.equals("bye")){
                    //表示退出
                    bye();
                    continue;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    /**
     * 當(dāng)前客戶端退出
     */
    private void bye() {
        for(Map.Entry<String,Socket> entry : ONLINE_CLIENT_MAP.entrySet()){
            Socket target = entry.getValue();
            if(target.equals(this.client)){   //在在線用戶中找到自己并且移除
                ONLINE_CLIENT_MAP.remove(entry.getKey());
                break;
            }
            System.out.println(getCurrentUserName()+"退出聊天室");
        }
        printOnlineClient();//打印當(dāng)前用戶
    }
 
    private String getCurrentUserName(){
        for (Map.Entry<String, Socket> entry : ONLINE_CLIENT_MAP.entrySet()) {
            Socket target = entry.getValue(); //getvalue得到Socket對象
            if(target.equals(this.client)){ //排除群聊的時候自己給自己發(fā)消息的情況
                return entry.getKey();
            }
        }
        return "";
    }
 
    /**
     * 私聊,給targetUserName發(fā)送message消息
     * @param targetUserName
     * @param message
     */
    private void privateChat(String targetUserName, String message) {
        Socket target = ONLINE_CLIENT_MAP.get(targetUserName);//獲取目標(biāo)用戶名
        if(target == null){
            this.sendMessage(this.client,"沒有這個人"+targetUserName,false);
        }else{
            this.sendMessage(target,message,true);
        }
    }
 
    /**
     * 群聊,發(fā)送message
     * @param message
     */
    private void groupChat(String message) {
        for (Map.Entry<String, Socket> entery : ONLINE_CLIENT_MAP.entrySet()) {
            Socket target = entery.getValue(); //getvalue得到Socket對象
            if(target.equals(this.client)){
                continue;            //排除群聊的時候自己給自己發(fā)消息的情況
            }
            this.sendMessage(target,message,true);
        }
    }
 
    /**
     * 以userName為key注冊當(dāng)前用戶(Socket client)
     * @param userName
     */
    private void register(String userName) {
        if(ONLINE_CLIENT_MAP.containsKey(userName)){
            this.sendMessage(this.client,"您已經(jīng)注冊過了,無需重復(fù)注冊",false);
        }else{
            ONLINE_CLIENT_MAP.put(userName,this.client);
            printOnlineClient();
            this.sendMessage(this.client,"恭喜"+userName+"注冊成功\n",false);
        }
    }
 
    private void sendMessage(Socket target,String message,boolean prefix){
        OutputStream clientOutput = null;      //value是每一個客戶端
        try {
            clientOutput = target.getOutputStream();
            OutputStreamWriter writer = new OutputStreamWriter(clientOutput);
            if(prefix) {
                String currentUserName = this.getCurrentUserName();
                writer.write("<" + currentUserName + "說:>" + message + "\n");
            }else{
                writer.write( message + "\n");
            }
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 打印在線客戶端
     */
    private void printOnlineClient(){
        System.out.println("當(dāng)前在線人數(shù):"+ONLINE_CLIENT_MAP.size()+","+"用戶名如下列表:");
        for(String userName : ONLINE_CLIENT_MAP.keySet()){  //Map的key為用戶名
            System.out.println(userName);
        }
    }
}

7. 客戶端代碼實現(xiàn) 

Client類

package Cilent;
 
import java.io.IOException;
import java.net.Socket;
 
/**
 * package:Cilent
 * Description:客戶端
 * @date:2019/8/14
 * @Author:weiwei
 **/
public class cilent {
    public static void main(String[] args) {
        try {
            //讀取地址
            String host = "127.0.0.1";
            //讀取端口號
            int port = 6666;
 
            Socket client = new Socket(host,port); //先寫數(shù)據(jù)再讀數(shù)據(jù),讀寫線程分離
            new ReadDataFromServerThread(client).start();//啟動讀線程
            new WriteDataToServerThread(client).start();//啟動寫線程
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

WriteDateToServer類

package Cilent;
 
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;
 
/**
 * Author:weiwei
 * description:客戶端給服務(wù)端發(fā)送數(shù)據(jù)的線程
 * 發(fā)送的數(shù)據(jù)來自命令行的交互式輸入
 * Creat:2019/3/12
 **/
public class WriteDataToServerThread extends Thread{
    private final Socket client;
    public WriteDataToServerThread(Socket client){
        this.client = client;
    }
    @Override
    public void run(){
        try {
            OutputStream clientOutput = this.client.getOutputStream();
            OutputStreamWriter writer = new OutputStreamWriter(clientOutput);
            Scanner scanner = new Scanner(System.in);  //有客戶端輸入數(shù)據(jù)
            while(true){
                System.out.print("請輸入>>");
                String data = scanner.nextLine(); //讀數(shù)據(jù)
                writer.write(data+"\n");
                writer.flush();
                if(data.equals("bye")){
                    System.out.println("您已下線...");
                    break;
                }
            }
            this.client.close();
        } catch (IOException e) {
           // e.printStackTrace();
        }
    }
}

ReadDateFromServer類

package Cilent;
 
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.util.Scanner;
 
/**
 * Author:weiwei
 * description:客戶端從服務(wù)端讀取數(shù)據(jù)的線程
 * Creat:2019/3/12
 **/
public class ReadDataFromServerThread extends Thread {
    private final Socket client;
    public ReadDataFromServerThread(Socket client){
        this.client=client;
    }
 
    @Override
    public void run(){
        try {
            InputStream clientInput = this.client.getInputStream();
            Scanner scanner = new Scanner(clientInput);
            while(true){
                String data = scanner.nextLine();//按行讀數(shù)據(jù)
                System.out.println("來自服務(wù)端消息:"+data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Nacos配置中心的配置文件的匹配規(guī)則及說明

    Nacos配置中心的配置文件的匹配規(guī)則及說明

    這篇文章主要介紹了Nacos配置中心的配置文件的匹配規(guī)則及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • 解決IDEA刪除子模塊并重建后MAVEN無法識別的問題

    解決IDEA刪除子模塊并重建后MAVEN無法識別的問題

    這篇文章主要介紹了解決IDEA刪除子模塊并重建后MAVEN無法識別的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Servlet3.0實現(xiàn)文件上傳的方法

    Servlet3.0實現(xiàn)文件上傳的方法

    本篇文章主要介紹了Servlet實現(xiàn)文件上傳的方法,所謂文件上傳就是將本地的文件發(fā)送到服務(wù)器中保存。有興趣的可以了解一下。
    2017-03-03
  • 接口簽名怎么用Java實現(xiàn)

    接口簽名怎么用Java實現(xiàn)

    今天帶大家學(xué)習(xí)java的相關(guān)知識,文章圍繞怎么用Java實現(xiàn)接口簽名展開,文中有非常詳細(xì)的代碼示例及介紹,需要的朋友可以參考下
    2021-06-06
  • 淺談關(guān)于Mybatis的mapper-locations配置問題

    淺談關(guān)于Mybatis的mapper-locations配置問題

    MyBatis 是一款優(yōu)秀的半自動的ORM持久層框架,它支持自定義 SQL、存儲過程以及高級映射。MyBatis 免除了幾乎所有的 JDBC 代碼以及設(shè)置參數(shù)和獲取結(jié)果集的工作,需要的朋友可以參考下
    2023-05-05
  • 淺析Spring boot 中 logback 配置<springProperty> 讀取application.properties 中的屬性

    淺析Spring boot 中 logback 配置<springPropert

    這篇文章主要介紹了淺析Spring boot 中 logback 配置<springProperty> 讀取application.properties 中的屬性,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-02-02
  • java實現(xiàn)python session功能代碼實例

    java實現(xiàn)python session功能代碼實例

    這篇文章主要介紹了java實現(xiàn)python session功能代碼實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • 使用eclipse 實現(xiàn)將springboot項目打成jar包

    使用eclipse 實現(xiàn)將springboot項目打成jar包

    這篇文章主要介紹了使用eclipse 實現(xiàn)將springboot項目打成jar包的流程,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Redis作為緩存應(yīng)用的情形詳細(xì)分析

    Redis作為緩存應(yīng)用的情形詳細(xì)分析

    實際開發(fā)中緩存處理是必須的,不可能我們每次客戶端去請求一次服務(wù)器,服務(wù)器每次都要去數(shù)據(jù)庫中進(jìn)行查找,為什么要使用緩存?說到底是為了提高系統(tǒng)的運行速度
    2023-01-01
  • MyBatis實現(xiàn)簡單的數(shù)據(jù)表分月存儲

    MyBatis實現(xiàn)簡單的數(shù)據(jù)表分月存儲

    本文主要介紹了MyBatis實現(xiàn)簡單的數(shù)據(jù)表分月存儲,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03

最新評論