Spring Boot中使用WebSocket 【第一部分】

原文鏈接:https://www.zifangsky.cn/1355.html

簡介

所謂WebSocket, 類似於Socket,它的作用是可以讓Web應用中的客戶端和服務端建立全雙工通信。在基於Spring的應用中使用WebSocket一般可以有以下三種方式:

  • 使用Java提供的@ServerEndpoint註解實現
  • 使用Spring提供的低層級WebSocket API實現
  • 使用STOMP消息實現

下面,我將對這三種實現方式做一個簡單介紹,此外有關WebSocket性質的更多介紹可以參考以下這篇文章:WebSocket探祕

注:本篇文章的完整源碼可以參考:https://github.com/zifangsky/WebSocketDemo

使用Java提供的@ServerEndpoint註解實現

(1)使用@ServerEndpoint註解監聽一個WebSocket請求路徑:

這裏監聽了客戶端的連接端口/reverse,並定義瞭如何處理客戶端發來的消息

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package cn.zifangsky.samplewebsocket.websocket;

 

import javax.websocket.OnMessage;

import javax.websocket.Session;

import javax.websocket.server.ServerEndpoint;

import java.io.IOException;

 

/**

* ReverseWebSocketEndpoint

*

* @author zifangsky

* @date 2018/9/30

* @since 1.0.0

*/

@ServerEndpoint("/reverse")

public class ReverseWebSocketEndpoint {

 

    @OnMessage

    public void handleMessage(Session session, String message) throws IOException {

        session.getBasicRemote().sendText("Reversed: " + new StringBuilder(message).reverse());

    }

 

}

 

(2)WebSocket相關配置:

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

package cn.zifangsky.samplewebsocket.config;

 

import cn.zifangsky.samplewebsocket.websocket.ReverseWebSocketEndpoint;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.socket.config.annotation.EnableWebSocket;

import org.springframework.web.socket.server.standard.ServerEndpointExporter;

 

/**

* WebSocket相關配置

*

* @author zifangsky

* @date 2018/9/30

* @since 1.0.0

*/

@Configuration

@EnableWebSocket

public class WebSocketConfig{

 

    @Bean

    public ReverseWebSocketEndpoint reverseWebSocketEndpoint() {

        return new ReverseWebSocketEndpoint();

    }

 

    @Bean

    public ServerEndpointExporter serverEndpointExporter() {

        return new ServerEndpointExporter();

    }

 

}

 

(3)示例頁面:

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

<html xmlns:th="http://www.thymeleaf.org">

<head>

    <meta content="text/html;charset=UTF-8"/>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>

    <meta name="viewport" content="width=device-width, initial-scale=1"/>

    <title>WebSocket Examples: Reverse</title>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

    <script th:src="@{/layui/layui.js}"></script>

    <link th:href="@{/layui/css/layui.css}" rel="stylesheet">

    <style type="text/css">

        #connect-container {

            margin: 0 auto;

            width: 400px;

        }

 

        #connect-container div {

            padding: 5px;

            margin: 0 7px 10px 0;

        }

 

        .layui-btn {

            display: inline-block;

        }

    </style>

    <script type="text/javascript">

        var ws = null;

 

        $(function () {

            var target = $("#target");

            if (window.location.protocol === 'http:') {

                target.val('ws://' + window.location.host + target.val());

            } else {

                target.val('wss://' + window.location.host + target.val());

            }

        });

 

        function setConnected(connected) {

            var connect = $("#connect");

            var disconnect = $("#disconnect");

            var reverse = $("#reverse");

 

            if (connected) {

                connect.addClass("layui-btn-disabled");

                disconnect.removeClass("layui-btn-disabled");

                reverse.removeClass("layui-btn-disabled");

            } else {

                connect.removeClass("layui-btn-disabled");

                disconnect.addClass("layui-btn-disabled");

                reverse.addClass("layui-btn-disabled");

            }

 

            connect.attr("disabled", connected);

            disconnect.attr("disabled", !connected);

            reverse.attr("disabled", !connected);

        }

 

        //連接

        function connect() {

            var target = $("#target").val();

 

            ws = new WebSocket(target);

            ws.onopen = function () {

                setConnected(true);

                log('Info: WebSocket connection opened.');

            };

            ws.onmessage = function (event) {

                log('Received: ' + event.data);

            };

            ws.onclose = function () {

                setConnected(false);

                log('Info: WebSocket connection closed.');

            };

        }

 

        //斷開連接

        function disconnect() {

            if (ws != null) {

                ws.close();

                ws = null;

            }

            setConnected(false);

        }

 

        //文字反轉

        function reverse() {

            if (ws != null) {

                var message = $("#message").val();

                log('Sent: ' + message);

                ws.send(message);

            } else {

                alert('WebSocket connection not established, please connect.');

            }

        }

 

        //日誌輸出

        function log(message) {

            console.debug(message);

        }

    </script>

