Websocket——化神篇

打印 上一主题 下一主题

主题 1655|帖子 1655|积分 4965

WebSocket机制

   WebSocket 是 HTML5 一种新的协议。它实现了欣赏器与服务器全双工通信,能更好的节省服务器资源和带宽并到达实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大差别是:
WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;
WebSocket 须要雷同 TCP 的客户端和服务器端通过握手连接,连接成功后才能相互通信。
  和socket区别

   Socket其实并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。
    Socket是应用层与TCP/IP协议族通信的中央软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族潜伏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
当两台主机通信时,必须通过Socket连接,Socket则利用TCP/IP协议建立TCP连接。TCP连接则更依赖于底层的IP协议,IP协议的连接则依赖于链路层等更低层次。
WebSocket则是一个典范的应用层协议。
区别
Socket是传输控制层协议,WebSocket是应用层协议。
  前世此生

众所周知,Web 应用的交互过程通常是客户端通过欣赏器发出一个哀求,服务器端接收哀求后举行处理并返回结果给客户端,客户端欣赏器将信息呈现,这种机制对于信息变化不是特别频仍的应用尚可,但对于实时要求高、海量并发的应用来说显得捉襟见肘,尤其在当前业界移动互联网发达发展的趋势下,高并发与用户实时响应是 Web 应用经常面临的问题,好比金融证券的实时信息,Web 导航应用中的地理位置获取,交际网络的实时消息推送等。
消息推送常用方式

轮询方式

1.轮询是一种客户端与服务器之间实时通信的技能本领。客户端定期发送哀求来查询服务器是否有新数据或事件,并将响应返 回给客户端。如果服务器有新的数据或事件,则将其返回给客户端;如果没有,则返回一个空响应。客户端收到响应后,可 以处理数据或事件,并根据须要继续发送下一个哀求。
2.长轮询是一种改进的轮询技能,其主要目的是低落轮询过程中的资源消耗和延迟。长轮询的基本原理是客户端发送一个 HTTP哀求给服务器,并保持连接打开,直到服务器有新的数据或事件时才返回响应给客户端。在这期间,服务器会一直保持连接打开,直到超时或有新数据或事件(HTTP1.1版本就是这个性子长连接)
SSE(服务器发送事件)
   SSE在服务器和客户端之间打开一个单向通道
服务端响应的不再是一次性的数据包,而是text/event-stream类型的数据流信息
服务器有数据变动时将数据流式传输到客户端

  WebSocket


客户端API

服务端 API



Endpoint示例
  1. @ServerEndpoint("/chat")
  2. @Component
  3. public class ChatEndpoint {
  4.     @OnOpen
  5.     //连接建立时被调用
  6.     public void onOpen(Session session, EndpointConfig config){}
  7.     @OnMessage
  8.     //接收到客户端发送的数据时被调用
  9.     public void onMessage(String message){}
  10.     @OnClose
  11.     //连接关闭时被调用
  12.     public void onClose(Session session){}
  13. }
复制代码
ChatEndpoint类通过实现WebSocket协议,用于处理客户端的连接、消息通报和关闭事件。
在线聊天室实现

流程分析

消息格式

引入坐标
  1. <dependency>
  2.     <groupId>org.springframework.boot</groupId>
  3.     <artifactId>spring-boot-starter-websocket</artifactId>
  4. </dependency>
复制代码
编写设置类
  1. @Configuration
  2. public class WebsocketConfig {
  3.     @Bean
  4.     public ServerEndpointExporter serverEndpointExporter() {
  5.         return new ServerEndpointExporter();
  6.     }
  7. }
复制代码
通过在设置类中界说一个 ServerEndpointExporter 的 @Bean 方法,Spring 会主动创建一个 ServerEndpointExporter 实例,并将其加入到 Spring 容器中。Spring框架会在启动时通过这个实例主动扫描项目中全部使用@ServerEndpoint注解的类,并将它们注册为WebSocket端点。
编写设置类,用于获取 HttpSession 对象
  1. public class GetHttpSessionConfig extends ServerEndpointConfig.Configurator {
  2.     @Override
  3.     public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
  4.         //获取HttpSession对象
  5.         HttpSession httpSession = (HttpSession) request.getHttpSession();
  6.         //将httpSession对象保存起来
  7.         sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
  8.     }
  9. }
