Java Socket编程实现群聊实践案例

 更新时间:2025年12月18日 14:40:30   作者:ilmoon05  
本文介绍了如何实现特定客户端与特定客户端之间的私聊和群聊功能,通过服务器端的多线程处理和客户端的Socket连接,实现了消息的路由和转发,本文给大家介绍Java Socket编程实现群聊功能,感兴趣的朋友跟随小编一起看看吧

上一篇文章已经可以实现服务端与客户端之间消息的交换并且开启多个客户端,那么如何实现特定客户端与特定客户端之间私聊以及特定客户端发给所有在线客户端的群聊呢,这一篇文章一起学习一下吧

代码详细解析

1.Mysever类-服务器主程序

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
/**1.创建服务器 绑定端口号
 * 2.在循环中 调用accept方法 重复监听连接过来的客户端
 * 3.启动线程保持和多个客户端连接
 * 4.在线程中 传进去对应的socket 获取输入输出流 实现和客户端的双向通讯
 * 5.创建客户端对象 绑定服务器的IP地址和端口 让客户端去连接服务器
 * 6.利用客户端输入输出流 跟服务端通讯
 * 7.在客户端启动线程 一直读取服务器发来的消息
 *
 */
public class MyServer {
    public static void main(String[] args){
        try {
            new MyServer().startServer();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    //启动服务器
    public void startServer() throws IOException {
        //创建服务器对象,并绑定监听器端口
        //端口取值范围(2字节):0~65535
        //知名端口:0~1024
        ServerSocket server=new ServerSocket(8888);
        System.out.println("服务端在8888端口监听器");
        //哈希表:保存一组映射关系:每个编号对应唯一的socket
        //基本数据类型对应的包装类
        HashMap<Integer,Socket> mp=new HashMap<>();
        //每个链接过来的客户端ID
        int ID=1;
        //重复监听连接这个服务器的客户端
        while(true){
            //监听连接这个服务器的客户端
            //该方法的返回的socket用来客户端通信
            Socket socket=server.accept();
            //保存当前对应的数据
            mp.put(ID,socket);
            //利用线程保持和多个客户端连接
            ServerThread st=new ServerThread(socket,mp,ID);
            new Thread(st).start();
            ID++;
        }
    }
}

知识点解析说明:

1.端口号

ServerSocket(int port):创建绑定到特定端口的服务器套接字

端口范围:0~65535

知名端口:0~1024,被系统服务占用

2.hashMap的使用

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

key:Integer类型的客户端ID

values:Socket对象,代表与客户端的链接

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

3.多线程处理

new Thread(st).start();

为每个客户端连接创造独立线程

避免单个客户端阻塞整个服务器

实现真正的并发处理

2.ServerThread类-服务器线程处理

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);
        }
        //发送客户端上线消息
        sendMsg(ID+":客户端上线..",os);
    }
    @Override
    public void run() {
        while(true) {
            try {
                //msg消息内容:   目标用户:消息内容
                String msg = readMsg();
                //对msg进行解析:拆分
                // 字符串分割
                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) {
                            //不将消息发给自己
                            if(value != this.socket) {
                            System.out.println("值:" + value);
                            OutputStream output = value.getOutputStream();
                            sendMsg(msgArr[1], output);
                        }
                    }
                }else{
                    //根据目标用户(ID)从mp中找到对应的socket(聊天对象)
                    int id = Integer.parseInt(msgArr[0]);  //字符串数字转成int
                    Socket socket = mp.get(id);
                    //获取该对象的输出流,写入数据
                    OutputStream output = socket.getOutputStream();
                    //把聊天内容转发给目标用户(聊天对象)
                    sendMsg(msgArr[1], output);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //发送消息的方法
    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();
    }
}

知识点解释说明:

InputStream:从套接字读取消息(接收消息)

OutputStream:向套接字写入数据(发送消息)

私聊:

客户端1给客户端3发消息,读取客户端1消息,获得客户端3输出流 ,将消息发给客户端3

群聊:

客户端1将消息发送给除自己以外所有客户端,接收客户端1消息,获得其他所有客户端输出流,将消息发送给其他所有客户端

2.消息格式

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

split()分隔符

返回值:分割后的字符串数组

+操作符用于字符串连接,将数组第一个元素、冒号分隔符、第二个元素连接成一个新字符串

3.消息协议

群聊:g:消息内容

私聊:目标ID:消息内容

4.遍历HashMap中所有数据

