SpringBoot + WebSocket achieve simple chat room

①: What is WebSocket?

WebSocket protocol is a full duplex communication over a single TCP connection

According to this definition there are two important considerations:

1. What is the protocol?

Agreement is the computer communicate with each other both parties must abide by a set of conventions.

Difference 2. WebSocket protocol and HTTP protocol?

1) HTTP protocol based on the TCP protocol, a link must be established in order to send a message through the three-way handshake.

2) http links into short links, long link, the link is a short three-way handshake each request to send their information. That is, each corresponding to a request response. Long link link is maintained within a certain period of time. Continue to keep the TCP connection open. The client communicates with the server, the client must be initiated by server then returns the result. The client is active, the server is passive.

3) WebSocket he is to address the client to initiate multiple http requests to the server resources browser must go through a long period of problems in rotation born, he realized multiplexing, he is a full-duplex communication. Customer and end the browser may transmit information simultaneously at webSocket protocol.

②: Use Based WebSocket STOMP protocol + Springboot simple chat room
1. Preparation of configuration files
@Configuration
@EnableWebSocketMessageBroker   //通过此注解开启 WebSocket 消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        //设置消息代理的前缀
        // 如果消息的前缀是 /topic 就会将消息转发给消息代理(broker)再由消息代理转发给所有连接的客户端
        config.enableSimpleBroker("/topic");    //客户端接收服务端消息的地址前缀

        //配置一个或多个前缀,通过这些前缀过滤出需要被注解方法处理的消息。
        // 例如前缀为"/app"的 destination 可以通过 @MessageMapping 注解的方法处理,而其他 destination("/topic","/query") 将被直接交给 broker 处理
        config.setApplicationDestinationPrefixes("/app");   //客户端给服务端发消息的地址前缀
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //定义一个前缀为 "chat" 的endpoint,并开启 sockJs支持。
        // sockJs 可以解决对 WebSocket 的兼容性问题,客户端将通过这里配置的 url 建立 WebSocket 连接
        registry.addEndpoint("/chat").withSockJS();
    }
}
2. Write Controller
@Controller
public class GreetingController {

    /**
     * 执行步骤:
     *  1,由 WebSocketConfig 中的配置,@MessageMapping 注解接收 "/app/hello" 路径发来的消息
     *  2,注解方法对消息进行处理后,将消息转发到 @SendTo 定义的路径上
     *  3,@SendTo 定义的路径是一个前缀为 "/topic" 的路径,由配置文件,此消息将被交给消息代理 broker,由 broker 进行广播
     * @param message
     * @return
     */
    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Message greeting(Message message) {
        return message;
    }
}
3.html Code
<!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>
    <script src="/app.js"></script>
</head>
<body>
    <div>
        <label for="name">请输入用户名</label>
        <input type="text" id="name" placeholder="用户名">
    </div>
    <div>
        <button id="connect"type="button">连接</button>
        <button id="disconnect"type="button" disabled="disabled">断开连接</button>
    </div>
    <div id="chat" style="display: none;">
        <div>
            <label for="name">请输入聊天内容</label>
            <input type="text" id="content" placeholder="聊天内容">
        </div>
        <button id="send" type="button">发送</button>
        <div id="greetings">
            <div id="conversation" style="display: none">群聊进行中</div>
        </div>
    </div>
</body>
</html>
4. js file
var stompClient = null;
//页面显示设置
function setConnected(connected) {
    $("#connect").prop("disabled", connected);
    $("#disconnect").prop("disabled", !connected);
    if (connected) {
        $("#conversation").show();
        $("#chat").show();
    } else {
        $("#conversation").hide();
        $("#chat").hide();
    }
    $("#greeting").html("");
}

//建立一个 WebSocket 连接,建立连接之前必须输入用户名
function connect() {
    if (!$("#name").val()) {
        return;
    }
    //创建一个 SockeJS 实例
    var socket = new SockJS('/chat');
    //使用stomp.over方式创建一个stompClient,完成客户端的创建。
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        //进行页面设置
        setConnected(true);
        //使用 subscribe 方法订阅服务端发送回来的消息,并将服务端发送的消息展示出来
        stompClient.subscribe('/topic/greetings', function (greetings) {
            showGreeting(JSON.parse(greetings.body));
        })
    })
}

//断开 WebSocket 连接
function disconnect() {
    if (stompClient != null) {
        stompClient.disconnect();
    }
    setConnected(false)
}

//发送信息
function sendName() {
    stompClient.send("/app/hello", {},
        JSON.stringify({'name': $('#name').val(), 'content': $('#content').val()}));
}

//展示信息
function showGreeting(message) {
    $('#greetings')
        .append("<div>" + message.name + ":" + message.content + "</div>")
}

$(function () {
    //建立连接
    $("#connect").click(function () {
        connect();
    });
    //断开连接
    $("#disconnect").click(function () {
        disconnect();
    });
    //发送信息
    $("#send").click(function () {
        sendName();
    });
})
5. Notes

. 1) dependent error introduced maven (maven central repository try to copy dependent)

2) introducing stomp protocol

    使用STOMP的好处在于,它完全就是一种消息队列模式,你可以使用生产者与消费者的思想来认识它,发送消息的是生产者,接收消息的是消费者。而消费者可以通过订阅不同的destination,来获得不同的推送消息,不需要开发人员去管理这些订阅与推送目的地之前的关系。

Case see spring's official website there is a simple springboot of Stomp-demo , if it is based on springboot, we can try to write a simple demo based on spring tutorial above.

③: another way to achieve bulk message
Controller
    /**
     *  1. @MessageMapping("/hello") Spring提供一个 @MessageMapping 注解实现了对 WebScoket 的封装
     *  2. SimpMessagingTemplate 是 Spring-WebSocket 内置的一个消息发送的工具
     * @param message
     * @throws Exception
     */
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;
    @MessageMapping("/hello")
    public void  greeting(Message message) throws Exception{
        //使用这个方法进行消息的转发发送
        simpMessagingTemplate.convertAndSend("/topic/greetings",message);
    }
④: point to point communication

Just realize the function of bulk messaging, private chat Here's how to look is achieved. Peer communication need to configure multiple users, we add two users with SpringSecurity.

1. Add SpringSecurity dependent
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
2. SpringSecurity profile
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                //添加两个用户 admin,sang,密码设为123。
                .withUser("admin")
                .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
                .roles("admin")
                .and()
                .withUser("sang")
                .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq")
                .roles("user");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .formLogin().permitAll();
    }
}
3. Modify the WebSocket profile
@Configuration
@EnableWebSocketMessageBroker   //通过此注解开启 WebSocket 消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {

        //客户端接收服务端消息的地址前缀
        //在群发的基础上,添加一个客户端接收地址的前缀。
        config.enableSimpleBroker("/topic","/queue");   

        //客户端给服务端发消息的地址前缀
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //定义一个前缀为 "chat" 的endpoint,并开启 sockJs支持。
        // sockJs 可以解决对 WebSocket 的兼容性问题,客户端将通过这里配置的 url 建立 WebSocket 连接
        registry.addEndpoint("/chat").withSockJS();
    }
}
4. Modify the controller
@Controller
public class GreetingController {

    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;

    //群发消息使用 @SendTo 注解
    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Message greeting(Message message) throws Exception{
        return message;
    }

    //点对点发送消息使用 SimpMessagingTemplate 实现
    @MessageMapping("/chat")    //来自 "/app/chat" 的消息将会被此方法处理
    public void chat(Principal principal, Chat chat)throws Exception{
        String from = principal.getName();
        chat.setFrom(from);
        simpMessagingTemplate.convertAndSendToUser(chat.getTo(),"/queue/chat",chat);
    }

}
5. onlinechat.html
<!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>
    <script src="/chat.js"></script>
</head>
<body>

    <div>
        <div id="chatsContent"></div>
        <div>
            请输入聊天内容:<input type="text" id="content" placeholder="聊天内容">
            目标用户:<input type="text" id="to" placeholder="目标用户">
            <button type="button" id="send">发送</button>
        </div>
    </div>

</body>
</html>
6. chat.js
var stompClient = null;

//建立一个 WebSocket 连接,建立连接之前必须输入用户名
function connect() {

    //创建一个 SockeJS 实例
    var socket = new SockJS('/chat');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function (frame) {
        //使用 subscribe 方法订阅服务端发送回来的消息,并将服务端发送的消息展示出来
        stompClient.subscribe('/user/queue/chat', function (chat) {
            showGreeting(JSON.parse(chat.body));
        })
    })
}

//发送信息
function sendMsg() {
    stompClient.send("/app/chat", {},
        JSON.stringify({'content': $('#content').val(), 'to': $('#to').val()}));
}

//展示信息
function showGreeting(message) {
    $('#chatsContent')
        .append("<div>" + message.from + ":" + message.content + "</div>")
}

$(function () {
   connect();
   $('#send').click(function () {
       sendMsg();
   });
})
7. The directory structure and the demonstration effect

Please use the same browser or a different user logs on different browsers presentation when the demonstration effect

SpringBoot + WebSocket achieve simple chat room
SpringBoot + WebSocket achieve simple chat room
SpringBoot + WebSocket achieve simple chat room

Guess you like

Origin blog.51cto.com/13416247/2439229