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 {@Beanpublic 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;@OnOpenpublic void whenOpening(Session session) {this.session = session;session.getUserProperties().put(START_TIME, System.currentTimeMillis());this.sendMessage("3:Just opened");}@OnMessagepublic 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");}@OnErrorpublic void whenSomethingGoesWrong(Throwable t) {this.sendMessage("2:Error: " + t.getMessage());}@OnClosepublic 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); // greycontext.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>
代码结构
WebSocket四种事件详解 入门篇(二)相关推荐
- python输入字符串并反序result_python字符串反转的四种方法详解
python字符串反转的四种方法详解 这篇文章主要介绍了python字符串反转的四种详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.用red ...
- js和php能生成一样的随机数_JavaScript_JS生成某个范围的随机数【四种情况详解】,前言:
JS没有现成的函数,能 - phpStudy...
JS生成某个范围的随机数[四种情况详解] 前言: JS没有现成的函数,能够直接生成指定范围的随机数. 但是它有个函数:Math.random() 这个函数可以生成 [0,1) 的一个随机数. 利用它 ...
- RTSP、HTTP、HTTPS、SDP四种协议详解
RTSP.HTTP.HTTPS.SDP四种协议详解 从这篇开始我们将进入流媒体的环节,流媒体在android中有nuplayer来实现的,在开始讲解android流媒体前,我们先来讲讲流媒体传输协议, ...
- kinux查日志_Linux实时查看日志的四种命令详解
原标题:Linux实时查看日志的四种命令详解 如何在Linux中实时查看日志文件的内容?那么有很多实用程序可以帮助用户在文件更改或不断更新时输出文件的内容.在Linux中实时显示文件内容的常用命令是t ...
- mhdd应用详解-入门篇(图文教程)
mhdd应用详解-入门篇(图文教程) 来源:wxiu.com 作者:fox 时间:2009-03-13 点击: 54 对于专业的 电脑维修人员来说, MHDD是必备的硬盘工具,但是技术人员一般只拿他 ...
- 内部类(四种内部类详解)
== = = = = = = 内部类(四种内部类详解)= = = = = = = == 一.基本介绍:一个类的内部又完整的嵌套了另一个类结构.被嵌套的类称为内部类(inner class),嵌套其他类 ...
- java正则表达式判断问号_正则表达式问号的四种用法详解
原文符号 因为?在正则表达式中有特殊的含义,所以如果想匹配?本身,则需要转义,\? 有无量词 问号可以表示重复前面内容的0次或一次,也就是要么不出现,要么出现一次. 非贪婪匹配 贪婪匹配 在满足匹配时 ...
- php 去重_php求两数组交集的四种方法详解
题目:给定两个数组,编写一个函数来计算它们的交集. 示例 1: 输入: nums1 = [1,2,2,1],nums2 = [2,2] 输出: [2] 示例 2: 输入: nums1 = [4,9,5 ...
- iVX低代码平台系列详解 -- 概述篇(二)
写在前面 ivx动手尝试电梯:ivx在线编辑器 iVX系列教程持续更新中 上篇文章可看:iVX低代码平台系列详解 – 概述篇(一) ivx目录 写在前面 一.iVX优势 1.快速学习 2.快速开发 3 ...
最新文章
- 推荐一款学习R的APP
- jmeter中控制器其中一个访问不到_Jmeter体系结构和运行原理
- 人体识别_深度学习资讯 | 用于人体动作识别的26层卷积神经网络
- [MS Sql Server术语解释]预读,逻辑读,物理读
- mysql数据生成词云图_CVPR2018关键字分析生成词云图与查找
- php异步学习(2)
- pytorch:测试GPU是否可用
- 使用Scrapy框架爬取网页并保存到Mysql
- 安卓端airplay实现IOS屏幕镜像
- 利用Rstudio对考试成绩进行数据分析
- android 图片缩放工具,批量图片缩放软件下载-批量图片缩放 安卓版v1.3.1-PC6安卓网...
- 我对平均值,期望,大数定律之间的联系的理解
- 外包程序员,如何提高自己跳出外包圈子?
- 第二章 VB的界面设计
- MoviePy - 中文文档4-MoviePy实战案例-追踪人脸,打马赛克
- 74HC148引脚图及功能
- 管晓宏院士谈工业数据共享和分类分级管理
- linux网络编程大杂烩==Linux应用编程7
- java 栈 先进先出_堆是先进先出,栈是先进后出
- 黑金花大理石_请问黑金花大理石是什么样的,有哪些特点?