基于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 实现聊天功能


相关推荐
狼头长啸李树身5 分钟前
眼儿媚·秋雨绵绵窗暗暗
大数据·网络·服务发现·媒体
Marst Code5 分钟前
(Django)初步使用
后端·python·django
代码之光_198012 分钟前
SpringBoot校园资料分享平台:设计与实现
java·spring boot·后端
编程老船长25 分钟前
第26章 Java操作Mongodb实现数据持久化
数据库·后端·mongodb
IT果果日记1 小时前
DataX+Crontab实现多任务顺序定时同步
后端
SizeTheMoment1 小时前
初识HTTP协议
网络·网络协议·http
哲伦贼稳妥2 小时前
一天认识一个硬件之机房地板
运维·网络·经验分享·其他
姜学迁2 小时前
Rust-枚举
开发语言·后端·rust
爱学习的小健3 小时前
MQTT--Java整合EMQX
后端
北极小狐3 小时前
Java vs JavaScript:类型系统的艺术 - 从 Object 到 any,从静态到动态
后端