复制代码
GetHttpSessionConfig类用于在WebSocket握手过程中获取HTTP会话(HttpSession)对象,并将其保存到用户属性中,以便在WebSocket会话中使用。

ChatEndpoint类
  1. package com.itheima.ws;
  2. import com.alibaba.fastjson.JSON;
  3. import com.itheima.config.GetHttpSessionConfig;
  4. import com.itheima.utils.MessageUtils;
  5. import com.itheima.ws.pojo.Message;
  6. import org.springframework.stereotype.Component;
  7. import javax.servlet.http.HttpSession;
  8. import javax.websocket.*;
  9. import javax.websocket.server.ServerEndpoint;
  10. import java.util.Map;
  11. import java.util.Set;
  12. import java.util.concurrent.ConcurrentHashMap;
  13. /**
  14. * @version v1.0
  15. * @ClassName: ChatEndpoint
  16. * @Description: TODO(一句话描述该类的功能)
  17. * @Author: 黑马程序员
  18. */
  19. @ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfig.class)
  20. @Component
  21. public class ChatEndpoint {
  22.     private static final Map<String,Session> onlineUsers = new ConcurrentHashMap<>();
  23.     private HttpSession httpSession;
  24.     /**
  25.      * 建立websocket连接后,被调用
  26.      * @param session
  27.      */
  28.     @OnOpen
  29.     public void onOpen(Session session, EndpointConfig config) {
  30.         //1,将session进行保存
  31.         this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
  32.         String user = (String) this.httpSession.getAttribute("user");
  33.         onlineUsers.put(user,session);
  34.         //2,广播消息。需要将登陆的所有的用户推送给所有的用户
  35.         String message = MessageUtils.getMessage(true,null,getFriends());
  36.         broadcastAllUsers(message);
  37.     }
  38.     public Set getFriends() {
  39.         Set<String> set = onlineUsers.keySet();
  40.         return set;
  41.     }
  42.     private void broadcastAllUsers(String message) {
  43.         try {
  44.             //遍历map集合
  45.             Set<Map.Entry<String, Session>> entries = onlineUsers.entrySet();
  46.             for (Map.Entry<String, Session> entry : entries) {
  47.                 //获取到所有用户对应的session对象
  48.                 Session session = entry.getValue();
  49.                 //发送消息
  50.                 session.getBasicRemote().sendText(message);
  51.             }
  52.         } catch (Exception e) {
  53.             //记录日志
  54.         }
  55.     }
  56.     /**
  57.      * 浏览器发送消息到服务端,该方法被调用
  58.      *
  59.      * 张三  -->  李四
  60.      * @param message
  61.      */
  62.     @OnMessage
  63.     public void onMessage(String message) {
  64.         try {
  65.             //将消息推送给指定的用户
  66.             Message msg = JSON.parseObject(message, Message.class);
  67.             //获取 消息接收方的用户名
  68.             String toName = msg.getToName();
  69.             String mess = msg.getMessage();
  70.             //获取消息接收方用户对象的session对象
  71.             Session session = onlineUsers.get(toName);
  72.             String user = (String) this.httpSession.getAttribute("user");
  73.             String msg1 = MessageUtils.getMessage(false, user, mess);
  74.             session.getBasicRemote().sendText(msg1);
  75.         } catch (Exception e) {
  76.             //记录日志
  77.         }
  78.     }
  79.     /**
  80.      * 断开 websocket 连接时被调用
  81.      * @param session
  82.      */
  83.     @OnClose
  84.     public void onClose(Session session) {
  85.         //1,从onlineUsers中剔除当前用户的session对象
  86.         String user = (String) this.httpSession.getAttribute("user");
  87.         onlineUsers.remove(user);
  88.         //2,通知其他所有的用户,当前用户下线了
  89.         String message = MessageUtils.getMessage(true,null,getFriends());
  90.         broadcastAllUsers(message);
  91.     }
  92. }
