最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java Socket編程實(shí)現(xiàn)群聊實(shí)踐案例

 更新時間:2025年12月18日 14:40:30   作者:ilmoon05  
本文介紹了如何實(shí)現(xiàn)特定客戶端與特定客戶端之間的私聊和群聊功能,通過服務(wù)器端的多線程處理和客戶端的Socket連接,實(shí)現(xiàn)了消息的路由和轉(zhuǎn)發(fā),本文給大家介紹Java Socket編程實(shí)現(xiàn)群聊功能,感興趣的朋友跟隨小編一起看看吧

上一篇文章已經(jīng)可以實(shí)現(xiàn)服務(wù)端與客戶端之間消息的交換并且開啟多個客戶端,那么如何實(shí)現(xiàn)特定客戶端與特定客戶端之間私聊以及特定客戶端發(fā)給所有在線客戶端的群聊呢,這一篇文章一起學(xué)習(xí)一下吧

代碼詳細(xì)解析

1.Mysever類-服務(wù)器主程序

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
/**1.創(chuàng)建服務(wù)器 綁定端口號
 * 2.在循環(huán)中 調(diào)用accept方法 重復(fù)監(jiān)聽連接過來的客戶端
 * 3.啟動線程保持和多個客戶端連接
 * 4.在線程中 傳進(jìn)去對應(yīng)的socket 獲取輸入輸出流 實(shí)現(xiàn)和客戶端的雙向通訊
 * 5.創(chuàng)建客戶端對象 綁定服務(wù)器的IP地址和端口 讓客戶端去連接服務(wù)器
 * 6.利用客戶端輸入輸出流 跟服務(wù)端通訊
 * 7.在客戶端啟動線程 一直讀取服務(wù)器發(fā)來的消息
 *
 */
