基于WebSocket实现客户聊天室

目录

一、实现聊天室原理

二、聊天室前端代码

三、聊天室后端代码(重点)

四、聊天室实现效果展示


一、实现聊天室原理

1.1 介绍websocket协议

websocket是一种通信协议,再通过websocket实现弹幕聊天室时候,实现原理是客户端首先使用http协议请求服务器将通信协议转为websocket协议。

1.2、websocket的API

websocket分为客户端与服务器,其实现的API都不一样。

前端创建websocket案例:

html 复制代码
<script>
let ws = new WebSocket("ws:/localhost/chat")
ws.open = function(){
};

ws.onmessage = function(evt){
    //通过evt.data 可以获取服务器发送的数据
};

ws.onclose = function(){
};
 
</script>

1.3 项目实现流程

1.4 项目结构


二、聊天室前端代码

前端核心在于两个:一个登陆界面,一个聊天室界面。

登陆界面:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <title>聊天室-登录</title>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta name="keywords"
          content="Transparent Sign In Form Responsive Widget,Login form widgets, Sign up Web forms , Login signup Responsive web form,Flat Pricing table,Flat Drop downs,Registration Forms,News letter Forms,Elements"/>
    <script type="application/x-javascript">
        addEventListener("load", function () {
            setTimeout(hideURLbar, 0);
        }, false);

        function hideURLbar() {
            window.scrollTo(0, 1);
        }
    </script>

    <script src="js/jquery-1.9.1.min.js"></script>
    <link rel="icon" href="img/chat.ico" type="image/x-icon"/>
    <link rel="stylesheet" href="css/font-awesome.css"/> <!-- Font-Awesome-Icons-CSS -->
    <link rel="stylesheet" href="css/login.css" type="text/css" media="all"/> <!-- Style-CSS -->
</head>


<body class="background">
<div class="header-w3l">
    <h1>聊天室</h1>
</div>
<div class="main-content-agile" id="app">
    <div class="sub-main-w3">
        <h2>登录</h2>
        <form id="loginForm">
            <div class="icon1">
                <input placeholder="用户名" id="username" v-model="user.username" type="text"/>
            </div>

            <div class="icon2">
                <input placeholder="密码" id="password" v-model="user.password" type="password"/>
            </div>

            <div class="clear"></div>
            <input type="button" id="btn1" @click="login" value="登录"/>
			<div class="icon1">
                <span id="err_msg" style="color: red; ">{{errMessage}}</span>
            </div>
        </form>
    </div>
</div>
<div class="footer">
    <p>北京传智播客教育科技有限公司 版权所有Copyright 2006-2019  All Rights Reserved </p>
</div>
<script src="js/vue.js"></script>
<script src="js/axios-0.18.0.js"></script>
<script>
    new Vue({
        el:"#app",
        data() {
            return {
                errMessage: "",
                user:{
                    username:"",
                    password:""
                }
            }
        },
        methods: {
            login() {
                axios.post("user/login",this.user).then(res => {
                    //判断登陆是否成功
                    if(res.data.flag) {
                        location.href = "main.html"
                    } else {
                        this.errMessage = res.data.message;
                    }
                });
            }
        }
    });
</script>
</body>
</html>

效果:

聊天室页面:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <title>黑马畅聊-登录</title>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta name="keywords"
          content="Transparent Sign In Form Responsive Widget,Login form widgets, Sign up Web forms , Login signup Responsive web form,Flat Pricing table,Flat Drop downs,Registration Forms,News letter Forms,Elements"/>
    <script type="application/x-javascript">
        addEventListener("load", function () {
            setTimeout(hideURLbar, 0);
        }, false);

        function hideURLbar() {
            window.scrollTo(0, 1);
        }
    </script>

    <script src="js/jquery-1.9.1.min.js"></script>
    <link rel="icon" href="img/chat.ico" type="image/x-icon"/>
    <link rel="stylesheet" href="css/font-awesome.css"/> <!-- Font-Awesome-Icons-CSS -->
    <link rel="stylesheet" href="css/login.css" type="text/css" media="all"/> <!-- Style-CSS -->
</head>


<body class="background">
<div class="header-w3l">
    <h1>聊天室</h1>
</div>
<div class="main-content-agile" id="app">
    <div class="sub-main-w3">
        <h2>登录</h2>
        <form id="loginForm">
            <div class="icon1">
                <input placeholder="用户名" id="username" v-model="user.username" type="text"/>
            </div>

            <div class="icon2">
                <input placeholder="密码" id="password" v-model="user.password" type="password"/>
            </div>

            <div class="clear"></div>
            <input type="button" id="btn1" @click="login" value="登录"/>
			<div class="icon1">
                <span id="err_msg" style="color: red; ">{{errMessage}}</span>
            </div>
        </form>
    </div>
</div>
<div class="footer">
    <p>北京传智播客教育科技有限公司 版权所有Copyright 2006-2019  All Rights Reserved </p>
</div>
<script src="js/vue.js"></script>
<script src="js/axios-0.18.0.js"></script>
<script>
    new Vue({
        el:"#app",
        data() {
            return {
                errMessage: "",
                user:{
                    username:"",
                    password:""
                }
            }
        },
        methods: {
            login() {
                axios.post("user/login",this.user).then(res => {
                    //判断登陆是否成功
                    if(res.data.flag) {
                        location.href = "main.html"
                    } else {
                        this.errMessage = res.data.message;
                    }
                });
            }
        }
    });
</script>
</body>
</html>

三、聊天室后端代码(重点)

3.1首先需要创建springboot项目,并导入以下jar包.

XML 复制代码
<!--        使用阿里巴巴的fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>

<!--        实现websocket的jar包-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

3.2 实现websocket,还需要对其进行配置,创建两个配置类

第一个进行websocketConfig的配置

java 复制代码
@Configuration
public class WebsocketConfig {

// 将ServerEndPointExplorer加入ioc容器中
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

第二个配置是获取httpsession配置

java 复制代码
public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {

    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
        //获取HttpSession对象
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        //将httpSession对象保存起来
        sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
    }
}

项目中需要ServerEndPoint存储所有用户的session对象,通过session实现用户之间的交流。所以还需要获取所有httpsession对象。

3.3 确定消息格式(JSON)

为了确保实现的消息格式的准确,需要创建对应工具类,确保对应的格式的准确。

java 复制代码
public class MessageUtils {

    public static String getMessage(boolean isSystemMessage,String fromName, Object message) {

        ResultMessage result = new ResultMessage();
        result.setSystem(isSystemMessage);
        result.setMessage(message);
        if(fromName != null) {
            result.setFromName(fromName);
        }
        return JSON.toJSONString(result);
    }
}

3.4 然后就是所有实体类的创建。

对于前端实体类,需要用户,封装http请求实体。

对于聊天室,需要用户发送的信息,服务器给用户发送的信息。

java 复制代码
@Data
public class Result {
    private boolean flag;
    private String message;
}
java 复制代码
@Data
public class User {

    private String userId;
    private String username;
    private String password;
}
java 复制代码
@Data
public class Message {
    private String toName;
    private String message;
}
java 复制代码
@Data
public class ResultMessage {

    private boolean isSystem;
    private String fromName;
    private Object message;//如果是系统消息是数组
}

3.5 后端基础功能的实现

后端实现登陆功能:用户名可以随意输入,但是密码必须是123。

java 复制代码
    @PostMapping("/login")
    public Result login(@RequestBody User user, HttpSession session) {
        Result result = new Result();
        if(user != null && "123".equals(user.getPassword())) {
            result.setFlag(true);
            //将数据存储到session对象中
            session.setAttribute("user",user.getUsername());
        } else {
            result.setFlag(false);
            result.setMessage("登陆失败");
        }
        return result;
    }

后端实现获取用户名称功能:

java 复制代码
 @GetMapping("/getUsername")
    public String getUsername(HttpSession session) {

        String username = (String) session.getAttribute("user");
        return username;
    }

3.6 通过session发送消息的核心功能**(重点)**

java 复制代码
@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfig.class)
@Component
public class ChatEndpoint {

    private static Map<String,Session> onlineUsers = new ConcurrentHashMap<>();

    static {
        // 初始化onlineUsers对象
        onlineUsers = new ConcurrentHashMap<>();
    }

    private HttpSession httpSession;

    /**
     * 建立websocket连接后,被调用
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        //1,将session进行保存
        this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        String user = (String) this.httpSession.getAttribute("user");
        onlineUsers.put(user,session);
        //2,广播消息。需要将登陆的所有的用户推送给所有的用户
        String message = MessageUtils.getMessage(true,null,getFriends());
        broadcastAllUsers(message);
    }

    public Set getFriends() {
        Set<String> set = onlineUsers.keySet();
        return set;
    }

    private void broadcastAllUsers(String message) {
        try {
            //遍历map集合
            Set<Map.Entry<String, Session>> entries = onlineUsers.entrySet();
            for (Map.Entry<String, Session> entry : entries) {
                //获取到所有用户对应的session对象
                Session session = entry.getValue();
                //发送消息
                session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            //记录日志
        }
    }

    /**
     * 浏览器发送消息到服务端,该方法被调用
     *
     * 张三  -->  李四
     * @param message
     */
    @OnMessage
    public void onMessage(String message) {
        try {
            //将消息推送给指定的用户
            Message msg = JSON.parseObject(message, Message.class);
            //获取 消息接收方的用户名
            String toName = msg.getToName();
            String mess = msg.getMessage();
            //获取消息接收方用户对象的session对象
            Session session = onlineUsers.get(toName);
            String user = (String) this.httpSession.getAttribute("user");
            String msg1 = MessageUtils.getMessage(false, user, mess);
            session.getBasicRemote().sendText(msg1);
        } catch (Exception e) {
            //记录日志
        }
    }

    /**
     * 断开 websocket 连接时被调用
     * @param session
     */
    @OnClose
    public void onClose(Session session) {
        //1,从onlineUsers中剔除当前用户的session对象
        String user = (String) this.httpSession.getAttribute("user");
        onlineUsers.remove(user);
        //2,通知其他所有的用户,当前用户下线了
        String message = MessageUtils.getMessage(true,null,getFriends());
        broadcastAllUsers(message);
    }
}

代码重点在于对session的使用,将内容放在对应用户session的共享域中,后端则负责统一管理所有用户的session。


四、聊天室实现效果展示

4.1 登陆功能:

4.2 在线与不在线,当后端运行后才能显示在线

4.3 当有其他人登陆时候弹出对应用户名称

4.4 通过点击对应的名称进行一对一聊天

4.5 实现聊天功能


相关推荐
Cobyte20 分钟前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc
米羊12137 分钟前
已有安全措施确认(上)
大数据·网络
程序员侠客行1 小时前
Mybatis连接池实现及池化模式
java·后端·架构·mybatis
Honmaple1 小时前
QMD (Quarto Markdown) 搭建与使用指南
后端
PP东2 小时前
Flowable学习(二)——Flowable概念学习
java·后端·学习·flowable
ManThink Technology2 小时前
如何使用EBHelper 简化EdgeBus的代码编写?
java·前端·网络
invicinble2 小时前
springboot的核心实现机制原理
java·spring boot·后端
全栈老石2 小时前
Python 异步生存手册:给被 JS async/await 宠坏的全栈工程师
后端·python
space62123272 小时前
在SpringBoot项目中集成MongoDB
spring boot·后端·mongodb
珠海西格电力科技2 小时前
微电网能量平衡理论的实现条件在不同场景下有哪些差异?
运维·服务器·网络·人工智能·云计算·智慧城市