复制代码
@ServerEndpoint(value = “/chat”, configurator = GetHttpSessionConfig.class) 这行代码声明了一个 WebSocket 端点,客户端可以通过 /chat 路径与之建立连接,并且在握手阶段使用 GetHttpSessionConfig 类来举行自界说设置。
该类是Spring Boot应用中的一个WebSocket端点,它用于处理聊天功能。主要功能包罗:


  • 在用户建立WebSocket连接时保存用户信息和会话对象。
  • 当用户发送消息时,将消息转发给指定的接收用户。
  • 当用户断开连接时,从在线用户列表中移除用户并关照其他用户。
  • 使用GetHttpSessionConfig设置器来获取HTTP会话中的用户信息。
    userController
  1. package com.itheima.controller;
  2. import com.itheima.pojo.Result;
  3. import com.itheima.pojo.User;
  4. import org.springframework.web.bind.annotation.*;
  5. import javax.servlet.http.HttpSession;
  6. @RestController
  7. @RequestMapping("user")
  8. public class UserController {
  9.     /**
  10.      * 登陆
  11.      * @param user 提交的用户数据,包含用户名和密码
  12.      * @param session
  13.      * @return
  14.      */
  15.     @PostMapping("/login")
  16.     public Result login(@RequestBody User user, HttpSession session) {
  17.         Result result = new Result();
  18.         if(user != null && "123".equals(user.getPassword())) {
  19.             result.setFlag(true);
  20.             //将数据存储到session对象中
  21.             session.setAttribute("user",user.getUsername());
  22.         } else {
  23.             result.setFlag(false);
  24.             result.setMessage("登陆失败");
  25.         }
  26.         return result;
  27.     }
  28.     /**
  29.      * 获取用户名
  30.      * @param session
  31.      * @return
  32.      */
  33.     @GetMapping("/getUsername")
  34.     public String getUsername(HttpSession session) {
  35.         String username = (String) session.getAttribute("user");
  36.         return username;
  37.     }
  38. }
复制代码
Result
  1. package com.itheima.pojo;
  2. import lombok.Data;
  3. /**
  4. * @version v1.0
  5. * @ClassName: Result
  6. * @Description: 用来封装http请求的响应数据
  7. * @Author: 黑马程序员
  8. */
  9. @Data
  10. public class Result {
  11.     private boolean flag;
  12.     private String message;
  13. }
复制代码
User
  1. package com.itheima.pojo;
  2. import lombok.Data;
  3. /**
  4. * @version v1.0
  5. * @ClassName: User
  6. * @Description: 接收登录请求的数据
  7. * @Author: 黑马程序员
  8. */
  9. @Data
  10. public class User {
  11.     private String userId;
  12.     private String username;
  13.     private String password;
  14. }
复制代码
MessageUtils
  1. package com.itheima.utils;
  2. import com.alibaba.fastjson.JSON;
  3. import com.itheima.ws.pojo.ResultMessage;
  4. /**
  5. * @version v1.0
  6. * @ClassName: MessageUtils
  7. * @Description: 封装json格式消息的工具类
  8. * @Author: 黑马程序员
  9. */
  10. public class MessageUtils {
  11.     public static String getMessage(boolean isSystemMessage,String fromName, Object message) {
  12.         ResultMessage result = new ResultMessage();
  13.         result.setSystem(isSystemMessage);
  14.         result.setMessage(message);
  15.         if(fromName != null) {
  16.             result.setFromName(fromName);
  17.         }
  18.         return JSON.toJSONString(result);
  19.     }
  20. }
复制代码
Message
  1. package com.itheima.ws.pojo;
  2. import lombok.Data;
  3. /**
  4. * @version v1.0
  5. * @ClassName: Message
  6. * @Description: 用于封装浏览器发送给服务端的消息数据
  7. * @Author: 黑马程序员
  8. */
  9. @Data
  10. public class Message {
  11.     private String toName;
  12.     private String message;
  13. }
复制代码
ResultMessage
  1. package com.itheima.ws.pojo;
  2. import lombok.Data;
  3. /**
  4. * @version v1.0
  5. * @ClassName: ResultMessage
  6. * @Description: 用来封装服务端给浏览器发送的消息数据
  7. * @Author: 黑马程序员
  8. */
  9. @Data
  10. public class ResultMessage {
  11.     private boolean isSystem;
  12.     private String fromName;
  13.     private Object message;//如果是系统消息是数组
  14. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

大连密封材料

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表