SpringBoot整合WebSocket

Lou.Chen
大约 6 分钟

SpringBoot整合WebSocket

一、springboot实现在线群聊

1、pom.xml

主要的后端依赖

spring-boot-starter-websocket

前端依赖

sockjs-client

jquery

stomp-websocket

webjars-locator-core

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.lc</groupId>
    <artifactId>websocket1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>websocket1</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <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>

        <!--前端websocket客户端依赖-->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>sockjs-client</artifactId>
            <version>1.1.2</version>
        </dependency>

        <!--jquery-->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.4.1</version>
        </dependency>

        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>stomp-websocket</artifactId>
            <version>2.3.3</version>
        </dependency>

        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>webjars-locator-core</artifactId>
        </dependency>


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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2、消息bean
@Getter
@Setter
public class Message {
    private String name;
    private String content;
}
3、websocket代理服务器
/**
 * 实现websocket消息代理人配置器
 */
@Configuration
//开启消息代理
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
//        消息代理的前缀
        registry.enableSimpleBroker("/topic");
        registry.setApplicationDestinationPrefixes("/app");
    }

    /**
     * 建立连接点
     * @param registry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/chat").withSockJS();
    }
}
4、消息请求处理controller
@Controller
public class GreetingController {

//    处理客户端请求消息的路径
    //客户端发送消息的路径  /app/hello
    @MessageMapping("/hello") 
//    服务器广播的路径(/topic/greetings) 由配置的topic前缀的服务去处理
    //客户端订阅消息的路径  /topic/greeting
    @SendTo("/topic/greetings")
    public Message greeting(Message message) {
        return message;
    }

}

5、聊天页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>群聊</title>
    <script src="/webjars/jquery/jquery.min.js"></script>
    <script src="/webjars/sockjs-client/sockjs.min.js"></script>
    <script src="/webjars/stomp-websocket/stomp.min.js"></script>
</head>
<body>

    <table>
        <tr>
            <td>请输入用户名</td>
            <td><input type="text" id="name"/></td>
        </tr>
        <tr>
            <td><input type="button" id="connect" value="连接"/></td>
            <td><input type="button" id="disconnect" value="断开连接" disabled="disabled"/></td>
        </tr>
    </table>

    <!--display:none 此块不会显示-->
    <div id="chat" style="display: none;">
            <table>
                <tr>请输入聊天内容</tr>
                <td><input type="text" id="content"/></td>
                <td><input type="button" id="send" value="发送"></td>
            </table>
        <div id="conversation">群聊进行中....</div>
    </div>

<script>
    $(function () {
        //点击连接
        $("#connect").click(function () {
            connect()
        });
    //    断开连接
        $("#disconnect").click(function () {
            if(stompClient!=null){
                //直接断开连接
                stompClient.disconnect();
            }
            setConnect(false);
        });

        $("#send").click(function () {
            stompClient.send('/app/hello',{},JSON.stringify({'name':$("#name").val(),'content':$("#content").val()}))
        })
    })

    var stompClient=null;

    //尝试连接
    function connect() {
        //如果没有输入用户名 直接返回
        if(!$("#name").val()){
            alert("请输入用户名")
            return;
        }
        //建立连接点
        var socket=new SockJS('/chat');
        stompClient=Stomp.over(socket);
        stompClient.connect({},function (success) {
            //连接成功的回调
            //建立连接 (界面的处理)
            setConnect(true);
        //    订阅服务器的消息(后端服务服务器广播消息的路径)
            stompClient.subscribe('/topic/greetings',function (msg) {
                //服务端的msg的具体消息 在body属性中
                //将字符串消息转为json对象
                showGreeting(JSON.parse(msg.body))
            })
        },function () {
        //    连接失败的回调
        })
    }

    //展示消息
    function showGreeting(msg) {
        //向聊天div中追加内容
        $("#conversation").append('<div>'+msg.name+":"+msg.content+'</div>')
    }

    //建立连接
    function setConnect(flag) {
        //prop() 方法设置或返回被选元素的属性和值。
        //如果连接成功 则设置 '连接' 按钮不可用
        $("#connect").prop("disabled",flag);
        //断开连接按钮 可用
        $("#disconnect").prop("disabled",!flag);
        if (flag) {
            //显示 聊天界面
            $("#chat").show();
        }else{
            $("#chat").hide();
        }
    }
</script>

</body>
</html>

二、 springboot实现在线单聊

1、pom.xml

主要的后端依赖

spring-boot-starter-websocket

spring-boot-starter-security

前端依赖

sockjs-client

jquery

stomp-websocket

webjars-locator-core

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.lc</groupId>
    <artifactId>websocket1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>websocket1</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

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

        <!--前端websocket客户端依赖-->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>sockjs-client</artifactId>
            <version>1.1.2</version>
        </dependency>

        <!--jquery-->
        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>jquery</artifactId>
            <version>3.4.1</version>
        </dependency>

        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>stomp-websocket</artifactId>
            <version>2.3.3</version>
        </dependency>

        <dependency>
            <groupId>org.webjars</groupId>
            <artifactId>webjars-locator-core</artifactId>
        </dependency>



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

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2、消息bean
@Getter
@Setter
public class Chat {
    /**
     * 消息从哪来
     */
    private String from;
    /**
     * 消息内容
     */
    private String content;
    /**
     * 消息发给谁
     */
    private String to;
}
3、websocket代理服务器
/**
 * 实现websocket消息代理人配置器
 */
