WebSocket四种事件详解 入门篇(二)

 

 

示例代码  在文章最下方,可以根据示例代码和讲解来理解整个WebSocket流程。代码用SpringBoot 和 maven搭建

 

服务端和客户端四种事件

1、打开事件

    此事件发生在端点上建立新连接时并且在任何其他时间发生之前。

    方法级注解:@OnOpen

                        使用注解的的方法是没有任何返回值的公有方法,这些方法有一个可选的Session参数,一个可选的EndpointConfig参数,以及

                        任意数量的被@PathParam注解的String参数(这些参数的顺序是可以任意排列的)。

 

在案例中运用:

@OnOpen
public void whenOpening(Session session) {
    this.session = session;
    session.getUserProperties().put(START_TIME, System.currentTimeMillis());
    this.sendMessage("3:Just opened");
}

        

2、消息事件

    此事件接收WebSocket对话中另一端发送的消息。它发生在WebSocket端点接收了打开事件之后并且在接收关闭事件关闭连接之前的任意时刻。

    方法级注解:@OnMessage

                        连接上的消息以3中基本形式抵达:文本消息、二进制消息、或pong消息。

                        方法可以有返回参数,当使用方法有返回参数时,WebSocket实现立即将返回值作为消息返回给刚刚在方法中处理的消息发送者。

在案例中运用

一、

//只接受文本消息,不处理其他格式消息

@OnMessage
public void whenGettingAMessage(String message) {
    System.out.println("接收消息:"+message);
    if (message.indexOf("xxx") != -1) {
        throw new IllegalArgumentException("xxx not allowed !");
    } else if (message.indexOf("close") != -1) {
        try {
            this.sendMessage("1:Server closing after " + this.getConnectionSeconds() + " s");
            session.close();
        } catch (IOException ioe) {
            System.out.println("Error closing session " + ioe.getMessage());
        }
        return;
    }
    this.sendMessage("3:Just processed a message");
}
二、
 

//只接受二进制消息,不处理其他格式消息

@OnMessage
public void processBinary(byte[] messageDate,Session session) {
    //代码逻辑
}       

3、错误事件

    此事件在WebSocket连接或者端点发生错误时产生。

    方法级注解:@OnError

                        有三种基本类型的错误:

                        1、WebSocket实现产生的错误可能会发生。

                        2、错误可能发生在当WebSocket实现试图将入站消息解码成开发人员所要求的某个对象时。

                        3、最后由WebSocket端点的其他方法产生的运行错误。

在案例中运用

注意:第二种错误实际上调用的也是第一种注解方式

一、模拟异常错误
@OnError
public void whenSomethingGoesWrong(Throwable t) {
    this.sendMessage("2:Error: " + t.getMessage());
}
二、模拟解析消息时自定义错误
 

4、关闭事件

    此事件表示WebSocket端点的连接目前正在部分地关闭,它可以由参与连接的任意一个端点发出。

    方法级注解:@OnClose

                        生命周期的最后一个事件

在案例中运用

一、模拟客户端关闭WebSocket

@OnClose
public void whenClosing() {
    System.out.println("Goodbye !");
}
二、模拟客户端发送关闭请求,服务端关闭WebSocket
 

注意:其实方法二,最终调用过的也是方法一。所以当服务端或者客户端关闭WebSocket都是调用@OnClose方法。

 

案例运行:

1、开始界面

2、点击连接WebSocket端点(连接后可以进行操作)

 

3、发送消息(调用了@OnMessage 方法)

4、发送错误消息(会导致连接断开,先调用了@OncClose注解方法,判断消息不和规范抛出异常调用@OnError注解方法,之后调用@Onclose注解方法)

4、调用服务端关闭和调用客户端关闭操作(本质上调用的都是@Onclose注解方法)

实际上流程

服务端关闭:

    1)先调用了@OnMessage注解方法,

    2)判断入参为Close ,标识需要服务端关闭连接,服务端调用当前Session.Close()

    3)然后调用@OnClose注解方法关闭WebSocket连接

客户端关闭

    1)调用@OnClose注解方法关闭WebSocket连接

总结:

WebSocket声明周期

开始:通过@OnOpen开始

通讯:通过@OnMessage进行通讯

中断:@OnError  和 @OnClose 都会导致中断。

 

SpringBoot启动配置(配置完成直接运行此类就可以)

package Develop;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    /*
        @SpringBootApplication
       标注启动配置入口,可以发现通过一个main方法启动。
       使用这个注解的类必须放置于最外层包中,因为默认扫描这个类以下的包。
       否则需要自己配置@ComponentScan。
     */
    public static void main(String[] arg){
        SpringApplication.run(Application.class);
    }
}

 

扫描服务端端点配置(如没有,则服务端端点无法实例化成功)

package Develop;

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 serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

 

服务端端点

package Develop.Lifecycle;
import org.springframework.stereotype.Component;

import java.io.IOException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/lights")
@Component
public class LifecycleEndpoint {
    private static String START_TIME = "Start Time";
    private Session session;

    @OnOpen
    public void whenOpening(Session session) {
        this.session = session;
        session.getUserProperties().put(START_TIME, System.currentTimeMillis());
        this.sendMessage("3:Just opened");
    }

