WebSocket消息推送和聊天功能实现

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

前端JavaScript调用WebSocket基础教程

SpringBoot集成WebSocket实现消息推送和聊天Demo

效果图
在这里插入图片描述
为了体现WebSocket的特性我尽量把代码写的简短。除第一个Action用作发布公告其它开一个游览器窗口会自动分配一个用户名。
开窗步骤
localhost:8080/ 发布公告页
localhost:8080/firstPage 用户aa
localhost:8080/firstPage 用户bb
localhost:8080/firstPage 用户cc
localhost:8080/firstPage 用户dd

在页面发送消息时加上 用户名 如:"aa你好吗"

代码结构


一个Controller发布两个Action:发布公告、聊天页
一个WebSocket配置类:发布WebSocket端点(Endpoint)
一个业务逻辑类:实现推送公告和聊天功能
两个Html5页面


gradle引入依赖

    implementation 'org.springframework.boot:spring-boot-starter-websocket'

测试用的Controller

@Controller
public class TestController {
    @RequestMapping("/")
    public String HomeIndexPage(){
        return "Index";
    }
    @RequestMapping("/firstPage")
    public String HomeIndexPage1(){
        return "First";
    }

    @RequestMapping("/publishanews")
    public String publishnewsproc(){
        WebSocketTest webSocketTest = new WebSocketTest();
        webSocketTest.sendMessageForAllClient("这是一条公告!");
        return "Index";
    }
}

两个测试页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>发布公告页</title>
    <script src="../static/jquery-3.3.1/jquery-3.3.1.js"></script>
    
    <script src="/jquery-3.3.1/jquery-3.3.1.js"></script>
</head>
<script>
    $(document).ready(function () {
        var websocket = null;

        //判断当前浏览器是否支持WebSocket
        if('WebSocket' in window){
            websocket = new WebSocket("ws://localhost:8080/websocket");
        }
        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>
<h2 id="cont">Good Evening</h2>
<a href="/publishanews">发布公告</a>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>聊天室</title>
<body>

<input id="text" type="text" /><button onclick="send()">Send</button>    <button onclick="closeWebSocket()">Close</button>
<div id="message">
</div>
</body>

<script type="text/javascript">
    var websocket = null;

    //判断当前浏览器是否支持WebSocket
    if('WebSocket' in window){
        websocket = new WebSocket("ws://localhost:8080/websocket");
    }
    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);
        // websocket.send()
    }
</script>
</html>

WebSocket的Endpoint配置类

这点和WebServices一样需要播出个端点出去。

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

业务逻辑类

@Service
@ServerEndpoint("/websocket")
public class WebSocketTest {
    private static Vector<Session> sessions = new Vector<>();
    private static TreeMap<String,Session> sessionTreeMap = new TreeMap<>();
    private static int loginNumber = 0;
    private Session session ;
    @OnOpen
    public void onopenproc(Session session) throws IOException {
        System.out.println("hava a client connected");
        this.session = session;
        sessions.add(session);
        if(loginNumber == 0)
            loginNumber++;
        else if(loginNumber == 1) {
            sessionTreeMap.put("aa", session);
            loginNumber++;
            sendMessageToClient("我是用户 aa " , session);
        }
        else if(loginNumber == 2) {
            sessionTreeMap.put("bb", session);
            loginNumber++;
            sendMessageToClient("我是用户 bb " , session);
        }
           else if(loginNumber == 3) {
                sessionTreeMap.put("cc", session);
                loginNumber++;
            sendMessageToClient("我是用户 cc " , session);
        }
        else if(loginNumber == 4) {
            sessionTreeMap.put("dd", session);
            loginNumber++;
            sendMessageToClient("我是用户 dd " , session);
        }
    }
    @OnClose
    public void oncloseproc(){
        System.out.println("had a client is disconneted");
    }
    @OnMessage
    public void onmessageproc(String message , Session session) throws IOException {
        if(message!=null) {
            StringBuffer stringBuffer = new StringBuffer();
            sessionTreeMap.forEach((k,v)->{
                if(v.equals(session)){
                    stringBuffer.append(k);
                }
            });

            switch (message.substring(0,2)){
                case "aa" :{
                    sendMessageToClient("From : "+stringBuffer.toString() + " : " + message , sessionTreeMap.get("aa"));
                }break;
                case "bb":{
                    sendMessageToClient("From : "+stringBuffer.toString() + " : " + message , sessionTreeMap.get("bb"));
                }break;
                case "cc":{
                    sendMessageToClient("From : "+stringBuffer.toString() + " : " + message , sessionTreeMap.get("cc"));
                }break;
                case "dd":{
                    sendMessageToClient("From : "+stringBuffer.toString() + " : " + message , sessionTreeMap.get("dd"));
                }break;
            }
            System.out.println(message);
        }
    }

    public void sendMessage(String msg) throws IOException {
        if(this.session!=null)
        this.session.getBasicRemote().sendText("hello everyone!");
        this.session.getBasicRemote().sendText(msg);
    }

    public void sendMessageForAllClient(String msg){
        if(!sessions.isEmpty()){
            sessions.forEach(i->{
                try {
                    if(i.isOpen()) {
                        i.getBasicRemote().sendText(msg+" : "+new Date().toString());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }

    public void sendMessageToClient(String msg , Session session) throws IOException {
        if(session.isOpen())
        session.getBasicRemote().sendText(msg);
    }
}

附上项目代码

发布了48 篇原创文章 · 获赞 8 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/wangxudongx/article/details/95807887