Springboot整合WebScoket实现在线聊天室

SpringBoot+WebScoket在线聊天室

提示:本次作者使用的环境是Windows10+jdk1.8+maven3.6.1 + idea2020.03
注:本版本只是一个简易版本的聊天室,更多功能可以自己自己去扩充,新手上路,老鸟勿喷,欢迎所有热爱java的朋友加作者wx



第一步:创建SpringBoot项目导入maven依赖

代码如下(示例):

   		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- SpringBoot框架websocket起步依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <!-- SpringBoot整合thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!-- lombok依赖 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

第二步:配置文件

代码如下(示例):

server:
  port: 8989
spring:
  thymeleaf:
    mode: HTML
    encoding: UTF-8
    prefix: classpath:/templates/  # 静态页面所在的路径,一般在resources文件加下创建
    suffix: .html
    cache: false   #关闭thymeleaf缓存

第二步:创建index.html页面(templates下)

代码如下(示例):

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <title>SpringBoot + WebSocket + html</title>
    <link rel="stylesheet" href="/css/bootstrap.min.css">
    <script src="/js/bootstrap.min.js"></script>
    <script src="/js/Jquery.js"></script>
</head>
<body style="margin: 45px">

<h4>小王在线聊天室</h4>
<div class="form-group">
    <label for="content"></label>
    <textarea id="content" readonly="readonly" cols="80" rows="15"></textarea>
</div>

<div class="form-group" style="margin-top: 8px">
    <textarea id="message" cols="80" rows="5" placeholder="请输入消息"></textarea>
    <div style="margin-top: 10px">
        <button id="toSend" class="btn-info">发送</button>
        <button id="toExit" class="btn-danger">离线</button>
        <input id="username" th:value="${username}" style="display: none">
    </div>
</div>

<script type="text/javascript">
    $(function () {
        var ws;
        //如果浏览器支持WebSocket
        if ("WebSocket" in window) {
            var baseUrl = 'ws://localhost:8989//websocket/';
            var username = $('#username').val();
            ws = new WebSocket(baseUrl + username);
            debugger;
            //建立连接之后,触发事件
            ws.onopen = function () {
                console.log("建立 websocket 连接......");
            };

            //接收后台服务端的消息,触发事件
            ws.onmessage = function (event) {
                $('#content').append(event.data + '\n\n');
                console.log("接收到服务端发送的消息......" + event.data + '\n');
            };

            //连接关闭时,触发事件
            ws.onclose = function () {
                $('#content').append('[' + username + ']已离线');
                console.log("关闭 websocket 连接......");
            };

            //发生错误时,触发事件
            ws.onerror = function (event) {
                console.log("websocket发生错误......" + event + '\n');
            };
        } else { //如果浏览器不支持WebSocket
            alert("很抱歉,您的浏览器不支持WebSocket!!!");
        }

        //发送按钮触发的行为,客户端发送消息到服务器
        $('#toSend').click(function () {
            sendMsg();
        });
        //支持回车键发送消息
        $(document).keyup(function (event) {
            if (event.keyCode == 13) {
                sendMsg();
            }
        });

        //发送消息的函数
        function sendMsg() {
            ws.send($('#message').val());
            $('#message').val("");
        }

        //离线按钮触发的行为
        $('#toExit').click(function () {
            if (ws) {
                ws.close();
            }
        })
    })
</script>
</body>
</html>

第三步:创建Conteoller页面跳转

代码如下(示例):

/**
 * ClassName ChatController
 * Description
 * @Author: xiaowang
 * @Date 2021/6/22 10:45
 */
@Controller
public class ChatController {
    //声明原子变量类,确保服务端和客户端之间操作的原子性和可见性
    private AtomicInteger atomicInteger = new AtomicInteger();
    @RequestMapping("/chat")
    public String toChat(Model model) {
        model.addAttribute("username", "user:" + atomicInteger.getAndIncrement());
        return "index";
    }

第四步:创建消息发送工具类

/**
 * ClassName ChatUtils
 * Description
 * @Author: xiaowang
 * @Date 2021/6/22 11:44
 */
@Slf4j
public class ChatUtils {
    //定义map集合,确保数据共享和安全,这里使用ConcurrentHashMap
    //用户名为key,session信息为value
    public static final Map<String, Session> CLIENTS = new ConcurrentHashMap<>();

    /**
     * 使用连接发送消息
     * @param session 用户的session
     * @param message 发送的消息内容
     */
    public static void sendMessage(Session session, String message) {
        if (session == null) {
            return;
        }
        final RemoteEndpoint.Basic basic = session.getBasicRemote();
        if (basic == null) {
            return;
        }
        try {
            basic.sendText(message);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("sendMessage IOException", e);
        }
    }
    /**
     * 发送消息给所有人
     * @param message
     */
    public static void sendMessageAll(String message) {
        CLIENTS.forEach((sessionId, session) -> sendMessage(session, message));
    }
    /**
     * 获取所有的在线用户
     */
    public static String getOnlineInfo() {
        Set<String> userNames = CLIENTS.keySet();
        if (userNames.size() == 0) {
            return "当前无人在线......";
        }
        return userNames.toString() + "在线";
    }
}

第五步:创建WebScoket相关事件监听

/**
 * ClassName ChatServerEndpoint
 * Description
 * @Author: xiaowang
 * @Date 2021/6/22 11:46
 */
@Slf4j
@Component
@ServerEndpoint("/websocket/{username}")
@SuppressWarnings("all")
public class ChatServerEndpoint {
    /**
     * 连接建立时触发
     */
    @OnOpen
    public void onOpen(@PathParam("username") String username, Session session) {
        log.info("用户{}登录", username);
        String message = "用户[" + username + "]已进入聊天室!";
        //将该用户登录的消息发送给其他人
        ChatUtils.sendMessageAll(message);
        //将自己的信息添加到map集合中
        ChatUtils.CLIENTS.put(username, session);
        //获取当前的在线人数,发给自己查看
        String onlineInfo = ChatUtils.getOnlineInfo();
        ChatUtils.sendMessage(session, onlineInfo);
    }
    /**
     * 客户端接收服务端发来的数据时触发
     */
    @OnMessage
    public void onMessage(@PathParam("username") String username, String message) {
        log.info("发送消息:{}, {}", username, message);
        //广播,把消息同步给其他客户端
        ChatUtils.sendMessageAll("[" + username + "]: " + message);
    }
    /**
     * 连接关闭时触发
     */
    @OnClose
    public void onClose(@PathParam("username") String username, Session session) {
        //从当前的map集合中移除该用户
        ChatUtils.CLIENTS.remove(username);
        //将该用户离线的消息通知给其他人
        ChatUtils.sendMessageAll("[" + username + "]已离线!");
        try {
            //关闭WebSocket下的该Seesion会话
            session.close();
            log.info("{} 已离线......", username);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("onClose error", e);
        }
    }
    /**
     * 聊天通信发生错误时触发
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        try {
            //关闭WebSocket下的该Seesion会话
            session.close();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("onError Exception", e);
        }
        log.info("Throwable msg " + throwable.getMessage());
    }
}

第六步:创建配置类

/**
 * ClassName ScoketConfig
 * Description
 * @Author: xiaowang
 * @Date 2021/6/22 12:26
 */
@Configuration
public class WebScoketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

第七步:启动测试

在这里插入图片描述

在这里插入图片描述

以上用多个浏览器打测试就能建立通讯了

最终:项目结构图

在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小王979578444

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

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

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

打赏作者

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

抵扣说明:

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

余额充值