基于Springboot整合Socket仿微信实现群聊、私聊功能。实现客户端client,服务端server心跳维护、超时机制【一文通】

博主介绍:✌java资深开发工程师、Java领域优质创作者,博客之星、专注于Java技术领域和学生毕业项目实战,面试讲解跟进,高校老师/讲师/同行交流合作✌

胡广愿景:

"比特星球",致力于帮助底层人员找到工作,让每个底层人员都能找到属于自己的星球。

拓展学习领域,获取社会知识,让你更好地面对职业挑战。与此同时,我们将实时关注社会热点,分享最新科技动态,激励你不断进步。加入比特星球,共同构建一个互助的学习社区。

👇🏻 感兴趣的可以先收藏起来👇🏻 不然下次找不到哟

大家在毕设选题,项目以及论文编写、就业面试等相关问题都可以给我留言咨询,希望帮助更多的人

🍅文末获取源码联系🍅

🍅文末获取源码联系🍅

🍅文末获取源码联系🍅

 大家好,我叫胡广,废话不多说咱们直接进入正题!!!

socket科普

参考《Socket通信原理》https://www.cnblogs.com/wangcq/p/3520400.html

整体流程图

我们制作的这个实时在线聊天系统分为客户端、服务端,

两个步骤:

1、连接注册

客户端往服务端发起注册请求,服务端将客户端的地址、端口存入HashSet在内存中缓存起来

2、消息处理(聊天功能)

客户端发送消息至服务端,服务端处理消息格式私密消息格式为 @targetUser(私密用户名) 消息内容。如果不为此格式那么就向除自己以外的用户都发送消息

整个的实现原理用一张图即可解释

相比你看到了此文章这里,基本有个大概的了解了吧

接下来就到了你最期待的时候了,没错就是实现代码,哈哈哈,我不知道有多少人是为了代码过来的,可否给我点个免费的小赞和收藏呢,胡广拜谢了。

如果你也是底层程序员正在水生活热的生活中迷茫,感到就业困难,面试少,可以到此文章末尾的公众号联系到我哦,我帮你一起克服眼前的困难。《同是天涯沦落人,相逢何必曾相识》

实现代码

客户端代码

1.这里使用 Socket 类创建一个与指定服务器地址和端口的socket连接

2.通过 BufferedReaderPrintWriter 分别设置输入和输出流。reader 用于读取服务器的消息,writer 用于向服务器发送消息。consoleReader 用于读取用户在控制台输入的消息

3.使用一个线程来不断地从服务器接收消息,并将其显示在客户端的控制台上。

4.在主线程中,用户可以输入消息,程序将其发送到服务器。如果用户输入 "exit",则退出循环,关闭连接

5.异常处理部分用于捕捉可能发生的 IOException 异常,并打印异常信息。

package org.sqs.socketchat.client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

/**
 * 通信客户端ds
 */
