目录
一、实现聊天室原理
二、聊天室前端代码
三、聊天室后端代码(重点)
四、聊天室实现效果展示
一、实现聊天室原理
1.1 介绍websocket协议
websocket是一种通信协议,再通过websocket实现弹幕聊天室时候,实现原理是客户端首先使用http协议请求服务器将通信协议转为websocket协议。
1.2、websocket的API
websocket分为客户端与服务器,其实现的API都不一样。
前端创建websocket案例:
<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 项目结构
二、聊天室前端代码
前端核心在于两个:一个登陆界面,一个聊天室界面。
登陆界面:
<!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>
效果:
聊天室页面:
<!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包.
<!-- 使用阿里巴巴的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的配置
@Configuration
public class WebsocketConfig {// 将ServerEndPointExplorer加入ioc容器中@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
第二个配置是获取httpsession配置
public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {@Overridepublic 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)
为了确保实现的消息格式的准确,需要创建对应工具类,确保对应的格式的准确。
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请求实体。
对于聊天室,需要用户发送的信息,服务器给用户发送的信息。
@Data
public class Result {private boolean flag;private String message;
}
@Data
public class User {private String userId;private String username;private String password;
}
@Data
public class Message {private String toName;private String message;
}
@Data
public class ResultMessage {private boolean isSystem;private String fromName;private Object message;//如果是系统消息是数组
}
3.5 后端基础功能的实现
后端实现登陆功能:用户名可以随意输入,但是密码必须是123。
@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;}
后端实现获取用户名称功能:
@GetMapping("/getUsername")public String getUsername(HttpSession session) {String username = (String) session.getAttribute("user");return username;}
3.6 通过session发送消息的核心功能(重点)
@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*/@OnOpenpublic 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*/@OnMessagepublic 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*/@OnClosepublic 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 实现聊天功能