1. WebSocket介绍
- WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。
- WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
- HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。
- 这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。
- 这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步 AJAX 请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。
2. websocket协议
- 本协议有两部分:握手和数据传输。
- 握手是基于http协议的。
3.1 websocket对象
实现 WebSockets 的 Web 浏览器将通过 WebSocket 对象公开所有必需的客户端功能(主要指支持 Html5 的浏览器)。
以下 API 用于创建 WebSocket 对象:
var ws = new WebSocket(url);
参数url格式说明: ws://ip地址:端口号/资源名称
3.2 websocket事件
WebSocket 对象的相关事件
事件事件处理程序描述onopenwebsocket对象.onopen连接建立时触发onmessagewebsocket对象.onmessage客户端接收服务端数据时触发onerrorwebsocket对象.onerror通信发生错误时触发onclosewebsocket对象.onclose连接关闭时触发3.3 WebSocket方法
WebSocket 对象的相关方法:
方法描述send使用连接时发送消息服务器实现
Java WebSocket应用由一系列的WebSocketEndpoint组成。Endpoint 是一个java对象,代表WebSocket链接的一端,对于服务端,我们可以视为处理具体WebSocket消息的接口, 就像Servlet之与http请求一样。
我们可以通过两种方式定义Endpoint:
· 第一种是编程式, 即继承类 javax.websocket.Endpoint并实现其方法。
· 第二种是注解式, 即定义一个POJO, 并添加 @ServerEndpoint相关注解。
实现流程
服务端如何接收数据
通过为 Session 添加 MessageHandler 消息处理器来接收消息,当采用注解方式定义Endpoint时,我们还可以通过 @OnMessage 注解指定接收消息的方法。
服务端如何推送数据
发送消息则由RemoteEndpoint完成,其实例由Session维护,根据使用情况,我们可以通过
Session.getBasicRemote获取同步消息发送的实例,然后调用其sendXxx()方法就可以发送消息,可以通过
Session.getAsyncRemote获取异步消息发送实例。
实现一个简单的聊天室功能
步骤:
1.首先导入依赖- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-websocket</artifactId>
- </dependency>
复制代码 Message- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class Message {
- private Long id;
- @TableField(value = "from_user_id")
- private User fromUser;
- @TableField(value = "to_user_id")
- private User toUser;
- private String content;
- @TableField(fill = FieldFill.INSERT)
- private LocalDateTime createTime;
- @TableField(fill = FieldFill.INSERT)
- private LocalDateTime updateTime;
- private int messageType;
- @TableField(exist = false)
- private String toName;
- @TableField(exist = false)
- private String fromName;
- @TableField(exist = false)
- private String message;
- //添加好友码
- public static final int ADD_FRIEND = 2;
- //好友列表消息码
- public static final int FRIEND_LIST_TYPE = 3;
复制代码 Result- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public class Result<T>{
- /**
- * 状态码
- */
- private Integer code;
- /**
- * 提示信息,如果有错误时,前端可以获取该字段进行提示
- */
- private boolean flag;
- private String message;
- private T data; //数据
- public static <T> Result<T> success(T object) {
- Result<T> r = new Result<T>();
- r.data = object;
- r.code = 1;
- r.flag = true;
- return r;
- }
- public static <T> Result<T> error(String message) {
- Result r = new Result();
- r.message = message;
- r.code = 0;
- r.flag = false;
- return r;
- }
- }
复制代码 登录- @PostMapping("/login")
- public Result<User> login(@RequestBody User user ,HttpSession session){
- String password = user.getPassword();
- String username = user.getUsername();
- log.info("用户登录操作");
- //MD5加密
- password = DigestUtils.md5DigestAsHex(password.getBytes());
- //根据用户名查找数据库
- LambdaQueryWrapper<User> wrapper =new LambdaQueryWrapper<>();
- wrapper.eq(User::getUsername,username);
- User one = userService.getOne(wrapper);
- if (one == null){
- return Result.error("用户不存在,请先注册");
- }if (! one.getPassword().equals(password)){
- return Result.error("用户名或者密码有误");
- }
- // 登录成功,将用户的ID存储到WebSocket连接的Session中
- session.setAttribute("userId", one.getId()); // 假设用户ID为one.getId()
- String sessionId = session.getId();
- return Result.success(one);
- }
复制代码 获取用户名- @GetMapping("/getUsername")
- private String getUsername(HttpSession session) {
- // 从 HttpSession 中获取用户信息
- User user = (User) session.getAttribute("user");
- if (user != null) {
- return user.getUsername();
- }
- return null;
- }
复制代码 配置- @Configuration
- @EnableWebSocketMessageBroker
- public class WebsocketConfig implements WebSocketMessageBrokerConfigurer {
- @Bean
- //注入ServerEndpointExporter bean.对象,自动注册使用了@ServerEndpoint
- public ServerEndpointExporter serverEndpointExporter(){
- return new ServerEndpointExporter();
- }
- @Override
- public void registerStompEndpoints(StompEndpointRegistry registry) {
- // 定义一个 WebSocket 入口,客户端需要连接到它才能接收推送消息
- registry.addEndpoint("/websocket").withSockJS();
- }
- @Override
- public void configureMessageBroker(MessageBrokerRegistry config) {
- // 启用推送的消息代理(即使用 STOMP 实现 WebSocket 的代理)
- config.enableSimpleBroker("/topic");
- // 开启基于用户的 WebSocket 会话
- config.setUserDestinationPrefix("/user");
- }
- }
- public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {
- /**
- * 获取session对象
- * @param sec
- * @param request
- * @param response
- */
- @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(value = "/chat",configurator = GetHttpSessionConfig.class)
- @Component
- @Slf4j
- public class ChatEndpoint {
- private Session session;
- private static HttpSession httpSession;
- //用来存储每一个客户端对象对应的ChatEndpoint对象
- private static final Map<String,Session> onlineUsers = new ConcurrentHashMap<>();
- @OnOpen
- public void onopen(Session session, EndpointConfig config) {
- //将局部的session对象赋值给成员session
- this.session = session;
- //获取Httpsession对象 ,键值对集合,得到键获取值
- this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
- String user = (String) this.httpSession.getAttribute("user");
- onlineUsers.put(user,session);
- //广播消息,获取在线的所有好友
- String message = MessageUtils.getMessage(true, null, getFriendsName());
- broadcastAllUsers(message);
- }
- /**
- * 获取所有在线的好友信息,名称
- * @return
- */
- public Set getFriendsName(){
- Set<String> set = onlineUsers.keySet();
- return set;
- }
- /**
- * 发给所有人的广播
- * @param message
- */
- private void broadcastAllUsers(String message){
- // 拿到所有的用户的chatEndpoint对象 //存储用户的session信息
- Set<Map.Entry<String,Session>> entries = onlineUsers.entrySet();
- //遍历map集合
- for (Map.Entry<String, Session> entry : entries) {
- //获取所有用户对应的session对象 //拥有getBasicRemote发送消息的方法
- Session session =entry.getValue();
- //发送消息
- try {
- session.getBasicRemote().sendText(message);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- @OnMessage
- public void onMessage(String message, @PathParam("username") String username){
- try {
- log.info("服务端收到用户username={}的消息:{}", username, message);
- //将消息转换成message对象
- Message msg = JSON.parseObject(message,Message.class);
- //获取接收方的用户名
- String toName = msg.getToName();
- //获取消息数据
- String message1 = msg.getMessage();
- //获取接收方的用户的session对象
- Session session = onlineUsers.get(toName);
- if (session != null) {
- // 获取当前登录的用户 从session中获取
- String user = (String) httpSession.getAttribute("user"); //user代表的是发送方
- String message2 = MessageUtils.getMessage(false, user, message1);
- session.getBasicRemote().sendText(message2);
- }else {
- log.info("未找到用户username{}的session",toName);
- }
- if (msg.ADD_FRIEND ==2 && session != null){
- //获取发送发
- String user = (String) httpSession.getAttribute("user");
- //获取消息
- String message2 = MessageUtils.getMessage(false, user, message1);
- //发送
- session.getBasicRemote().sendText(message2);
- //将好友添加到相应的列表中,例如用Map存储好友列表
- Map<String, List<String>> friendLists = (Map<String, List<String>>) httpSession.getAttribute("friendLists");
- List<String> friendList=friendLists.get(user);
- friendList.add(toName);
- friendLists.put(user,friendList);
- httpSession.setAttribute("friendLists", friendLists);
- //发送好友列表
- Message friendListMessage = new Message();
- friendListMessage.setMessageType(3);
- friendListMessage.setFromName("System");
- friendListMessage.setToName(user);
- friendListMessage.setMessage(JSON.toJSONString(friendList));
- ChatEndpoint.send(friendListMessage,getFriendsName());
- }
- else {
- log.info("未找到用户username{}的session",toName);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- private static void send(Message friendListMessage, Set friendsName) {
- try {
- String toName = friendListMessage.getToName();
- String message = friendListMessage.getMessage();
- Session session = onlineUsers.get(toName);
- if (session != null) {
- String json = JSON.toJSONString(message);
- session.getBasicRemote().sendText(json);
- } else {
- log.info("未找到用户{}的session", toName);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- }
- @OnClose
- public void onclose(Session session) {
- //提出session中的记录
- String user = (String) this.httpSession.getAttribute("user");
- onlineUsers.remove(user);
- //通知所有用户,此账号下线。
- String message = MessageUtils.getMessage(true, null, getFriendsName());
- broadcastAllUsers(message);
- }
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |