Websocket+SpringBoot实现简单在线聊天(包含前后端代码)

发布时间 2023-07-05 21:44:10作者: 风筝上的猫

1、样式展示

登录界面

(用户名自己取,密码是111,可在前端文件中改,因为做的比较简单,没有把用户做数据库相关的,所以直接在前端固定了密码是111)

聊天界面

2、代码展示

前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <script type="text/javascript" src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
    <title>登录</title>
    <script>
        $(function() {
            var websocket=null;

            //登录
            $(".loginBtn").click(function(){
                var nickname=$(".nicknameI").val()
                var password=$(".passwordI").val()
                if(nickname==""){
                    alert("请输入昵称")
                    return
                }
                if(password==""){
                    alert("请输入密码")
                    return
                }
                if(password==111){
                    alert("登录成功")
                    $(".container").hide()
                    $(".messageBox").show()
                    $(".nicknameI").val("")
                    $(".passwordI").val("")
                }else{
                    alert("密码错误")
                }
                //判断当前浏览器是否支持websocket
                var supprotWebSocket='WebSocket' in window || 'MozWebSocket' in window;
                if(supprotWebSocket){
                    //建立websocket连接
                    websocket = new WebSocket("ws://localhost:8080/websocket/"+nickname);
                }else{
                    alert("该浏览器不支持websocket,请更换")
                }

                //建立连接发生错误回调方法
                websocket.onerror=function(){
                    console.log("error")
                }
                //建立连接正确回调
                websocket.onopen=function (){
                    console.log("连接建立成功")
                }
                //收到消息回调方法
                websocket.onmessage=function(event){
                    console.log(event)
                    var flag=event.data.search("恭喜您成功连接上WebSocket")!=-1;
                    console.log(flag)
                     if(flag){
                      onlineMessage(event.data)
                         return
                     }
                    messageShow(event.data)

                }
                //关闭连接回调方法
                websocket.onclose=function() {
                    console.log("连接关闭")
                }
                //监听窗口关闭时间,窗口关闭主动关闭websocket连接,防止连接还没断开就关闭
                window.onbeforeunload=function (){
                    websocket.close();
                }
            })

            //将消息显示到界面
            function messageShow(message){
                $("#messageArea").append('<span>'+message+'</span><br/><br/>');
            }
            //显示在线人数信息
            function onlineMessage(message){
                $(".onlineMessage").empty()
                $(".onlineMessage").append(message)
            }

            //关闭websocket连接
            $(".close").click(function (){
                websocket.close();
                $(".messageBox").hide()
                $(".container").show()
            })

            //发送消息
            $(".send").click(function(){
                var message=$(".message").val();
                var toUser=$(".toUser").val()
                var socketMsg = {msg:message,toUser:toUser};
                if(toUser == ''){
                    //群聊.
                    socketMsg.type = 0;
                }else{
                    //单聊.
                    socketMsg.type = 1;
                }
                //转为JSON格式
                websocket.send(JSON.stringify(socketMsg));
                $(".message").val("")
                $(".toUser").val("")
            })

        });

    </script>

</head>

<body>
<div class="messageBox">
    <div class="onlineMessage"></div>
    <div id="messageArea"></div>
    <div class="operate">
        <input type="text" placeholder="输入你想说的话" class="message">
        <input class="toUser" type="text" placeholder="目标"/>
        <input type="button" value="发送" class="send">
        <input type="button" value="下线" class="close">
    </div>
</div>
<div class="container">
    <ul class="tab">
        <li class="current">快速登录</li>
    </ul>
    <div class="content">
        <div class="maincontent1">
            <div class="nickname">
                <div class="nicknameT">昵称:</div>
                <input type="text" class="nicknameI">
            </div>
            <div class="password">
                <div class="passwordT">密码:</div>
                <input type="password" class="passwordI">
            </div>
            <div class="forget"><a href="#">忘记密码?</a></div>
            <input type="button" value="登录" class="loginBtn" id="conectWebSocket">
        </div>
    </div>
</div>

