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

10 篇文章 0 订阅
10 篇文章 0 订阅

简介

所谓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>

启动项目后访问页面,效果如下:

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值