</head>

<body>

    <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being

        enabled. Please enable

        Javascript and reload this page!</h2></noscript>

    <div>

        <div id="connect-container" class="layui-elem-field">

            <legend>Reverse</legend>

            <div>

                <input id="target" type="text" class="layui-input" size="40" style="width: 350px" value="/reverse"/>

            </div>

            <div>

                <button id="connect" class="layui-btn layui-btn-normal" οnclick="connect();">Connect</button>

                <button id="disconnect" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled"

                        οnclick="disconnect();">Disconnect

                </button>

 

            </div>

            <div>

                <textarea id="message" class="layui-textarea" placeholder="請輸入需要反轉的內容" style="width: 350px"></textarea>

            </div>

            <div>

                <button id="reverse" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled"

                        οnclick="reverse();">Reverse message

                </button>

            </div>

        </div>

    </div>

</body>

</html>

啓動項目後訪問頁面,效果如下:

使用Spring提供的低層級WebSocket API實現

Spring 4.0爲WebSocket通信提供了支持,包括:

  • 發送和接收消息的低層級API;

  • 發送和接收消息的高級API;

  • 用來發送消息的模板;

  • 支持SockJS,用來解決瀏覽器端、服務器以及代理不支持WebSocket的問題。

使用Spring提供的低層級API實現WebSocket,主要需要以下幾個步驟:

(1)添加一個WebSocketHandler:

定義一個繼承了AbstractWebSocketHandler類的消息處理類,然後自定義對”建立連接“、”接收/發送消息“、”異常情況“等情況進行處理

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

package cn.zifangsky.samplewebsocket.websocket;

 

import cn.zifangsky.samplewebsocket.service.EchoService;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.web.socket.CloseStatus;

import org.springframework.web.socket.TextMessage;

import org.springframework.web.socket.WebSocketSession;

import org.springframework.web.socket.handler.TextWebSocketHandler;

 

import javax.annotation.Resource;

import java.text.MessageFormat;

 

/**

* 通過繼承 {@link org.springframework.web.socket.handler.AbstractWebSocketHandler} 的示例

*

* @author zifangsky

* @date 2018/10/9

* @since 1.0.0

*/

public class EchoWebSocketHandler extends TextWebSocketHandler{

    private final Logger logger = LoggerFactory.getLogger(getClass());

 

    @Resource(name = "echoServiceImpl")

    private EchoService echoService;

 

    @Override

    public void afterConnectionEstablished(WebSocketSession session) throws Exception {

        logger.debug("Opened new session in instance " + this);

    }

 

    @Override

    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {

        //組裝返回的Echo信息

        String echoMessage = this.echoService.echo(message.getPayload());

        logger.debug(MessageFormat.format("Echo message \"{0}\"", echoMessage));

 

        session.sendMessage(new TextMessage(echoMessage));

    }

 

    @Override

    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {

        session.close(CloseStatus.SERVER_ERROR);

        logger.debug("Info: WebSocket connection closed.");

    }

}

 

(2)WebSocket相關配置:

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

package cn.zifangsky.samplewebsocket.config;

 

import cn.zifangsky.samplewebsocket.websocket.EchoWebSocketHandler;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.socket.WebSocketHandler;

import org.springframework.web.socket.config.annotation.EnableWebSocket;

import org.springframework.web.socket.config.annotation.WebSocketConfigurer;

import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

 

/**

* WebSocket相關配置

*

* @author zifangsky

* @date 2018/9/30

* @since 1.0.0

*/

@Configuration

@EnableWebSocket

public class WebSocketConfig implements WebSocketConfigurer{

 

    @Override

    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {

        registry.addHandler(echoWebSocketHandler(), "/echoMessage");

        registry.addHandler(echoWebSocketHandler(), "/echoMessage_SockJS").withSockJS();

    }

 

    /**

     * 通過繼承 {@link org.springframework.web.socket.handler.AbstractWebSocketHandler} 的示例

     */

    @Bean

    public WebSocketHandler echoWebSocketHandler(){

        return new EchoWebSocketHandler();

    }

 

}

從上面代碼可以看出,這裏除了配置了基本的WebSocket(也就是/echoMessage這個連接地址),還使用SockJS配置了瀏覽器不支持WebSocket技術時的替代方案(也就是/echoMessage_SockJS這個連接地址)。

(3)兩個示例頁面:

i)echo.html:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

<html xmlns:th="http://www.thymeleaf.org">

<head>

    <meta content="text/html;charset=UTF-8"/>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>

    <meta name="viewport" content="width=device-width, initial-scale=1"/>

    <title>WebSocket Examples: Reverse</title>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

    <script th:src="@{/layui/layui.js}"></script>

    <link th:href="@{/layui/css/layui.css}" rel="stylesheet">

    <style type="text/css">

        #connect-container {

            margin: 0 auto;

            width: 400px;

        }

 

        #connect-container div {

            padding: 5px;

            margin: 0 7px 10px 0;

        }

 

        .layui-btn {

            display: inline-block;

        }

    </style>

    <script type="text/javascript">

        var ws = null;

 

        $(function () {

            var target = $("#target");

            if (window.location.protocol === 'http:') {

                target.val('ws://' + window.location.host + target.val());

            } else {

                target.val('wss://' + window.location.host + target.val());

            }

        });

 

        function setConnected(connected) {

            var connect = $("#connect");

            var disconnect = $("#disconnect");

            var echo = $("#echo");

 

            if (connected) {

                connect.addClass("layui-btn-disabled");

                disconnect.removeClass("layui-btn-disabled");

                echo.removeClass("layui-btn-disabled");

            } else {

                connect.removeClass("layui-btn-disabled");

                disconnect.addClass("layui-btn-disabled");

                echo.addClass("layui-btn-disabled");

            }

 

            connect.attr("disabled", connected);

            disconnect.attr("disabled", !connected);

            echo.attr("disabled", !connected);

        }

 

        //連接

        function connect() {

            var target = $("#target").val();

 

            ws = new WebSocket(target);

            ws.onopen = function () {

                setConnected(true);

                log('Info: WebSocket connection opened.');

            };

            ws.onmessage = function (event) {

                log('Received: ' + event.data);

            };

            ws.onclose = function () {

                setConnected(false);

                log('Info: WebSocket connection closed.');

            };

        }

 

        //斷開連接

        function disconnect() {

            if (ws != null) {

                ws.close();

                ws = null;

            }

            setConnected(false);

        }

 

        //Echo

        function echo() {

            if (ws != null) {

                var message = $("#message").val();

                log('Sent: ' + message);

                ws.send(message);

            } else {

                alert('WebSocket connection not established, please connect.');

            }

        }

 

        //日誌輸出

        function log(message) {

            console.debug(message);

        }

    </script>

</head>

<body>

    <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being

        enabled. Please enable

        Javascript and reload this page!</h2></noscript>

    <div>

        <div id="connect-container" class="layui-elem-field">

            <legend>Echo</legend>

            <div>

                <input id="target" type="text" class="layui-input" size="40" style="width: 350px" value="/echoMessage"/>

            </div>

            <div>

                <button id="connect" class="layui-btn layui-btn-normal" οnclick="connect();">Connect</button>

                <button id="disconnect" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled"

                        οnclick="disconnect();">Disconnect

                </button>

 

            </div>

            <div>

                <textarea id="message" class="layui-textarea" placeholder="請輸入請求的內容" style="width: 350px"></textarea>

            </div>

            <div>

                <button id="echo" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled"

                        οnclick="echo();">Echo message

                </button>

            </div>

        </div>

    </div>

</body>

</html>

ii)echo_sockjs.html:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

<html xmlns:th="http://www.thymeleaf.org">

<head>

    <meta content="text/html;charset=UTF-8"/>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>

    <meta name="viewport" content="width=device-width, initial-scale=1"/>

    <title>WebSocket Examples: Reverse</title>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js"></script>

    <script th:src="@{/layui/layui.js}"></script>

    <link th:href="@{/layui/css/layui.css}" rel="stylesheet">

    <style type="text/css">

        #connect-container {

            margin: 0 auto;

            width: 400px;

        }

 

        #connect-container div {

            padding: 5px;

            margin: 0 7px 10px 0;

        }

 

        .layui-btn {

            display: inline-block;

        }

    </style>

    <script type="text/javascript">

        var ws = null;

 

        $(function () {

            var target = $("#target");

            if (window.location.protocol === 'http:') {

                target.val('http://' + window.location.host + target.val());

            } else {

                target.val('https://' + window.location.host + target.val());

            }

        });

 

        function setConnected(connected) {

            var connect = $("#connect");

            var disconnect = $("#disconnect");

            var echo = $("#echo");

 

            if (connected) {

                connect.addClass("layui-btn-disabled");

                disconnect.removeClass("layui-btn-disabled");

                echo.removeClass("layui-btn-disabled");

            } else {

                connect.removeClass("layui-btn-disabled");

                disconnect.addClass("layui-btn-disabled");

                echo.addClass("layui-btn-disabled");

            }

 

            connect.attr("disabled", connected);

            disconnect.attr("disabled", !connected);

            echo.attr("disabled", !connected);

        }

 

        //連接

        function connect() {

            var target = $("#target").val();

 

            ws = new SockJS(target);

            ws.onopen = function () {

                setConnected(true);

                log('Info: WebSocket connection opened.');

            };

            ws.onmessage = function (event) {

                log('Received: ' + event.data);

            };

            ws.onclose = function () {

                setConnected(false);

                log('Info: WebSocket connection closed.');

            };

        }

 

        //斷開連接

        function disconnect() {

            if (ws != null) {

                ws.close();

                ws = null;

            }

            setConnected(false);

        }

 

        //Echo

        function echo() {

            if (ws != null) {

                var message = $("#message").val();

                log('Sent: ' + message);

                ws.send(message);

            } else {

                alert('WebSocket connection not established, please connect.');

            }

        }

 

        //日誌輸出

        function log(message) {

            console.debug(message);

        }

    </script>

</head>

<body>

    <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being

        enabled. Please enable

        Javascript and reload this page!</h2></noscript>

    <div>

        <div id="connect-container" class="layui-elem-field">

            <legend>Echo With SockJS</legend>

            <div>

                <input id="target" type="text" class="layui-input" size="40" style="width: 350px" value="/echoMessage_SockJS"/>

            </div>

            <div>

                <button id="connect" class="layui-btn layui-btn-normal" οnclick="connect();">Connect</button>

                <button id="disconnect" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled"

                        οnclick="disconnect();">Disconnect

                </button>

 

            </div>

            <div>

                <textarea id="message" class="layui-textarea" placeholder="請輸入請求的內容" style="width: 350px"></textarea>

            </div>

            <div>

                <button id="echo" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled"

                        οnclick="echo();">Echo message

                </button>

            </div>

        </div>

    </div>

</body>

</html>

具體效果省略,可自行運行源碼查看。

使用STOMP消息實現

所謂STOMP(Simple Text Oriented Messaging Protocol),就是在WebSocket基礎之上提供了一個基於幀的線路格式(frame-based wire format)層。它對發送簡單文本消息定義了一套規範格式(STOMP消息基於Text,當然也支持傳輸二進制數據),目前很多服務端消息隊列都已經支持STOMP,比如:RabbitMQ、 ActiveMQ等。

(1)WebSocket相關配置:

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

package cn.zifangsky.stompwebsocket.config;

 

import cn.zifangsky.stompwebsocket.interceptor.websocket.MyChannelInterceptor;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Configuration;