因为本编程需要遍历套接字,查看原代码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()获取所有值的集合,适合只关心值的场景。

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){
                //读取服务器消息
                String msg=readMsg();
                System.out.println(msg);
            }
        }).start();
        //发消息
        Scanner scanner=new Scanner(System.in);
        while(true){
            System.out.println("client:");
            String clientMsg=scanner.nextLine();
            sendMsg(clientMsg);
        }
    }
    //发送消息的方法
    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();
    }
}

知识点解释说明:

1.Socket客户端连接

127.0.0.1:本地回环地址,用于本地测试

8888:服务端监听端口

2.多线程消息处理

接收线程:

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

· 持续监听服务器发来的消息

发送消息:

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

Scanner用于读取控制台输入
nextLine()是阻塞方法,等待用户输入

补充:

服务端为什么使用线程:服务端要保持与多个客户端通信,每个客户端都需要一个单独的线程来控制通信,不然没有办法进行消息的收发

客户端为什么使用线程:读消息是一个阻塞方法,如果读消息发消息没有单独的线程去控制,他就只能按顺序收发,无法连续发消息 ,想要连续发消息,就没有办法控制,必须要有单独的线程去控制读和写

4.使用指南

1.启动服务器

2.启动客户端(可启动多个)

3.消息发送格式

群发消息:g:hello

私聊消息:3:nihao

测试:

群聊

客户端1输入:g;hello

客户端2显示:hello

客户端3显示:hello

私聊

客户端1输入:3:nihao

客户端2显示:无显示

客户端3显示:nihao

总结

这个Java聊天室项目展示了Socket编程的核心概念,包括服务器监听、客户端连接、多线程处理和消息路由。虽然功能相对基础,但架构清晰,为扩展更复杂的功能提供了良好的基础。

通过这个项目,可以深入理解网络编程、多线程同步和客户端-服务器架构的设计模式,是学习Java网络编程的优秀实践案例。

后续可以再进行扩展升级,例如添加注册登录及聊天界面。

到此这篇关于Java Socket编程实现群聊的文章就介绍到这了,更多相关Java Socket群聊内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Boot 自动参数校验的实现步骤

    Spring Boot 自动参数校验的实现步骤

    在 Spring Boot中实现参数自动校验主要依靠 Java Bean Validation API(JSR 380)和 Spring 的集成支持,下面给大家介绍Spring Boot 自动参数校验的实现步骤,感兴趣的朋友一起看看吧
    2025-06-06
  • java实现利用String类的简单方法读取xml文件中某个标签中的内容

    java实现利用String类的简单方法读取xml文件中某个标签中的内容

    下面小编就为大家带来一篇java实现利用String类的简单方法读取xml文件中某个标签中的内容。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • SpringBoot 实现定时任务的方法详解

    SpringBoot 实现定时任务的方法详解

    这篇文章主要介绍了SpringBoot 实现定时任务的方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • Java如何实现图像的卷积效果

    Java如何实现图像的卷积效果

    这篇文章主要介绍了Java如何实现图像的卷积效果问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • JavaGUI实现随机单词答题游戏

    JavaGUI实现随机单词答题游戏

    这篇文章主要为大家详细介绍了JavaGUI实现随机单词答题游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-12-12
  • 教你Spring Cloud保证各个微服务之间调用安全性

    教你Spring Cloud保证各个微服务之间调用安全性

    在微服务的架构下,系统会根据业务拆分为多个服务,各自负责单一的职责,在这样的架构下,我们需要确保各api的安全性,今天通过本文给大家分享Spring Cloud中如何保证各个微服务之间调用的安全性,需要的朋友参考下吧
    2021-08-08
  • 关于feign.codec.DecodeException异常的解决方案

    关于feign.codec.DecodeException异常的解决方案

    这篇文章主要介绍了关于feign.codec.DecodeException异常的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • java读写excel文件实现POI解析Excel的方法

    java读写excel文件实现POI解析Excel的方法

    在日常工作中,我们常常会进行Excel文件读写操作,这篇文章主要介绍了java读写excel文件实现POI解析Excel的方法,实例分析了java读写excel的技巧,非常具有实用价值,需要的朋友可以参考下
    2018-10-10
  • mybatis执行错误但sql执行正常问题

    mybatis执行错误但sql执行正常问题

    这篇文章主要介绍了mybatis执行错误但sql执行正常问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • SpringCloud @RefreshScope刷新机制深入探究

    SpringCloud @RefreshScope刷新机制深入探究

    RefeshScope这个注解想必大家都用过,在微服务配置中心的场景下经常出现,他可以用来刷新Bean中的属性配置,那大家对他的实现原理了解吗?它为什么可以做到动态刷新呢
    2023-03-03

最新评论