public class MyServer {
    public static void main(String[] args){
        try {
            new MyServer().startServer();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    //啟動服務(wù)器
    public void startServer() throws IOException {
        //創(chuàng)建服務(wù)器對象,并綁定監(jiān)聽器端口
        //端口取值范圍(2字節(jié)):0~65535
        //知名端口:0~1024
        ServerSocket server=new ServerSocket(8888);
        System.out.println("服務(wù)端在8888端口監(jiān)聽器");
        //哈希表:保存一組映射關(guān)系:每個編號對應(yīng)唯一的socket
        //基本數(shù)據(jù)類型對應(yīng)的包裝類
        HashMap<Integer,Socket> mp=new HashMap<>();
        //每個鏈接過來的客戶端ID
        int ID=1;
        //重復(fù)監(jiān)聽連接這個服務(wù)器的客戶端
        while(true){
            //監(jiān)聽連接這個服務(wù)器的客戶端
            //該方法的返回的socket用來客戶端通信
            Socket socket=server.accept();
            //保存當(dāng)前對應(yīng)的數(shù)據(jù)
            mp.put(ID,socket);
            //利用線程保持和多個客戶端連接
            ServerThread st=new ServerThread(socket,mp,ID);
            new Thread(st).start();
            ID++;
        }
    }
}

知識點(diǎn)解析說明:

1.端口號

ServerSocket(int port):創(chuàng)建綁定到特定端口的服務(wù)器套接字

端口范圍:0~65535

知名端口:0~1024,被系統(tǒng)服務(wù)占用

2.hashMap的使用

 HashMap<Integer,Socket> mp=new HashMap<>();

key:Integer類型的客戶端ID

values:Socket對象,代表與客戶端的鏈接

作用:集中管理所有客戶端鏈接,便于消息路由

3.多線程處理

new Thread(st).start();

為每個客戶端連接創(chuàng)造獨(dú)立線程

避免單個客戶端阻塞整個服務(wù)器

實(shí)現(xiàn)真正的并發(fā)處理

2.ServerThread類-服務(wù)器線程處理

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Collection;
import java.util.HashMap;
public class ServerThread implements Runnable{
    //跟客戶端連接的套接字
    public Socket socket;
    public InputStream is;
    public OutputStream os;
    public HashMap<Integer,Socket> mp;
    public int ID;
    public ServerThread(Socket socket,HashMap<Integer,Socket> mp,int ID){
        this.socket=socket;
        this.mp=mp;
        try {
            os = socket.getOutputStream();
            is = socket.getInputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //發(fā)送客戶端上線消息
        sendMsg(ID+":客戶端上線..",os);
    }
    @Override
    public void run() {
        while(true) {
            try {
                //msg消息內(nèi)容:   目標(biāo)用戶:消息內(nèi)容
                String msg = readMsg();
                //對msg進(jìn)行解析:拆分
                // 字符串分割
                String[] msgArr = msg.split(":");
                System.out.println(msgArr[0] + ":" + msgArr[1]);
                if(msgArr[0].equals("g")){
                    Collection<Socket> values=mp.values();
                        for (Socket value : values) {
                            //不將消息發(fā)給自己
                            if(value != this.socket) {
                            System.out.println("值:" + value);
                            OutputStream output = value.getOutputStream();
                            sendMsg(msgArr[1], output);
                        }
                    }
                }else{
                    //根據(jù)目標(biāo)用戶(ID)從mp中找到對應(yīng)的socket(聊天對象)
                    int id = Integer.parseInt(msgArr[0]);  //字符串?dāng)?shù)字轉(zhuǎn)成int
                    Socket socket = mp.get(id);
                    //獲取該對象的輸出流,寫入數(shù)據(jù)
                    OutputStream output = socket.getOutputStream();
                    //把聊天內(nèi)容轉(zhuǎn)發(fā)給目標(biāo)用戶(聊天對象)
                    sendMsg(msgArr[1], output);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //發(fā)送消息的方法
    public void sendMsg(String msg,OutputStream os){
        try {
            byte[] b = msg.getBytes();
            os.write(b);
            os.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    //讀取消息的方法
    public String readMsg(){
        byte[] b = new byte[1024];  //最多讀取的消息長度
        try {
            is.read(b);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        // trim() 去掉字符尾部空格
        String s=new String(b);
        return s.trim();
    }
}

知識點(diǎn)解釋說明:

InputStream:從套接字讀取消息(接收消息)

OutputStream:向套接字寫入數(shù)據(jù)(發(fā)送消息)

私聊:

客戶端1給客戶端3發(fā)消息,讀取客戶端1消息,獲得客戶端3輸出流 ,將消息發(fā)給客戶端3

群聊:

客戶端1將消息發(fā)送給除自己以外所有客戶端,接收客戶端1消息,獲得其他所有客戶端輸出流,將消息發(fā)送給其他所有客戶端

2.消息格式

String[] msgArr = msg.split(":");
                System.out.println(msgArr[0] + ":" + msgArr[1]);

split()分隔符

返回值:分割后的字符串?dāng)?shù)組

+操作符用于字符串連接,將數(shù)組第一個元素、冒號分隔符、第二個元素連接成一個新字符串

3.消息協(xié)議

群聊:g:消息內(nèi)容

私聊:目標(biāo)ID:消息內(nèi)容

4.遍歷HashMap中所有數(shù)據(jù)

因?yàn)楸揪幊绦枰闅v套接字,查看原代碼socket是values,所以用values方法遍歷

代碼示例:

import java.util.Collection;
import java.util.HashMap;
public class HashMapTraversal {
    public static void main(String[] args) {
        HashMap<String, Integer> map = new HashMap<>();
        map.put("A", 1);
        map.put("B", 2);
        map.put("C", 3);
        Collection<Integer> values = map.values();
        for (Integer value : values) {
            System.out.println("Value: " + value);
        }
    }
}

通過values()獲取所有值的集合,適合只關(guān)心值的場景。

3.Client類-客戶端程序

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
    public static void main(String[] args){
        try {
            new Client().startClient();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public InputStream is;
    public OutputStream os;
    //啟動客戶端
    public void startClient() throws IOException{
        Socket client =new Socket("127.0.0.1",8888);
        os=client.getOutputStream();
        is=client.getInputStream();
        new Thread(()->{
            while(true){
                //讀取服務(wù)器消息
                String msg=readMsg();
                System.out.println(msg);
            }
        }).start();
        //發(fā)消息
        Scanner scanner=new Scanner(System.in);
        while(true){
            System.out.println("client:");
            String clientMsg=scanner.nextLine();
            sendMsg(clientMsg);
        }
    }
    //發(fā)送消息的方法
    public void sendMsg(String msg){
        try {
            os.write(msg.getBytes());
            os.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public String readMsg(){
        byte[] b=new byte[1024];
        try {
            //阻塞方法:一直等待讀取消息,如果沒有會一直等著
            is.read(b);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //trim
        return new String(b).trim();
    }
}

知識點(diǎn)解釋說明:

1.Socket客戶端連接

127.0.0.1:本地回環(huán)地址,用于本地測試

8888:服務(wù)端監(jiān)聽端口

2.多線程消息處理

接收線程:

        new Thread(()->{
            while(true){
                //讀取服務(wù)器消息
                String msg=readMsg();
                System.out.println(msg);
            }
        }).start();

· 持續(xù)監(jiān)聽服務(wù)器發(fā)來的消息

發(fā)送消息:

 Scanner scanner=new Scanner(System.in);
        while(true){
            System.out.println("client:");
            String clientMsg=scanner.nextLine();
            sendMsg(clientMsg);
        }
    }

Scanner用于讀取控制臺輸入
nextLine()是阻塞方法,等待用戶輸入

補(bǔ)充:

服務(wù)端為什么使用線程:服務(wù)端要保持與多個客戶端通信,每個客戶端都需要一個單獨(dú)的線程來控制通信,不然沒有辦法進(jìn)行消息的收發(fā)

客戶端為什么使用線程:讀消息是一個阻塞方法,如果讀消息發(fā)消息沒有單獨(dú)的線程去控制,他就只能按順序收發(fā),無法連續(xù)發(fā)消息 ,想要連續(xù)發(fā)消息,就沒有辦法控制,必須要有單獨(dú)的線程去控制讀和寫

4.使用指南

1.啟動服務(wù)器

2.啟動客戶端(可啟動多個)

3.消息發(fā)送格式

群發(fā)消息:g:hello

私聊消息:3:nihao

測試:

群聊

客戶端1輸入:g;hello

客戶端2顯示:hello

客戶端3顯示:hello

私聊

客戶端1輸入:3:nihao

客戶端2顯示:無顯示

客戶端3顯示:nihao

總結(jié)

這個Java聊天室項(xiàng)目展示了Socket編程的核心概念,包括服務(wù)器監(jiān)聽、客戶端連接、多線程處理和消息路由。雖然功能相對基礎(chǔ),但架構(gòu)清晰,為擴(kuò)展更復(fù)雜的功能提供了良好的基礎(chǔ)。

通過這個項(xiàng)目,可以深入理解網(wǎng)絡(luò)編程、多線程同步和客戶端-服務(wù)器架構(gòu)的設(shè)計模式,是學(xué)習(xí)Java網(wǎng)絡(luò)編程的優(yōu)秀實(shí)踐案例。

后續(xù)可以再進(jìn)行擴(kuò)展升級,例如添加注冊登錄及聊天界面。

到此這篇關(guān)于Java Socket編程實(shí)現(xiàn)群聊的文章就介紹到這了,更多相關(guān)Java Socket群聊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java簡單模擬實(shí)現(xiàn)一個線程池

    Java簡單模擬實(shí)現(xiàn)一個線程池

    本文主要介紹了Java簡單模擬實(shí)現(xiàn)一個線程池,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-01-01
  • SpringBoot整合mybatis-plus實(shí)現(xiàn)分頁查詢功能

    SpringBoot整合mybatis-plus實(shí)現(xiàn)分頁查詢功能

    這篇文章主要介紹了SpringBoot整合mybatis-plus實(shí)現(xiàn)分頁查詢功能,pringBoot分頁查詢的兩種寫法,一種是手動實(shí)現(xiàn),另一種是使用框架實(shí)現(xiàn),現(xiàn)在我將具體的實(shí)現(xiàn)流程分享一下,需要的朋友可以參考下
    2023-11-11
  • Dubbo新版本zk注冊中心連接問題及解決

    Dubbo新版本zk注冊中心連接問題及解決

    這篇文章主要介紹了Dubbo新版本zk注冊中心連接問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • 使用Spring由構(gòu)造方法自動裝配

    使用Spring由構(gòu)造方法自動裝配

    這篇文章主要介紹了使用Spring由構(gòu)造方法自動裝配,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Spring Data JPA踩坑記錄(@id @GeneratedValue)

    Spring Data JPA踩坑記錄(@id @GeneratedValue)

    這篇文章主要介紹了Spring Data JPA踩坑記錄(@id @GeneratedValue),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • 如何使用Spring Validation優(yōu)雅地校驗(yàn)參數(shù)

    如何使用Spring Validation優(yōu)雅地校驗(yàn)參數(shù)

    這篇文章主要介紹了如何使用Spring Validation優(yōu)雅地校驗(yàn)參數(shù),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-07-07
  • Java中創(chuàng)建線程池的幾種方式以及區(qū)別

    Java中創(chuàng)建線程池的幾種方式以及區(qū)別

    創(chuàng)建線程池有多種方式,主要通過 Java 的 java.util.concurrent 包提供的 Executors 工具類來實(shí)現(xiàn),本文給大家介紹了幾種常見的線程池類型及其區(qū)別,并通過代碼示例講解的非常詳細(xì),需要的朋友可以參考下
    2024-11-11
  • SpringBoot3集成Zookeeper的代碼詳解

    SpringBoot3集成Zookeeper的代碼詳解

    ZooKeeper是一個集中的服務(wù),用于維護(hù)配置信息、命名、提供分布式同步、提供組服務(wù),分布式應(yīng)用程序以某種形式使用所有這些類型的服務(wù),本文將給大家介紹SpringBoot3集成Zookeeper的代碼,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2024-01-01
  • 將Sublime Text 2配置為Java的IDE的教程

    將Sublime Text 2配置為Java的IDE的教程

    這篇文章主要介紹了將Sublime Text 2配置為Java的IDE的教程,包括能讓Sublime這個文本編輯器編譯和運(yùn)行Java程序等,需要的朋友可以參考下
    2015-07-07
  • Java正則表達(dá)式的替換和分組功能

    Java正則表達(dá)式的替換和分組功能

    這篇文章主要給大家介紹了關(guān)于Java正則表達(dá)式的替換和分組功能的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09

最新評論

乌兰浩特市| 常熟市| 云梦县| 商南县| 都兰县| 龙州县| 通江县| 亚东县| 江西省| 兴文县| 麟游县| 临猗县| 新干县| 东宁县| 新闻| 武乡县| 尚志市| 新余市| 万山特区| 山阳县| 上蔡县| 荆州市| 高要市| 台前县| 池州市| 天津市| 隆安县| 广西| 西贡区| 曲靖市| 永定县| 得荣县| 扶绥县| 峨山| 岑巩县| 申扎县| 汕尾市| 景谷| 老河口市| 陕西省| 若羌县|