一、websocket是什么
二、实现websocket
2.1参考学习b站资料(一定要看,前后端详细)
01-websocket协议及实现_哔哩哔哩_bilibili
2.2学习配套代码
1.导入pom依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
2.编写配置类
//配置类 @Configuration public class WebSocketConfig { @Bean //注入ServerEndpointExporter bean对象,自动注册使用注解@ServerEndpoint的bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }
3.所需要的pojo类:
4.需要的工具类,用于转化信息格式
import com.example.ex1.mes_websocket.pojo.ResultMessage; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; public class MessageUtils { public static String getMessage(boolean isSystemMessage,String fromName,Object message){ try { ResultMessage result = new ResultMessage(); result.setSystem(isSystemMessage); result.setMessage(message); if (fromName!=null){ result.setFromName(fromName); } //把字符串转成json格式的字符串 ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(result); }catch (JsonProcessingException e){ e.printStackTrace(); } return null; } }
5.登录的验证以及获取用户名的controller
import com.example.ex1.mes_websocket.pojo.Result; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpSession; @RestController public class LoginController { @RequestMapping("/toLogin") public Result tologin(@RequestParam("user") String user,@RequestParam("pwd") String pwd, HttpSession session){ Result result = new Result(); if (user.equals("张三")&&pwd.equals("123")){ result.setFlag(true); session.setAttribute("user",user); }else if (user.equals("李四")&&pwd.equals("123")){ result.setFlag(true); session.setAttribute("user",user); }else if (user.equals("123")&&pwd.equals("123")){ result.setFlag(true); session.setAttribute("user",user); } else if (user.equals("王五")&&pwd.equals("123")){ result.setFlag(true); session.setAttribute("user",user); }else { result.setFlag(false); result.setMessage("登录失败"); } return result; } @RequestMapping("/getUsername") public String getUsername(HttpSession session){ String username = (String) session.getAttribute("user"); return username; } }
6.登录页面(前端页面用到jquery)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <script src="js/jquery.min.js"></script> <form id="loginForm"> <label>账号:</label> <input type="text" name="user"> <label>密码:</label> <input type="password" name="pwd"> <button type="button" id="btn">登录</button> </form> <p id="err_msg"></p> <script> $("#btn").click(function () { $.get("toLogin?",$("#loginForm").serialize(),function(res){ if (res.flag){ console.log(res); location.href = "main.html"; } else { console.log(res); $("#err_msg").html(res.message); } },"json"); }) </script> </body> </html>
7.主页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <style> #left{ float: left; width: 30%; height: 500px; margin-left: 200px; } #right{ float: right; width: 30%; height: 500px; } #top{ margin-top: 50px; float: top; width: 30%; height: 250px; } #content{ border: aquamarine 1px solid; width: 100%; height: 300px; } #input{ margin-top: 20px; width: 100%; height: 200px; } #input input{ width: 100%; height: 100px; } #input button{ float: right; } #mes_left{ float: left; } #mes_right{ float: right; width: 50%; text-align: right; } </style> <body> <script src="js/jquery.min.js"></script> <h3 style="text-align: center" id="username"></h3> <div> <div id="left"> <h4 id="new"></h4> <div id="content"> </div> <div id="input"> <input type="text" id="input_text"> <button id="submit">发送</button> </div> </div> <div id="right"> <div id="top"> <p>在线的好友</p> <div id="hylist"> </div> </div> <div id="bottom"> <p>系统广播</p> <div id="xtlist"> </div> </div> </div> </div> <script> var username; $(function () { var toName; $.ajax({ url:"getUsername", success:function (res) { username = res; $("#username").html("用户:"+ username +"<span>在线</span>"); // $("#username").html("用户:123<span>在线</span>"); }, async:false //同步请求,只有上面好了才会接着下面 }); var ws = new WebSocket("ws://localhost:8080/chat"); ws.onopen = function (ev) { $("#username").html("用户:"+ username +"<span>在线</span>"); } //接受消息 ws.onmessage = function (ev) { var datastr = ev.data; var res = JSON.parse(datastr); //判断是否是系统消息 if(res.system){ //好友列表 //系统广播 var names = res.message; var userlistStr = ""; var broadcastListStr = ""; for (var name of names){ if (name != username){ userlistStr += "<a οnclick='showChat(\""+name+"\")'>"+ name +"</a></br>"; broadcastListStr += "<p>"+ name +"上线了</p>"; } }; $("#hylist").html(userlistStr); $("#xtlist").html(broadcastListStr); }else { //不是系统消息 var str = "<span id='mes_left'>"+ res.message +"</span></br>"; if (toName == res.fromName) $("#content").append(str); var chatdata = sessionStorage.getItem(res.fromName); if (chatdata != null){ str = chatdata + str; } sessionStorage.setItem(res.fromName,str); }; }, ws.onclose = function (ev) { $("#username").html("用户:"+ username +"<span>离线</span>"); } showChat = function(name){ // alert("dsaad"); toName = name; //清空聊天区 $("#content").html(""); $("#new").html("当前正与"+toName+"聊天"); var chatdata = sessionStorage.getItem(toName); if (chatdata != null){ $("#content").html(chatdata); } }; //发送消息 $("#submit").click(function () { //获取输入的内容 var data = $("#input_text").val(); $("#input_text").val(""); var json = {"toName": toName ,"message": data}; //将数据展示在聊天区 var str = "<span id='mes_right'>"+ data +"</span></br>"; $("#content").append(str); var chatdata = sessionStorage.getItem(toName); if (chatdata != null){ str = chatdata + str; } sessionStorage.setItem(toName,str); //发送数据 ws.send(JSON.stringify(json)); }) }) </script> </body> </html>
8.获取HttpSession对象的类
import javax.servlet.http.HttpSession; import javax.websocket.HandshakeResponse; import javax.websocket.server.HandshakeRequest; import javax.websocket.server.ServerEndpointConfig; public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator { @Override public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) { //获取HttpSession对象 HttpSession httpSession = (HttpSession) request.getHttpSession(); sec.getUserProperties().put(HttpSession.class.getName(),httpSession); } }
9.最后的通信类
import com.example.ex1.mes_websocket.pojo.Message; import com.example.ex1.mes_websocket.utils.MessageUtils; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Component; import javax.servlet.http.HttpSession; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfigurator.class) @Component public class ChatEndpoint { //用来存储每个用户客户端对象的ChatEndpoint对象 private static Map<String,ChatEndpoint> onlineUsers = new ConcurrentHashMap<>(); //声明session对象,通过对象可以发送消息给指定的用户 private Session session; //声明HttpSession对象,我们之前在HttpSession对象中存储了用户名 private HttpSession httpSession; //连接建立 @OnOpen public void onOpen(Session session, EndpointConfig config){ this.session = session; HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName()); this.httpSession = httpSession; //存储登陆的对象 String username = (String)httpSession.getAttribute("user"); onlineUsers.put(username,this); //将当前在线用户的用户名推送给所有的客户端 //1 获取消息 String message = MessageUtils.getMessage(true, null, getNames()); //2 调用方法进行系统消息的推送 broadcastAllUsers(message); } private void broadcastAllUsers(String message){ try { //将消息推送给所有的客户端 Set<String> names = onlineUsers.keySet(); for (String name : names) { ChatEndpoint chatEndpoint = onlineUsers.get(name); chatEndpoint.session.getBasicRemote().sendText(message); } }catch (Exception e){ e.printStackTrace(); } } //返回在线用户名 private Set<String> getNames(){ return onlineUsers.keySet(); } //收到消息 @OnMessage public void onMessage(String message,Session session){ //将数据转换成对象 try { ObjectMapper mapper =new ObjectMapper(); Message mess = mapper.readValue(message, Message.class); String toName = mess.getToName(); String data = mess.getMessage(); String username = (String) httpSession.getAttribute("user"); String resultMessage = MessageUtils.getMessage(false, username, data); //发送数据 onlineUsers.get(toName).session.getBasicRemote().sendText(resultMessage); } catch (Exception e) { e.printStackTrace(); } } //关闭 @OnClose public void onClose(Session session) { String username = (String) httpSession.getAttribute("user"); //从容器中删除指定的用户 onlineUsers.remove(username); MessageUtils.getMessage(true,null,getNames()); }}