    @OnMessage
    public void whenGettingAMessage(String message) {
        System.out.println("接收消息:"+message);
        if (message.indexOf("xxx") != -1) {
            throw new IllegalArgumentException("xxx not allowed !");
        } else if (message.indexOf("close") != -1) {
            try {
                this.sendMessage("1:Server closing after " + this.getConnectionSeconds() + " s");
                session.close();
            } catch (IOException ioe) {
                System.out.println("Error closing session " + ioe.getMessage());
            }
            return;
        }
        this.sendMessage("3:Just processed a message");
    }

    @OnError
    public void whenSomethingGoesWrong(Throwable t) {
        this.sendMessage("2:Error: " + t.getMessage());
    }

    @OnClose
    public void whenClosing() {
        System.out.println("Goodbye !");
    }

    void sendMessage(String message) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (Throwable ioe) {
            System.out.println("Error sending message " + ioe.getMessage());
        }
    }

    int getConnectionSeconds() {
        long millis = System.currentTimeMillis() -
                ((Long) this.session.getUserProperties().get(START_TIME));
        return (int) millis / 1000;
    }
}

 客户端页面(可以自行更改服务端连接地址)


<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Lifecycle</title>
    </head>
    <body>
        <h1 style="text-align: center;">Lifecycle Lights</h1>

        <table style="text-align: left; width: 50px; margin-left: auto;
        margin-right: auto;" border="0" cellpadding="20" cellspacing="0">
            <tbody>
            <tr>
                <td style=" text-align: center; vertical-align: top;">
                    <div style="text-align: center;">
                    <form action="">
                        <input onclick="open_connection()" value="Open Connection" type="button" id="ocID">
                        <input onclick="send_valid_message()" value="Send Message" type="button" id="svmID">
                        <input onclick="send_invalid_message()" value="Send Bad Message" type="button" id="simID">
                        <input onclick="request_close_connection()" value="Server Close Connection" type="button"  id="rccID">
                        <input onclick="close_connection()" value="Client Close Connection" type="button" id="rcID">
                    </form>
                </div>
                </td>
            </tr>    
            <tr>
                <td style=" text-align: center; vertical-align: top;">
                    <canvas id="myDrawing" width="200" height="210"></canvas>
                    <div id="traffic_light_display"></div>
                </td>
            </tr>
            </tbody>
        </table>
        <div id="output"></div>
    </body>
    <script language="javascript" type="text/javascript">
            var lifecycle_websocket;
            
            function init() {
                output = document.getElementById("output");
                traffic_light_display = document.getElementById("traffic_light_display");
                update_display("1", "No connection");
                update_buttons();
            }

            function open_connection() {
                lifecycle_websocket = new WebSocket("ws://localhost:8080/lights");
                lifecycle_websocket.onmessage = function (evt) {
                    update_for_message(evt.data);
                    update_buttons();
                };
                lifecycle_websocket.onclose = function (evt) {
                    update_buttons();
                }; 
            }
            
            function get_color(light_index, light_on_index) {
                if (light_index == 1 && light_on_index == 1) {
                    return "red"
                } else if (light_index == 2 && light_on_index == 2) {
                    return "yellow"
                } else if (light_index == 3 && light_on_index == 3) {
                    return "green"
                } else {
                    return "grey"
                }  
            }
            
            function get_light_index(message) {
                return message.substring(0, 1)
            }
            
            function get_display_message(message) {
                return message.substring(2, message.length)
            }
            
            function update_for_message(message) {
                var display_message = get_display_message(message);
                var light_index = get_light_index(message);
                update_display(light_index, display_message);
            }
            
            function update_display(light_index, display_message) {
                var old = traffic_light_display.firstChild;
                var pre = document.createElement("pre");
                pre.style.wordWrap = "break-word"; 
                pre.innerHTML = "<b><font face='Arial'>"+display_message+"</font></b>";
                if (traffic_light_display.firstChild != null) {
                    traffic_light_display.replaceChild(pre, traffic_light_display.firstChild);
                } else {
                    traffic_light_display.appendChild(pre)
                }
                
                var context = document.getElementById('myDrawing').getContext('2d');
                context.beginPath();
                context.fillStyle = "black"
                context.fillRect(65,0,70,210);
                context.fill();
                
                context.beginPath();
                context.fillStyle = get_color(1, light_index); // grey
                context.arc(100,35,25,0,(2*Math.PI), false)
                context.fill();
                
                context.beginPath();
                context.fillStyle = get_color(2, light_index);
                context.arc(100,105,25,0,(2*Math.PI), false)
                context.fill();
                
                context.beginPath();
                context.fillStyle = get_color(3, light_index);
                context.arc(100,175,25,0,(2*Math.PI), false)
                context.fill(); 
            }
            
            function isOpen() {
                return lifecycle_websocket != null && lifecycle_websocket.readyState == lifecycle_websocket.OPEN;
            }
            
            function update_buttons() {
                ocID.disabled = isOpen();
                simID.disabled = !isOpen();
                svmID.disabled = !isOpen();
                rccID.disabled = !isOpen();
                rcID.disabled = !isOpen();
            }
            
            function send_valid_message() {
                lifecycle_websocket.send("Hello")  
            }
            
            function send_invalid_message() {
                lifecycle_websocket.send("Helxxxlo")
                
            }
            
            function request_close_connection() {
                lifecycle_websocket.send("close");
            }
            
            function close_connection() {
                lifecycle_websocket.close();
                update_display("1", "Client closed connection");
            }

            window.addEventListener("load", init, false);
        </script>
</html>

 

maven依赖



    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.3.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-juli</artifactId>
            <version>8.0.53</version>
        </dependency>

    </dependencies>

 

代码结构

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值