SpringBoot的WebSocket入门例子

半兽人 发表于: 2020-03-07   最后更新时间: 2020-03-07 20:25:52  
{{totalSubscript}} 订阅, 2,012 游览

1. 使用内置的AbstractWebSocketHandler

该类是一个抽象类,在org.springframework:spring-websocket包中

maven引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    <version>2.1.0.RELEASE</version>
</dependency>

即可使用

使用时需要继承该类,根据需要重写相应方法即可

例如:

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;

public class MyWebSocketHandler extends AbstractWebSocketHandler {

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("Connection established");
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        System.out.println("收到消息:"+message);
        Thread.sleep(2000L);
        session.sendMessage(new TextMessage("Hello World!"));
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("Connection closed");
    }
}

如果是Spring MVC可以在XML中配置端点映射(例如映射到/hello):

<websocket:handlers>
    <websocket:mapping path="/hello" handler="socketHandler"/>
</websocket:handlers>
<bean class="MyWebSocketHandler" id="socketHandler"/>

也可以继承 AbstractWebSocketMessageBrokerConfigurer 并重写相应方法配置端点(还需要进行注入)

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/hello").withSockJS();
}

然后就可以和网页通信了

2. 使用websocket-api提供的注解/编程式接口

引入依赖:

<dependency>
   <groupId>javax.websocket</groupId>
   <artifactId>javax.websocket-api</artifactId>
   <version>1.1</version>
   <scope>provided</scope>
</dependency>

这里使用注解实现:


import Users;
import org.springframework.stereotype.Component;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

@ServerEndpoint(value = "/chatroom/{userName}")
@Component
public class WebsocketServer {
    private Session session;
    private static int count=0;

    @OnOpen
    public void onOpen(Session session, @PathParam("userName")String userName) throws IOException {
        this.session=session;
        //Users用来存放数据,user是该类的一个静态HashMap变量
        if(Users.user.get(userName)==null) {
            Users.user.put(userName, this);
            count++;
        }
        session.getBasicRemote().sendText("系统消息:当前在线人数:"+count);
    }

    @OnMessage
    public void onMessage(String msg) throws IOException {
        for(WebsocketServer server:Users.user.values()){
            server.session.getBasicRemote().sendText(msg);
        }
    }
}

还需要注入ServerEndpointExporter:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter confServerEndpointExporter(){
        return new ServerEndpointExporter();
    }
}

这里有一个大坑,网上的很多案例里面,@ServerEndpoint标记的类,都没有使用@Component,我在测试的时候,总是报404错误,无法连接,加上该注解后问题解决

如果是编程式实现,只要继承 javax.websocket.Endpoint 类并实现/重写相关方法,剩下的和注解式相同

3. 前端实现和测试

写了两个简单页面,就不用Controller进行映射了,直接把页面放到 resource/static 下

index.html:

<html>
<head>
    <meta charset="UTF-8">
    <title>登录</title>
</head>
<body>
    <form action="chat.html" method="get">
        你的名字:<input type="text" name="userName"/>
        <input type="submit" value="提交"/>
    </form>
</body>
</html>

chat.html:

<html>
<head>
    <meta charset="UTF-8">
    <title>聊天</title>
    <script type="text/javascript" src="webjars/jquery/2.1.4/jquery.min.js"></script>
    <script type="application/javascript">
        var url=null;
        var sock=null;
        var userName=getQueryString("userName")
        //百度来的函数,用来截取参数
        function getQueryString(name) {
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
            var r = window.location.search.substr(1).match(reg);
            if (r != null) return unescape(r[2]); return null;
        }
        $(function(){
            if(!window.WebSocket){
                alert('你的浏览器不支持WebSocket');
            }else{
                url='ws://localhost:8080/chatroom/'+userName
                sock=new WebSocket(url)
                sock.onopen=function () {
                    $("#content").append("<h1>欢迎"+userName+"来到聊天室</h1>");
                }
                sock.onmessage=function (e) {
                    $("#content").append("<p>"+e.data+"</p>")
                }
                sock.onclose=function () {
                    window.close();
                }
            }
        });
        function sendMessage() {
            var date=new Date()
            sock.send(userName+" "+date.toLocaleTimeString()+" 说<br/>"+$('.msg').val())
            $('.msg').val("")
        }
    </script>
</head>
<body>
<div id="content"></div>
<input type="text" class="msg"/><br/>
<input type="button" value="发送" οnclick="sendMessage()"/><br/>
<input type="button" value="关闭websocket" οnclick="sock.close()"/>
</body>
</html

这样就能实现一个公共聊天室了:

如果想实现私聊也很简单,稍作修改就可以,我写了个简单实现:

修改后的chat.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天</title>
    <script type="text/javascript" src="webjars/jquery/2.1.4/jquery.min.js"></script>
    <script type="application/javascript">
        var url=null;
        var sock=null;
        var userName=getQueryString("userName")
        function getQueryString(name) {
            var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
            var r = window.location.search.substr(1).match(reg);
            if (r != null) return unescape(r[2]); return null;
        }
        $(function(){
            if(!window.WebSocket){
                alert('你的浏览器不支持WebSocket');
            }else{
                url='ws://localhost:8080/chatroom/'+userName
                sock=new WebSocket(url)
                sock.onopen=function () {
                    $("#content").append("<p>欢迎"+userName+"来到聊天室</p>");
                }
                sock.onmessage=function (e) {
                    $("#content").append("<p>"+e.data+"</p>")
                }
                sock.onclose=function () {
                    window.close();
                }
            }
        });
        function sendMessage() {
            var date=new Date()
            sock.send(userName+" "+date.toLocaleTimeString()+" 说<br/>"+$('.msg').val())
            $('.msg').val("")
        }
        function sendPrivateMessage() {
            var date=new Date()
            sock.send("<font color='red'>"+userName+date.toLocaleTimeString()+"对"+$('.privatechat').val()+"悄悄说<br/>"+$('.msg').val()+"</font>")
            $('.msg').val("")
        }
    </script>
</head>
<body>

<div id="content"></div>
<input type="text" class="msg"/><br/>
<input type="button" value="发送" οnclick="sendMessage()"/><br/>
<input type="button" value="关闭websocket" οnclick="sock.close()"/>
对他私聊:<input type="text" class="privatechat"/><br/>
<input type="button" value="发送私聊" οnclick="sendPrivateMessage()"/><br/>
</body>
</html>

主要就是增加了一个私密发送的方法

WebSocketServer类的onMessage方法略作修改即可:

    @OnMessage
    public void onMessage(String msg) throws IOException {
        if(msg.startsWith("<font")){
            String user2=msg.substring(msg.indexOf("对")+1,msg.indexOf("悄悄"));
            WebsocketServer server2=Users.user.get(user2);
            if(server2!=null){
                server2.session.getBasicRemote().sendText(msg);
            }
            session.getBasicRemote().sendText(msg);
        }else {
            for (WebsocketServer server : Users.user.values()) {
                server.session.getBasicRemote().sendText(msg);
            }
        }
    }
更新于 2020-03-07

查看SpringBoot更多相关的文章或提一个关于SpringBoot的问题,也可以与我们一起分享文章