zoukankan      html  css  js  c++  java
  • 微服务-springboot+websocket在线聊天室

    一.引入依赖

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
            </dependency>

    二.注入ServerEndpointExporter

    编写一个WebSocketConfig配置类,注入对象ServerEndpointExporter,
    这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.socket.server.standard.ServerEndpointExporter;
    
    /**
     * @Description: 编写一个WebSocketConfig配置类,注入对象ServerEndpointExporter,
     *      这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     */
    @Configuration
    public class WebSocketConfig {
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    }
    
    

    三.websocket的具体实现类

    使用springboot的唯一区别是要@Component声明下,而使用独立容器是由容器自己管理websocket的,
    但在springboot中连容器都是spring管理的。
    虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,
    所以可以用一个静态set保存起来。
    import org.springframework.stereotype.Component;
    
    import javax.websocket.*;
    import javax.websocket.server.ServerEndpoint;
    import java.util.concurrent.CopyOnWriteArraySet;
    
    /**
     *  * @Description: websocket的具体实现类
     *  * 使用springboot的唯一区别是要@Component声明下,而使用独立容器是由容器自己管理websocket的,
     *  * 但在springboot中连容器都是spring管理的。
     *     虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,
     *     所以可以用一个静态set保存起来。
    */
    @ServerEndpoint(value = "/websocket")
    @Component
    public class MyWebSocket {
        //用来存放每个客户端对应的MyWebSocket对象。
        private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
    
        //与某个客户端的连接会话,需要通过它来给客户端发送数据
        private Session session;
    
        /**
         * 连接建立成功调用的方法
         */
        @OnOpen
        public void onOpen(Session session) {
            this.session = session;
            webSocketSet.add(this);     //加入set中
            System.out.println("有新连接加入!当前在线人数为" + webSocketSet.size());
            //群发消息,告诉每一位
            broadcast(session.getId()+"连接上WebSocket-->当前在线人数为:"+webSocketSet.size());
        }
    
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose() {
            webSocketSet.remove(this);  //从set中删除
            System.out.println("有一连接关闭!当前在线人数为" + webSocketSet.size());
    
            //群发消息,告诉每一位
            broadcast(session.getId()+"下线,当前在线人数为:"+webSocketSet.size());
        }
    
        /**
         * 收到客户端消息后调用的方法
         *
         * @param message 客户端发送过来的消息
         * */
        @OnMessage
        public void onMessage(String message, Session session) {
            System.out.println("来自客户端的消息:" + message);
            //群发消息
            broadcast(message);
        }
    
        /**
         * 发生错误时调用
         *
         */
        @OnError
        public void onError(Session session, Throwable error) {
            System.out.println("发生错误");
            error.printStackTrace();
        }
    
        /**
         * 群发自定义消息
         * */
        public  void broadcast(String message){
            for (MyWebSocket item : webSocketSet) {
                //同步异步说明参考:http://blog.csdn.net/who_is_xiaoming/article/details/53287691
                //this.session.getBasicRemote().sendText(message);
                item.session.getAsyncRemote().sendText(message);//异步发送消息.
            }
        }
    }

    四.编写index.ftl

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <title>Spring Boot Demo - FreeMarker</title>
    
        <link href="/css/index.css" rel="stylesheet" />
        <script src="/js/index.js"></script>
    </head>
    <body>
        <button onclick="connectWebSocket()">连接WebSocket</button>
        <button onclick="closeWebSocket()">断开连接</button>
        <hr />
        <br />
        消息:<input id="text" type="text" />
        <button onclick="send()">发送消息</button>
        <div id="message"></div>
    </body>
    </html>

    五.编写index.js

    var websocket = null;
    function connectWebSocket(){
        //判断当前浏览器是否支持WebSocket
        //判断当前浏览器是否支持WebSocket
        if ('WebSocket'in window) {
            websocket = new WebSocket("ws://localhost:8080/websocket");
        } else {
            alert('当前浏览器不支持websocket');
        }
    
        //连接发生错误的回调方法
        websocket.onerror = function() {
            setMessageInnerHTML("error");
        };
    
        //接收到消息的回调方法
        websocket.onmessage = function(event) {
            setMessageInnerHTML(event.data);
        }
    
        //连接关闭的回调方法
        websocket.onclose = function() {
            setMessageInnerHTML("Loc MSG:关闭连接");
        }
    
        //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
        window.onbeforeunload = function() {
            websocket.close();
        }
    }
    
    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }
    
    //关闭连接
    function closeWebSocket() {
        websocket.close();
    }
    
    //发送消息
    function send() {
        var message = document.getElementById('text').value;
        websocket.send(message);
    }

    六.index.css

    #message{
        margin-top:40px;
        border:1px solid gray;
        padding:20px;
    }

    七.controller

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("web")
    public class WebController {
    
        @RequestMapping("index")
        public String index(){
            return "index";
        }
    }

    测试:http://localhost:8080/web/index

    点击【连接WebSocket,然后就可以发送消息了。

    打开另外一个浏览器或者直接打开一个TAB访问地址http://localhost:8080/web/index

    点击【连接WebSocket,然后就可以发送消息了。

    观察两边的信息打印,看是否可以接收到消息。

    源码:下载地址

    单聊

    一.创建消息对象socketMsg

    public class SocketMsg {
        private int type;//聊天类型0:群聊,1:单聊
        private String fromuUser://发送者
        private String toUser;//接收者
        private String msg;//发送的消息
    
        public int getType() {
            return type;
        }
    
        public void setType(int type) {
            this.type = type;
        }
    
        public String getFromuUser() {
            return fromuUser;
        }
    
        public void setFromuUser(String fromuUser) {
            this.fromuUser = fromuUser;
        }
    
        public String getToUser() {
            return toUser;
        }
    
        public void setToUser(String toUser) {
            this.toUser = toUser;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }

    二.调整建立连接的方法(MyWebSocket):

    这里主要是要使用一个map对象保存频道号和session之前的关系,之后就可以通过频道号获取session,然后使用session进行消息的发送。

     

    //用来记录sessionId和该session进行绑定
        private static ConcurrentHashMap<String,Session> map = new ConcurrentHashMap<String, Session>();

     

    三.修改连接的方法onOpen:

    在建立连接的时候,就保存频道号(这里使用的是session.getId()作为频道号)

    /**
         * 连接建立成功调用的方法
         */
        @OnOpen
        public void onOpen(Session session) {
            this.session = session;
            map.put(session.getId(),session);
    
            webSocketSet.add(this);     //加入set中
            System.out.println("有新连接加入:"+session.getId()+"!当前在线人数为" + webSocketSet.size());
            //群发消息,告诉每一位
            broadcast(session.getId()+"连接上WebSocket-->当前在线人数为:"+webSocketSet.size());
        }

    三.修改消息发送的方法onMessage:

     /**
         * 收到客户端消息后调用的方法
         *
         * @param message 客户端发送过来的消息
         * */
        @OnMessage
        public void onMessage(String message, Session session) {
            System.out.println("来自客户端的消息:" + session.getId() + ":" + message);
    
            //从客户端传过来的数据是json数据,所以这里使用jackson进行转换为SocketMsg对象,
            // 然后通过socketMsg的type进行判断是单聊还是群聊,进行相应的处理:
            ObjectMapper objectMapper = new ObjectMapper();
            SocketMsg socketMsg;
            try {
                socketMsg = objectMapper.readValue(message, SocketMsg.class);
                if(socketMsg.getType() == 1){
                    //单聊
                    socketMsg.setFromUser(session.getId());//发送者.
                    Session fromSession = map.get(socketMsg.getFromUser());
                    Session toSession = map.get(socketMsg.getToUser());
                    if(toSession != null){
                        //发送给发送者和接受者.
                        fromSession.getAsyncRemote().sendText(session.getId()+":"+socketMsg.getMsg());
                        toSession.getAsyncRemote().sendText(session.getId()+":"+socketMsg.getMsg());
                    }else{
                        //发送给发送者
                        fromSession.getAsyncRemote().sendText("系统消息:对方已下线");
                    }
                }else{
                    //群聊
                    broadcast(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    四.页面调整

    消息:<input id="text" type="text" />
        接收者:<input id="toUser" type="text" />
        <button onclick="send()">发送消息</button>

    五.js调整

    //发送消息
    function send() {
        var message = document.getElementById('text').value;
        var toUser = document.getElementById('toUser').value;
        var socketMsg = {msg:message,toUser:toUser};
        if(toUser == ''){
            //群聊.
            socketMsg.type = 0;
        }else{
            //单聊.
            socketMsg.type = 1;
        }
        websocket.send(JSON.stringify(message));
    }

    运行测试:

    2的消息:

     

    1的消息:

    0的消息:

    源码:下载地址

  • 相关阅读:
    opencv出现问题:/usr/lib/x86_64-linux-gnu/libpng16.so.16: undefined reference to `inflateValidate@ZLIB_1.2.9'
    SCI-HUB 解锁论文的正确姿势——如何免费下载论文
    VS2019 实现与 Linux服务器同步
    VS Code 写代码实时同步服务器【Sftp插件】
    Docker实用技巧(二):容器与主机之间复制文件
    Docker实用技巧(一):镜像的备份/保存/加载/删除
    mycat注解及高可用(三)
    mycat分片及主从(二)
    SVN中trunk,branches,tags用法详解
    寄存器的英文全称
  • 原文地址:https://www.cnblogs.com/qjm201000/p/10223640.html
Copyright © 2011-2022 走看看