import org.springframework.messaging.simp.config.ChannelRegistration;

import org.springframework.messaging.simp.config.MessageBrokerRegistry;

import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;

import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

 

/**

* WebSocket相關配置

*

* @author zifangsky

* @date 2018/9/30

* @since 1.0.0

*/

@Configuration

@EnableWebSocketMessageBroker

public class WebSocketConfig implements WebSocketMessageBrokerConfigurer{

    @Autowired

    private MyChannelInterceptor myChannelInterceptor;

 

    @Override

    public void registerStompEndpoints(StompEndpointRegistry registry) {

        registry.addEndpoint("/stomp-websocket").withSockJS();

    }

 

    @Override

    public void configureMessageBroker(MessageBrokerRegistry registry) {

        //客戶端需要把消息發送到/message/xxx地址

        registry.setApplicationDestinationPrefixes("/message");

        //服務端廣播消息的路徑前綴,客戶端需要相應訂閱/topic/yyy這個地址的消息

        registry.enableSimpleBroker("/topic");

    }

 

    @Override

    public void configureClientInboundChannel(ChannelRegistration registration) {

        registration.interceptors(myChannelInterceptor);

    }

 

}

 

從上面代碼可以看出,這裏設置了好幾個地址,簡單解釋如下:

  • 首先註冊了一個名爲/stomp-websocket的端點,也就是STOMP客戶端連接的地址。

  • 此外,定義了服務端處理WebSocket消息的前綴是/message,這個地址用於客戶端向服務端發送消息(比如客戶端向/message/hello這個地址發送消息,那麼服務端通過@MessageMapping(“/hello”)這個註解來接收並處理消息)

  • 最後,定義了一個簡單消息代理,也就是服務端廣播消息的路徑前綴(比如客戶端監聽/topic/greeting這個地址,那麼服務端就可以通過@SendTo(“/topic/greeting”)這個註解向客戶端發送STOMP消息)。

需要注意的是,上面代碼中還添加了一個名爲MyChannelInterceptor的攔截器,目的是爲了在客戶端斷開連接後打印一下日誌。相關代碼如下:

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

package cn.zifangsky.stompwebsocket.interceptor.websocket;

 

import org.apache.commons.lang3.StringUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.messaging.Message;

import org.springframework.messaging.MessageChannel;

import org.springframework.messaging.simp.stomp.StompCommand;

import org.springframework.messaging.simp.stomp.StompHeaderAccessor;

import org.springframework.messaging.support.ChannelInterceptor;

import org.springframework.stereotype.Component;

 

import java.security.Principal;

import java.text.MessageFormat;

 

/**

* 自定義{@link org.springframework.messaging.support.ChannelInterceptor},實現斷開連接的處理

*

* @author zifangsky

* @date 2018/10/10

* @since 1.0.0

*/

@Component

public class MyChannelInterceptor implements ChannelInterceptor{

    private final Logger logger = LoggerFactory.getLogger(getClass());

 

    @Override

    public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {

        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);

        StompCommand command = accessor.getCommand();

 

        //用戶已經斷開連接

        if(StompCommand.DISCONNECT.equals(command)){

            String user = "";

            Principal principal = accessor.getUser();

            if(principal != null && StringUtils.isNoneBlank(principal.getName())){

                user = principal.getName();

            }else{

                user = accessor.getSessionId();

            }

 

            logger.debug(MessageFormat.format("用戶{0}的WebSocket連接已經斷開", user));

        }

    }

 

}

 

(2)使用@MessageMapping和@SendTo註解處理消息:

@MessageMapping註解用於監聽指定路徑的客戶端消息,而@SendTo註解則用於將服務端的消息發送給監聽了該路徑的客戶端。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package cn.zifangsky.stompwebsocket.controller;

 

import cn.zifangsky.stompwebsocket.model.websocket.Greeting;

import cn.zifangsky.stompwebsocket.model.websocket.HelloMessage;

import org.springframework.messaging.handler.annotation.MessageMapping;

import org.springframework.messaging.handler.annotation.SendTo;

import org.springframework.stereotype.Controller;

 