</body>
<style>
    * {
        margin: 0;
        padding: 0;
        text-decoration: none;
    }

    body {
        text-align: center;
    }

    .container {
        width: 400px;
        background-color: rgba(189, 218, 234, 0.7);
        margin: 200px auto;
        border-radius: 30px;
    }

    .maincontent1 {}


    .tab {
        display: flex;
        justify-content: space-between;
        margin: 20px auto 35px auto;
        list-style: none;
    }

    .current {
        border-bottom: 2px solid rgba(161, 161, 160,0.8);
    }

    .tab>li {
        width: 100%;
        padding: 15px ;
        cursor: pointer;
        font-size: 20px;
        text-shadow: 3px 2px 1px darkgray;
    }

    .nickname {
        display: flex;
        justify-content: space-around;
        margin: 20px auto 20px auto;
    }


    .password {
        display: flex;
        justify-content: space-around;
        margin: 20px auto 20px auto;

    }

    .nicknameI,
    .passwordI,
    .message,
    .toUser{
        width: 60%;
        height: 25px;
        border-radius: 5px;
        border: none;
        outline: none;
        box-shadow: -1px 1px 1px 2px rgba(42, 84, 96, 0.16);
    }
    .toUser{
        width:30px;
        margin: 0px 3px 0px 8px;
    }

    .forget {
        width: 90%;
        text-align: right;
    }

    .loginBtn {
        width: 80%;
        height: 35px;
        background-color: rgb(57, 134, 156);
        margin: 30px auto 35px auto;
        border: none;
        border-radius: 7px;
        cursor: pointer;
        box-shadow: -1px 1px 1px 2px rgba(0, 0, 156, 0.16);
    }

    .loginBtn:hover {
        background: rgb(62, 121, 138);
        background-position-x: -280px;
    }
    .messageBox{
        display: none;
        margin: 50px auto;
        width: 500px;
        height: 600px;
        border:3px solid rgb(57, 134, 156);
        border-radius: 15px;
        padding: 10px;
        position: relative;
    }
    .messageBox .send,.close{
        width: 45px;
        height: 26px;
        margin: 0px 3px;
        background-color: rgb(57 134 156 / 68%);;
        border: none;
        border-radius: 7px;
        cursor: pointer;
        box-shadow: 1px 1px 0px 1px rgb(137 137 145 / 70%);
    }
    .messageBox .send:hover,.messageBox .close:hover{
        background-color: rgb(57, 134, 156);
    }
    .operate{
        width: 100%;
        display: flex;
        position: absolute;
        left:calc(250px - 41%);
        bottom: 20px;
    }
    #messageArea{
        width: 400px;
        margin:10px auto 20px;
        padding:20px;
        max-height: 480px;
        white-space: pre-line;
        word-break: break-all;
        overflow-y:auto;

    }
    .onlineMessage{
        width: 430px;
        height: 20px;
        margin:-5px auto;
    }
    #messageArea span{
        padding: 2px 3px;
        margin-top: 3px;
        height: 25px;
        border-radius: 5px;
        border: none;
        outline: none;
        box-shadow: -1px 1px 1px 2px rgba(42, 84, 96, 0.16);
    }
</style>
</html>

后端

首先做一个WebSocket的配置

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

然后需要一个消息实体类

import lombok.Data;

@Data
public class SocketMsg {
    /**
     *聊天类型
     */
    private int type;

    /**
     * 发送者
     */
    private String fromUser;

    /**
     * 接收者
     */
    private String toUser;

    /**
     * 消息
     */
    private String msg;
}

接着写websocket逻辑

import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.qcby.socketdemo.entity.SocketMsg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @Auther: liu.ning
 * @Description: websocket的具体实现类
 */
@Slf4j
@ServerEndpoint(value = "/websocket/{nickname}")
@Component
public class MyWebSocket {
    //用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    private String nickname;
    private static Map<String,Session> map=new HashMap<>();
    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("nickname") String nickname) {
        this.session = session;
        this.nickname=nickname;
        map.put(session.getId(),session);
        //加入set中
        webSocketSet.add(this);
        System.out.println("有新连接加入:"+nickname+",当前在线人数为" + webSocketSet.size());
        this.session.getAsyncRemote().sendText("恭喜您成功连接上WebSocket【"+session.getId()+"】-->当前在线人数为:"+webSocketSet.size());

    }
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
        System.out.println("有一连接关闭!当前在线人数为" + webSocketSet.size());
    }
    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息*/
    @OnMessage
    public void onMessage(String message, Session session,@PathParam("nickname") String nickname) {
        System.out.println("来自客户端的消息-->"+nickname+": " + message);
        try{
            //从客户端过来的是json数据,将其转换为SocketMsg对象
            Gson gson=new Gson();
            SocketMsg socketMsg=gson.fromJson(message,SocketMsg.class);
            log.info("json转为对象:"+socketMsg);
            log.info("map"+map);
            if(socketMsg.getType()==1){
                //单聊
                socketMsg.setFromUser(session.getId());
//                socketMsg.setToUser();
                Session fromSession=map.get(socketMsg.getFromUser());
                log.info("touser:"+socketMsg.getToUser());
                Session toSession=map.get(socketMsg.getToUser());
                log.info("接收方:"+toSession+"发送方:"+fromSession);
                if(toSession!=null){
                    fromSession.getAsyncRemote().sendText(nickname+":"+socketMsg.getMsg());
                    toSession.getAsyncRemote().sendText(nickname+":"+socketMsg.getMsg());
                }else{
                    fromSession.getAsyncRemote().sendText("对方不在线或您输入的频道号错误");
                }
            }else{
                //群发消息
                broadcast(nickname+": "+socketMsg.getMsg());
            }
        }catch (JsonParseException e) {
            e.printStackTrace();
        }

    }
    /**
     * 发生错误时调用
     *
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }
    /**
     * 群发自定义消息
     * */
    public void broadcast(String message){
        for (MyWebSocket item : webSocketSet) {
            item.session.getAsyncRemote().sendText(message);//异步发送消息.
        }
    }
}

 到这儿所有想过逻辑都写完了,我们需要一个登录跳转

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class loginController {

    @RequestMapping("/login")
    public String skip(){
        return "login";
    }
}

启动项目,打开浏览器 输入http://localhost:8080/login,访问登录页面

(这里端口号因为没做配置所以使用的默认端口号8080,根据自己情况更改)

当然我们可以根据启动信息,找到端口号