Springboot+WebSocket实现匿名聊天室

在这里插入图片描述

由于毕设的需要,想要做一个匿名聊天.

一、基本需求

  1. 一个人发送的消息其他人都能看的见,服务端无需保存消息,未上线的用户看不到历史消息
  2. 能够区分某条消息是自己还是别人发送的,因为自己发送的消息会用不同颜色显示。
  3. 能够显示在线的人数,且人数发生变化后能立马变化

二、简介

网络通信通常有三种方式:

  1. 单向通信:只能A跟B说话,B不能跟A说话
  2. 半双工通信:A能和B说话,B也能跟A说话,但A和B不能同时说话
  3. 全双工通信:A和B能够同时说话

传统的Http协议只能由客户端发起,无法做到服务器端主动向客户端推送消息。如果需要知晓服务器的信息,需要客户端维持轮询,很消耗性能。

WebSocket最显著的特点就是能够全双工通信,服务器端能够主动的向客户端推送消息.很适合这种聊天或者推送系统。

WebSocket 教程 - 阮一峰的网络日志
HTML5 WebSocket | 菜鸟教程

三、代码实现

1.后端代码实现
  1. 导入websocket依赖
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-websocket</artifactId>
       </dependency>
  1. 新建webSocket配置类WebSocketConfig
@Configuration
public class WebSocketConfig {
   @Bean
   public ServerEndpointExporter getServerEndpointExporter() {
       return new ServerEndpointExporter();
   }
}
  1. 新建WebSocketServer,对于ws请求进行处理,类似于Controller
@Component
@ServerEndpoint(Api.CHAT_ROOM + "/{name}/{uuid}")
@Slf4j
/**
 * 通过websocket实现匿名聊天室
 */
public class WebSocketServer {
    //通过AtomicInteger控制在线的人数,onOpen时count+1,onClose时count-1
    private static AtomicInteger count = new AtomicInteger(0);
    //因为每个客户端都会有对应的WebSocketServer,通过ConCurrentHashMap将客户端保存下来,区分客户端的方法是客户端上次昵称的时候还要带上一个uuid,map的key为uuid+name
    private static Map<String, WebSocketServer> servers = new ConcurrentHashMap();
    private Session session = null;
    private String name = null;
    //因为是匿名聊天,所以用户名无法区分用户,通过js生成的uuid来区分
    private String uuid = null;