@Configuration
//开启消息代理
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
//        消息代理的前缀
//        在这里我们定义 /topic为群聊消息   /queue为单聊消息
        registry.enableSimpleBroker("/topic","/queue");
//        定义程序的发送目标前缀
        registry.setApplicationDestinationPrefixes("/app");
    }

    /**
     * 建立连接点
     * @param registry
     */
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/chat").withSockJS();
    }
}
4、security配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("lc")
                .password("123")
                .roles("admin")
                .and()
                .withUser("zs")
                .password("123")
                .roles("user");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().
                anyRequest().authenticated()
                .and()
                .formLogin()
//                允许所有登录请求
                .permitAll();
    }
}
5、消息请求处理controller
@Controller
public class GreetingController {

//    //    处理客户端请求消息的路径
//    @MessageMapping("/hello")
////    服务器广播的路径(/topic/greetings) 由配置的topic前缀的服务去处理
//    @SendTo("/topic/greetings")
//    public Message greeting(Message message) {
//        return message;
//    }

    /**
     * 使用消息发送模板
     */
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    @MessageMapping("/hello")
    public void greeting(Message message) {
//        直接指定发送的路径和消息
        simpMessagingTemplate.convertAndSend("/topic/greetings",message);
    }

    /**
     * 单聊
     * @param principal 当前登录的用户信息
     * @param chat
     */
    @MessageMapping("/chat")
    public void chat(Principal principal, Chat chat) {
        //谁发的消息 (指定发送人)
        chat.setFrom(principal.getName());
//        指定发送人 ,发送的路径 ,发送的消息
        simpMessagingTemplate.convertAndSendToUser(chat.getTo(), "/queue/chat", chat);
    }

}

6、聊天页面

注意:

 //    注意在单聊是 发送给指定用户,那么websocket服务端自动帮我们加 /user     所以这里我们也要加 /user
     stompClient.subscribe('/user/queue/chat', function (msg) {
           //服务端的msg的具体消息 在body属性中
           //将字符串消息转为json对象
           showGreeting(JSON.parse(msg.body))
      })
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>单聊</title>
    <script src="/webjars/jquery/jquery.min.js"></script>
    <script src="/webjars/sockjs-client/sockjs.min.js"></script>
    <script src="/webjars/stomp-websocket/stomp.min.js"></script>
</head>
<body>
<input type="button" id="connect" value="连接"/>
<input type="button" id="disconnect" value="断开连接" disabled="disabled"/>
<hr>
<table>
    <tr>
        <td>消息内容:</td>
        <td><input type="text" id="content"></td>
    </tr>
    <tr>
        <td>目标用户:</td>
        <td><input type="text" id="to"></td>
    </tr>
    <tr>
        <td><input type="button" value="发送" id="send"></td>

    </tr>
</table>
<div id="chat" style="display: none">
    <div id="conversation">连接进行中...</div>
</div>
<script>
    $(function () {
        //点击连接
        $("#connect").click(function () {
            connect()
        });
        //    断开连接
        $("#disconnect").click(function () {
            if (stompClient != null) {
                //直接断开连接
                stompClient.disconnect();
            }
            setConnect(false);
        });

        //点击发送
        $("#send").click(function () {
            stompClient.send('/app/chat', {}, JSON.stringify({
                'to': $("#to").val(),
                'content': $("#content").val()
            }))
        })
    })

    var stompClient = null;

    //尝试连接
    function connect() {
        // 创建连接点
        var socket = new SockJS('/chat');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function (success) {
            //连接成功的回调
            //建立连接 (界面的处理)
            setConnect(true);
            //    订阅服务器的消息
            //    注意在单聊是 发送给指定用户,那么websocket服务端自动帮我们加 /user     所以这里我们也要加 /user
            stompClient.subscribe('/user/queue/chat', function (msg) {
                //服务端的msg的具体消息 在body属性中
                //将字符串消息转为json对象
                showGreeting(JSON.parse(msg.body))
            })
        }, function () {
            //    连接失败的回调
        })
    }

    //展示消息
    function showGreeting(msg) {
        //向聊天div中追加内容
        $("#conversation").append('<div>' + msg.from + ":" + msg.content + '</div>')
    }

    //建立连接
    function setConnect(flag) {
        //prop() 方法设置或返回被选元素的属性和值。
        //如果连接成功 则设置 '连接' 按钮不可用
        $("#connect").prop("disabled", flag);
        //断开连接按钮 可用
        $("#disconnect").prop("disabled", !flag);
        if (flag) {
            //显示 聊天界面
            $("#chat").show();
        } else {
            $("#chat").hide();
        }
    }
</script>

</body>
</html>