public class ChatClient {
    public static void main(String[] args) {


        String serverHost = "127.0.0.1";
        int serverPort = 8080;

        try (Socket socket = new Socket(serverHost, serverPort);
             //字符流读取
             BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             //输出字符流
             PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
             //读取控制台么
             BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in))) {

            // 启动接收消息线程
            new Thread(() -> {
                try {
                    while (true) {
                        String message = reader.readLine();
                        if (message == null) {
                            break;
                        }
                        System.out.println(message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();

            // 发送用户输入的消息
            while (true) {
                String userInput = consoleReader.readLine();
                //退出链接
                if (userInput.equals("exit")) {
                    break;
                }
                //往服务端输出消息
                writer.println(userInput);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

服务端代码

消息处理器

1.当客户端发送消息时,服务端会检查消息是否以 "@" 开头,如果是,则认为是私密消息。私密消息格式为 @targetUser message。如果消息格式正确,服务端会将私密消息发送给目标用户,否则会通知发送者用户不存在。广播消息给所有用户的方法和之前相同。这只是一个简单的实现,实际场景中可能需要更复杂的逻辑和安全性保障。

package org.sqs.socketchat.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Set;

/**
 * 消息处理
 */
public class ChatHandler implements Runnable{
    //socket客户端
    private Socket clientSocket;
    //客户端集
    private Set<ChatHandler> handlers;
    //缓冲输入字符流
    private BufferedReader reader;
    //输出字符流
    private PrintWriter writer;
    //添加用户名字段
    private String username;

    /**
     * 消息处理
     * @param socket
     * @param handlers
     */
    public ChatHandler(Socket socket, Set<ChatHandler> handlers) {
        this.clientSocket = socket;
        this.handlers = handlers;

        try {
            //输入流与输出流
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            writer = new PrintWriter(socket.getOutputStream(), true);
            // 为每个客户端生成一个唯一的用户名(可根据实际需求修改)这里用的是当前时间,小伙伴可以用UUID来代替都行
            this.username = "User" + System.currentTimeMillis();
            sendMessage("您的客户端连接名为 " + username);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 获取用户名
    public String getUsername() {
        return username;
    }

    /**
     * 开启消息处理
     */
    @Override
    public void run() {
        try {
            while (true) {
                //读取消息
                String message = reader.readLine();
                //消息非空校验
                if (message == null) {
                    break;
                }

                // 解析私密消息格式(示例:@targetUser message)
                if (message.startsWith("@")) {
                    String[] parts = message.split(" ", 2);
                    if (parts.length == 2) {
                        ChatServer.sendPrivateMessage(parts[1], parts[0].substring(1), this);
                        continue;
                    }
                }

                // 广播消息给所有客户端
                ChatServer.broadcast(username + ": " + message, this);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 异常之后移除处理器
            handlers.remove(this);
            try {
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void sendMessage(String message) {
        writer.println(message);
    }
}

服务端的端口监听

1.监听客户端连接端口8080

2.将客户端信息存入内存缓存处理

3.提供群聊广播消息、私密消息功能

package org.sqs.socketchat.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
import java.util.Set;

/**
 * 通信服务端
 */
public class ChatServer {
    //定义端口号
    private static final int PORT = 8080;
    //定义聊天处理器,将每个客户端存入处理,用Set不会重复
    private static Set<ChatHandler> handlers = new HashSet<>();

    public static void main(String[] args) {
        //监听端口号
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("聊天服务端正在此端口上运行:" + PORT);

            //开始循环监听
            while (true) {
                //1.服务器调用 serverSocket.accept() 方法:该方法会一直阻塞,直到有客户端请求连接到服务器。
                //2.客户端发起连接请求: 当有客户端发起连接请求时,serverSocket.accept() 会返回一个新的 Socket 对象,该对象代表与客户端建立的连接。
                //3.创建新的 Socket 对象: 服务器通过 serverSocket.accept() 创建一个新的 Socket 对象,该对象包含了客户端的信息,包括客户端的地址和端口等。
                //4.处理客户端连接: 服务器可以使用返回的 Socket 对象与客户端进行通信,发送和接收数据
                Socket clientSocket = serverSocket.accept();
                System.out.println("新的客户端已连接 " + clientSocket);

                //将此socket客户端连接存入Set当中
                ChatHandler handler = new ChatHandler(clientSocket, handlers);
                handlers.add(handler);

                //创一个新线程来处理此客户端的消息。建议:新线程可以用线程池来管理,固定1线程的线程池或者调整线程池策略
                Thread handlerThread = new Thread(handler);
                handlerThread.start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // Broadcast消息给所有客户端。广播
    public static void broadcast(String message, ChatHandler sender) {
        for (ChatHandler handler : handlers) {
            // 避免将消息发回给发送者
            if (handler != sender) {
                handler.sendMessage(message);
            }
        }
    }

    // 发送私密消息给指定客户端
    public static void sendPrivateMessage(String message, String targetClient, ChatHandler sender) {
        for (ChatHandler handler : handlers) {
            // 找到目标客户端并发送私密消息
            if (handler.getUsername().equals(targetClient)) {
                handler.sendMessage(sender.getUsername() + " (私密): " + message);
                return;
            }
        }

        // 如果目标客户端不存在,通知发送者
        sender.sendMessage("用户 '" + targetClient + "' 找不到.");
    }
}

以上就是所有的代码咯,当然还有标题中提到的心跳以及超时的机制解决方案,以下代码可供大家参考

心跳机制代码

弄一个Timer定时器,定时的向服务端发送消息来保持socket连接的活跃性

    /**
     * 往上机系统发送指令,接收响应
     *
     * @param message
     */
    public void sendMessage(String message) {
        try {
            log.info(message);
            if(null != out){
                out.println(message);
            }else{
                log.error("发送消息失败!!! Socket out 为null,请检查连接是否成功");
            }
            String response;
            if(null != in){
                response = in.readLine();
                log.info(response);
            }else{
                log.error("发送消息失败!!! Socket in 为null,请检查连接是否成功");
            }
        } catch (IOException e) {
                e.printStackTrace();
        } finally {
            try {
                closeConnection();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 定时心跳
     *
     * @param intervalSeconds
     */
    private void startHeartbeatTimer(int intervalSeconds) {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                sendMessage("客户端:我的心跳信息,俺还活着哦!\t" + sdf.format(new Date()));
            }
        }, 0, intervalSeconds * 1000L);
    }

超时机制代码

连接socket服务端时,捕获一个超时的异常就行

        try {
            socket = new Socket(serverHost, serverPort);
            //读写操作的超时时间
            socket.setSoTimeout(timeout);
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
        } catch (IOException e) {
            // 捕获连接超时异常
            if (e instanceof ConnectException) {
                log.error("初始化连接失败,请检查server的活跃性!!!");
            } else {
                // 处理其他异常
                e.printStackTrace();
            }
        } finally {
            try {
                closeConnection();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

效果图展示

 客户端1效果图:

 客户端2效果图:

客户端3效果图:

服务端效果图:

 源码+项目部署

数据库还有源码一起都打包再下边的地址里了,下载开箱即用,跟springboot一样,嘻嘻嘻!

嗯嗯嗯......终于到了激动人心的时候了,我来帮你搞定一切各种面试,技术问题,毕业设计,帝王般的服务你值得拥有,免费的哟。

各类源码扫描搜索公众号《与胡广一起探索比特星球》发送任何消息免费获取

各类源码扫描搜索公众号《与胡广一起探索比特星球》发送任何消息免费获取

各类源码扫描搜索公众号《与胡广一起探索比特星球》发送任何消息免费获取

最后附上

一寸光阴一寸金,寸金难买寸光阴。请珍惜现在美好的青春,咱们一起努力奋斗,创造美好未来

拜托拜托!!!拜托拜托!!!拜托拜托!!!

BIT PLANET 

BIT PLANET
  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡广的比特星球

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值