    /**
     * 连接创建成功
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("name") String name, @PathParam("uuid") String uuid) {
        //log.info("创建连接");
        this.session = session;
        this.name = name;
        this.uuid = uuid;
        count.incrementAndGet();
        servers.put(name + uuid, this);
        try {
            JSONObject result = new JSONObject();
            result.put("count", count);
            //将你上线的消息发给所有的在线用户,不能就通过形参中的session发送,那样就只有你只知道你上线了,而要遍历所有的WebSocketServer,拿到每个用户的session,下面的发送消息和下线同理.
            if (uuid != null && servers.containsKey(name+uuid)) {
                for (WebSocketServer server : servers.values()) {
                    server.session.getBasicRemote().sendText(result.toString());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 连接关闭
     */
    @OnClose
    public void onClose() {
        count.decrementAndGet();
        servers.remove(name + uuid);
        if(servers.size()!=0){
            JSONObject result = new JSONObject();
            result.put("count", count);
            for (WebSocketServer server : servers.values()) {
                try {
                    server.session.getBasicRemote().sendText(result.toString());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 接受消息并通过session返回消息
     */
    @OnMessage
    public void onMessage(String message) throws IOException {
        //log.info("onmessage: "+message);
        JSONObject result = new JSONObject();
        result.put("time", TimeUtil.getTime());
        result.put("message", message);
        result.put("name", name);
        if (uuid != null && servers.containsKey(name+uuid)) {
            for (WebSocketServer server : servers.values()) {
                server.session.getBasicRemote().sendText(result.toString());
            }
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        try {
            JSONObject result = new JSONObject();
            result.put("count", count);
            session.getBasicRemote().sendText(result.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}
2.前端代码实现

前端与后端类似,只需要new WebSocket(url)对象,重写回调方法进行逻辑处理.

bootstrap、jquery、uuidv4.min.js自行下载

chat.html

<html>
<head>
    <link rel="shortcut icon" href="/common/images/favicon.ico" type="image/x-icon">
    <link rel="stylesheet" href="/user/css/bootstrap.min.css">
    <link rel="stylesheet" href="/user/css/chat.css">
    <title>上海电机学院BBS-电机人自己的BBS</title>
</head>
<body>
<div class="chat">
    <div class="panel panel-default">
        <div class="panel-heading" id="count"></div>
        <div id="div_msg" class="panel-body" style="width: 500px; height: 630px;">
            <div id="message-list" style="margin-bottom:10px;position:relative;left:0px;">
            </div>
        </div>
        <div class="panel-heading">
            <textarea id="message" class="form-control" style="height:100px;resize:none;" placeholder="发送的内容"></textarea>
            <button id="btn_send" type="button" style="width: 100%; margin-top: 5px;" class="btn btn-info">发送</button>
        </div>
    </div>
</div>
<script src="/common/js/jquery-3.1.1.js"></script>
<script src="/common/js/bootstrap.min.js"></script>
<script src="/common/js/uuidv4.min.js"></script>
<script src="/user/js/chat.js"></script>
</body>
</html>

chat.css

.chat {
    width: 500px;
    height: 700px;
    margin: 50 auto;
}

chat.js

let URL = "ws:localhost:8080";
$(function () {
    let name = prompt("请输入您的昵称");
    if (name == null || name == "") {
        return;
    }
    //通过uuid区分不同客户端
    let socket = new WebSocket(URL + "/api/chat/" + name + "/" + uuidv4());
    socket.onopen = function () {

    };
    socket.onmessage = function (msg) {
        var result = JSON.parse(msg.data);
        if (null != result.count) {
            $("#count").empty().append("在线人数: " + result.count);
        }
        if (null != result.message && null != result.time && null != result.name) {
            //<div style="color:green">你 16:37:33</div>等待服务器Websocket握手包...-->
            var section = $("<div></div>").css("margin-bottom", "10px");
            $("#message-list").append(section);
            var element = $("<div></div>");
            element.css("color", name == result.name ? "green" : "blue").empty().append(result.name + "&nbsp;&nbsp;" + result.time);
            section.append(element);
            element.after(result.message);
            console.log(result.message)
        }

    };
    socket.onclose = function () {
    };
    socket.onerror = function () {
    }

    $("#btn_send").click(function () {
        socket.send($("#message").val());
        $("#message").val('');
    });
});
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是一个简单的使用Spring Boot、Vue.js和WebSocket实现聊天的代码示例: Spring Boot后端代码: ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new WebSocketHandler(), "/chat").setAllowedOrigins("*"); } @Bean public ObjectMapper objectMapper() { return new ObjectMapper(); } } class WebSocketHandler extends TextWebSocketHandler { private static final Map<WebSocketSession, String> users = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) { users.put(session, "Anonymous"); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { ChatMessage chatMessage = new ObjectMapper().readValue(message.getPayload(), ChatMessage.class); if (chatMessage.getType() == ChatMessage.MessageType.JOIN) { users.put(session, chatMessage.getSender()); } for (WebSocketSession user : users.keySet()) { user.sendMessage(new TextMessage(new ObjectMapper().writeValueAsString(chatMessage))); } } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { users.remove(session); } } @Data @AllArgsConstructor @NoArgsConstructor class ChatMessage { public enum MessageType { CHAT, JOIN, LEAVE } private String sender; private String content; private MessageType type; public static ChatMessage joinMessage(String sender) { return new ChatMessage(sender, "", MessageType.JOIN); } public static ChatMessage leaveMessage(String sender) { return new ChatMessage(sender, "", MessageType.LEAVE); } } @RestController public class ChatController { @GetMapping("/users") public List<String> users() { return new ArrayList<>(WebSocketHandler.users.values()); } } ``` Vue.js前端代码: ```html <template> <div> <h2>Chat Room</h2> <div> <label>Your name:</label> <input v-model="name" @keyup.enter="join" /> <button @click="join">Join</button> </div> <div v-if="joined"> <div> <label>Message:</label> <input v-model="message" @keyup.enter="send" /> <button @click="send">Send</button> </div> <div> <h3>Users:</h3> <ul> <li v-for="user in users" :key="user">{{ user }}</li> </ul> </div> <div> <h3>Chat:</h3> <ul> <li v-for="chat in chats" :key="chat.id"> <strong>{{ chat.sender }}:</strong> {{ chat.content }} </li> </ul> </div> </div> </div> </template> <script> import SockJS from "sockjs-client"; import Stomp from "stompjs"; export default { data() { return { name: "", message: "", joined: false, chats: [], users: [], stompClient: null, }; }, methods: { join() { const socket = new SockJS("/chat"); this.stompClient = Stomp.over(socket); this.stompClient.connect({}, () => { this.stompClient.subscribe("/topic/chat", (message) => { const chat = JSON.parse(message.body); if (chat.type === "JOIN") { this.users.push(chat.sender); } else if (chat.type === "LEAVE") { this.users.splice(this.users.indexOf(chat.sender), 1); } this.chats.push(chat); }); this.stompClient.send( "/app/chat", JSON.stringify(ChatMessage.joinMessage(this.name)) ); this.joined = true; }); }, send() { this.stompClient.send( "/app/chat", JSON.stringify( new ChatMessage(this.name, this.message, ChatMessage.MessageType.CHAT) ) ); this.message = ""; }, }, }; class ChatMessage { static MessageType = { CHAT: "CHAT", JOIN: "JOIN", LEAVE: "LEAVE", }; constructor(sender, content, type) { this.sender = sender; this.content = content; this.type = type; } static joinMessage(sender) { return new ChatMessage(sender, "", ChatMessage.MessageType.JOIN); } static leaveMessage(sender) { return new ChatMessage(sender, "", ChatMessage.MessageType.LEAVE); } } </script> ``` 在这个示例中,我们使用了Spring Boot的WebSocket支持来处理来自客户端的事件。我们创建了一个WebSocket处理程序,它维护了一个用户会话列表,并在用户加入、离开或发送聊天消息时广播消息到所有连接的客户端。我们还为WebSocket处理程序创建了一个控制器,以便在客户端请求所有当前连接的用户列表时返回它们。 在Vue.js应用程序中,我们使用SockJS和Stomp.js来建立与服务器的WebSocket连接,并处理来自服务器的事件。我们使用Vue.js的数据绑定来更新聊天消息、用户列表和用户输入框中的数据,并在加入聊天、发送聊天消息或断开连接时发送相关的WebSocket事件。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Selenium399

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

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

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

打赏作者

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

抵扣说明:

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

余额充值