/**

* Greeting

* @author zifangsky

* @date 2018/9/30

* @since 1.0.0

*/

@Controller

public class GreetingController {

 

    @MessageMapping("/hello")

    @SendTo("/topic/greeting")

    public HelloMessage greeting(Greeting greeting) {

        return new HelloMessage("Hello," + greeting.getName() + "!");

    }

}

 

(3)示例頁面:

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

<html xmlns:th="http://www.thymeleaf.org">

<head>

    <meta content="text/html;charset=UTF-8"/>

    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>

    <meta name="viewport" content="width=device-width, initial-scale=1"/>

    <title>WebSocket Examples: Reverse</title>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>

    <script th:src="@{/layui/layui.js}"></script>

    <link th:href="@{/layui/css/layui.css}" rel="stylesheet">

    <style type="text/css">

        #connect-container {

            margin: 0 auto;

            width: 400px;

        }

 

        #connect-container div {

            padding: 5px;

            margin: 0 7px 10px 0;

        }

 

        .layui-btn {

            display: inline-block;

        }

    </style>

    <script type="text/javascript">

        var stompClient = null;

 

        $(function () {

            var target = $("#target");

            if (window.location.protocol === 'http:') {

                target.val('http://' + window.location.host + target.val());

            } else {

                target.val('https://' + window.location.host + target.val());

            }

        });

 

        function setConnected(connected) {

            var connect = $("#connect");

            var disconnect = $("#disconnect");

            var echo = $("#echo");

 

            if (connected) {

                connect.addClass("layui-btn-disabled");

                disconnect.removeClass("layui-btn-disabled");

                echo.removeClass("layui-btn-disabled");

            } else {

                connect.removeClass("layui-btn-disabled");

                disconnect.addClass("layui-btn-disabled");

                echo.addClass("layui-btn-disabled");

            }

 

            connect.attr("disabled", connected);

            disconnect.attr("disabled", !connected);

            echo.attr("disabled", !connected);

        }

 

        //連接

        function connect() {

            var target = $("#target").val();

 

            var ws = new SockJS(target);

            stompClient = Stomp.over(ws);

 

            stompClient.connect({}, function () {

                setConnected(true);

                log('Info: STOMP connection opened.');

 

                //訂閱服務端的/topic/greeting地址

                stompClient.subscribe("/topic/greeting", function (greeting) {

                    log('Received: ' + JSON.parse(greeting.body).content);

                })

            },function () {

                //斷開處理

                setConnected(false);

                log('Info: STOMP connection closed.');

            });

        }

 

        //斷開連接

        function disconnect() {

            if (stompClient != null) {

                stompClient.disconnect();

                stompClient = null;

            }

            setConnected(false);

            log('Info: STOMP connection closed.');

        }

 

        //向服務端發送姓名

        function sendName() {

            if (stompClient != null) {

                var username = $("#username").val();

                log('Sent: ' + username);

                stompClient.send("/message/hello", {}, JSON.stringify({'name': username}));

            } else {

                alert('STOMP connection not established, please connect.');

            }

        }

 

        //日誌輸出

        function log(message) {

            console.debug(message);

        }

    </script>

</head>

<body>

    <noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being

        enabled. Please enable

        Javascript and reload this page!</h2></noscript>

    <div>

        <div id="connect-container" class="layui-elem-field">

            <legend>STOMP Message With SockJS</legend>

            <div>

                <input id="target" type="text" class="layui-input" size="40" style="width: 350px" value="/stomp-websocket"/>

            </div>

            <div>

                <button id="connect" class="layui-btn layui-btn-normal" οnclick="connect();">Connect</button>

                <button id="disconnect" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled"

                        οnclick="disconnect();">Disconnect

                </button>

 

            </div>

            <div>

                <input id="username" type="text" class="layui-input" size="40" style="width: 350px" placeholder="請輸入你的姓名" value=""/>

            </div>

            <div>

                <button id="echo" class="layui-btn layui-btn-normal layui-btn-disabled" disabled="disabled"

                        οnclick="sendName();">Say hello

                </button>

            </div>

        </div>

    </div>

</body>

</html>

啓動項目後訪問頁面,效果如下:

參考:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章