最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

全球微资讯!websocket

来源:博客园

1. WebSocket介绍

  • WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。


    (资料图片仅供参考)

  • WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

  • HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。

  • 这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。

  • 这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步 AJAX 请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。

2. websocket协议

  • 本协议有两部分:握手和数据传输。

  • 握手是基于http协议的。

  1. 客户端(浏览器)实现

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.首先导入依赖

            org.springframework.boot        spring-boot-starter-websocket    
Message
@Data@AllArgsConstructor@NoArgsConstructorpublic 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@NoArgsConstructorpublic class Result{    /**     * 状态码     */    private Integer code;    /**     * 提示信息,如果有错误时,前端可以获取该字段进行提示     */    private boolean flag;    private String message;    private T data; //数据    public static  Result success(T object) {        Result r = new Result();        r.data = object;        r.code = 1;        r.flag = true;        return r;    }    public static  Result error(String message) {        Result r = new Result();        r.message = message;        r.code = 0;        r.flag = false;        return r;    }}
登录
@PostMapping("/login")    public Result login(@RequestBody User user ,HttpSession session){        String password = user.getPassword();        String username = user.getUsername();        log.info("用户登录操作");         //MD5加密        password = DigestUtils.md5DigestAsHex(password.getBytes());        //根据用户名查找数据库        LambdaQueryWrapper 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@EnableWebSocketMessageBrokerpublic 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@Slf4jpublic class ChatEndpoint {    private Session session;    private static HttpSession httpSession;    //用来存储每一个客户端对象对应的ChatEndpoint对象    private static final Map 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 set = onlineUsers.keySet();        return set;    }    /**     * 发给所有人的广播     * @param message     */    private void broadcastAllUsers(String message){//        拿到所有的用户的chatEndpoint对象  //存储用户的session信息        Set> entries = onlineUsers.entrySet();        //遍历map集合        for (Map.Entry 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> friendLists = (Map>) httpSession.getAttribute("friendLists");                List 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);   }

关键词: