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);
    }
}

附上项目代码

  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,主要用于实时、双向数据交换,特别适合于像在线聊天、游戏和股票交易等需要频繁更新数据的应用场景。以下是WebSocket消息推送实现的通用步骤: 1. **客户端(Web浏览器)连接**:客户端创建一个WebSocket对象,并指定服务器的URL。然后调用`connect()`方法尝试建立连接。 ```javascript const socket = new WebSocket('wss://your-server.com'); ``` 2. **握手过程**:服务器收到请求后,会发送一个HTTP升级头响应,将HTTP升级为WebSocket协议。客户端收到这个响应后,会进入open状态,表示连接已建立。 3. **发送和接收消息**:一旦连接建立,客户端和服务器可以相互发送文本或二进制数据。在JavaScript中,可以使用`send()`方法发送数据,`onmessage`事件监听接收到的数据。 ```javascript socket.send('Hello Server'); socket.onmessage = function(event) { console.log('Received:', event.data); }; ``` 4. **持久化连接**:WebSocket会保持长连接,除非主动关闭。数据传输是持续的,直到一方关闭连接。 5. **错误处理**:客户端和服务器都需要设置错误处理机制,如`onerror`事件,来捕获和处理可能出现的问题。 6. **安全与认证**:通常,WebSocket连接会在服务器端进行验证,如使用HTTPS确保安全,或者通过鉴权机制验证客户端身份。 7. **断线重连**:为了提高用户体验,一些库会提供自动重连功能,当连接断开时尝试重新连接。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值