# SpringBoot WebSocket

背景:http协议只能由客户端发起请求,服务端被动接收,所以websocket来解决了,它实现了浏览器与服务器全双工通信,服务端可以主动向客户端发送数据

# 使用

# 1. 引入jar包

    <!--websocket-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
1
2
3
4
5

# 2. 简单配置下,开启支持

@Configuration
public class WebSocketConfig {

    /** 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
1
2
3
4
5
6
7
8
9

# 3. WebSocketServer


/**
 * WebSocketServer 核心类 相当于一个ws协议的Controller
 *
 * @author wbbaijq
 */
@ServerEndpoint("/websocket/{sid}")
@Component
public class WebSocketServer {

    static Logger log = LoggerFactory.getLogger(WebSocketServer.class);

    /**
     * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的
     */
    private static AtomicInteger onlineNum = new AtomicInteger(0);
    /**
     * 线程安全,用来存放每个客户端对应的 WebSocketServer 对象。
     */
    private static ConcurrentHashMap<String, WebSocketServer> webSocketServerPools = new ConcurrentHashMap<>();
    /**
     * 某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;
    /**
     * 接收sid
     */
    private String sid;


    /**
     * 建立连接成功调用
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this.session = session;
        this.sid = sid;
        //放到pools中
        if (webSocketServerPools.containsKey(sid)) {
            webSocketServerPools.remove(sid);
            webSocketServerPools.put(sid, this);
        } else {
            webSocketServerPools.put(sid, this);
            //在线数加一
            addOnlineCount();
        }
        log.info(sid + "加入webSocket!当前人数为" + onlineNum);
        try {
            sendMessage("欢迎" + sid + "加入连接!");
        } catch (IOException e) {
            log.error("用户" + sid + "WebScoket IO异常(网络):" + e.getMessage());
        }
    }

    /**
     * 关闭连接时调用
     */
    @OnClose
    public void onClose() {
        if (webSocketServerPools.containsKey(this.sid)) {
            //从map中移除
            webSocketServerPools.remove(this.sid);
            //在线数减一
            subOnlineCount();
        }
        log.info(sid + "断开webSocket连接!当前人数为" + onlineNum);
    }


    /**
     * 收到客户端信息
     *
     * @param message 客户端发过来的消息
     */
    @OnMessage
    public void onMessage(String message) {
        log.info("收到来自" + sid + "的信息:" + message);
        //群发消息
        for (WebSocketServer item : webSocketServerPools.values()) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 错误时调用
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误, 原因:" + error.getMessage());
        error.printStackTrace();
    }

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    /**
     * 群发自定义消息
     */
    public static void sendInfo(String message) {
        log.info("推送消息到窗口,内容:" + message);
        for (WebSocketServer item : webSocketServerPools.values()) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }


    public static void addOnlineCount() {
        onlineNum.incrementAndGet();
    }

    public static void subOnlineCount() {
        onlineNum.decrementAndGet();
    }

}

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

# 4. controller

@Controller
public class WebSocketController {

    @GetMapping("/socket/{cid}")
    public ModelAndView socket(@PathVariable String cid) {
        ModelAndView mv = new ModelAndView("/socket");
        mv.addObject("cid", cid);
        return mv;
    }

    @ResponseBody
    @RequestMapping("/socket/push/{cid}")
    public String pushToWeb(@PathVariable String cid, String message) {
        try {
            WebSocketServer.sendInfo(message);
        } catch (Exception e) {
            e.printStackTrace();
            return "发送失败";
        }
        return "发送成功";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 5. 页面

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>websocket通讯</title>
</head>
<body>
    <p><input id="text" type="text" /></p>

    <p><button onclick="send()">Send</button></p>

    <p><button onclick="closeWebSocket()">Close</button></p>

    <div id="message"></div>


    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script>
    <script>
        var websocket;

        //判断当前浏览器是否支持WebSocket
        if('WebSocket' in window){
            websocket = new WebSocket("ws://localhost:9001/websocket/9527");
        }
        else{
            alert('Not support websocket')
        }

        //连接发生错误的回调方法
        websocket.onerror = function(){
            setMessageInnerHTML("error");
        };

        //连接成功建立的回调方法
        websocket.onopen = function(event){
            setMessageInnerHTML("open");
        }

        //接收到消息的回调方法
        websocket.onmessage = function(event){
            setMessageInnerHTML(event.data);
        }

        //连接关闭的回调方法
        websocket.onclose = function(){
            setMessageInnerHTML("close");
        }

        //监听窗口关闭事件,当窗口关闭时,主动去关闭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);
        }
    </script>

</body>

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