JSR356 标准的WebSocket

WebSocket 机制

WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大不同是:

WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;
WebSocket 需要类似 TCP 的客户端和服务器端通过握手连接,连接成功后才能相互通信。

非 WebSocket 模式传统 HTTP 客户端与服务器的交互如下图所示:
图 1. 传统 HTTP 请求响应客户端服务器交互图
在这里插入图片描述
使用 WebSocket 模式客户端与服务器的交互如下图:
在这里插入图片描述
上图对比可以看出,相对于传统 HTTP 每次请求-应答都需要客户端与服务端建立连接的模式,WebSocket 是类似 Socket 的 TCP 长连接的通讯模式,一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输。在客户端断开 WebSocket 连接或 Server 端断掉连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。

WebSocket 实现

WebSocket 的实现分为客户端和服务端两部分,客户端(通常为浏览器)发出 WebSocket 连接请求,服务端响应,实现类似 TCP 握手的动作,从而在浏览器客户端和 WebSocket 服务端之间形成一条 HTTP 长连接快速通道。两者之间后续进行直接的数据互相传送,不再需要发起连接和相应。

以下简要描述 WebSocket 服务端 API 及客户端 API。

WebSocket 服务端 API

WebSocket 服务端在各个主流应用服务器厂商中已基本获得符合 JEE JSR356 标准规范 API 的支持(详见 JSR356 WebSocket API 规范 ),以下列举了部分常见的商用及开源应用服务器对 WebSocket Server 端的支持情况:

WebSocket 服务端支持

厂商应用服务器备注
IBMWebSphereWebSphere 8.0 以上版本支持,7.X 之前版本结合 MQTT 支持类似的 HTTP 长连接
OracleWebLogicWebLogic 12c 支持,11g 及 10g 版本通过 HTTP Publish 支持类似的 HTTP 长连接
MicrosoftIISIIS 7.0+支持
ApacheTomcatTomcat 7.0.5+支持,7.0.2X 及 7.0.3X 通过自定义 API 支持
JettyJetty 7.0+支持

注:WebLogic 12.1.3开始支持JSR356 标准,12.1.2及其以下使用WebSocket在下一章记录。

非JSR356 标准的WebSocket,WebLogic12.1.2下的实现方式:
https://blog.csdn.net/weixin_36782521/article/details/111611636

以下我们使用 Tomcat7.0.5 版本的服务端示例代码说明 WebSocket 服务端的实现:

JSR356 的 WebSocket 规范使用 javax.websocket.* 的 API,可以将一个普通 Java 对象(POJO)使用 @ServerEndpoint 注释作为 WebSocket 服务器的端点,代码示例如下:

WebSocket 服务端 API 示例

@ServerEndpoint("/echo")
public class EchoEndpoint {

	@OnOpen
	public void onOpen(Session session) throws IOException {
		// 以下代码省略...
	}

	@OnMessage
	public String onMessage(String message) {
		// 以下代码省略...
	}

	@Message(maxMessageSize = 6)
	public void receiveMessage(String s) {
		// 以下代码省略...
	}

	@OnError
	public void onError(Throwable t) {
		// 以下代码省略...
	}

	@OnClose
	public void onClose(Session session, CloseReason reason) {
		// 以下代码省略...
	}

}

代码解释:

上文的简洁代码即建立了一个 WebSocket 的服务端,@ServerEndpoint(“/echo”) 的 annotation 注释端点表示将 WebSocket 服务端运行在 ws://[Server 端 IP 或域名]:[Server 端口]/websockets/echo 的访问端点,客户端浏览器已经可以对 WebSocket 客户端 API 发起 HTTP 长连接了。

使用 ServerEndpoint 注释的类必须有一个公共的无参数构造函数,@onMessage 注解的 Java 方法用于接收传入的 WebSocket 信息,这个信息可以是文本格式,也可以是二进制格式。

OnOpen 在这个端点一个新的连接建立时被调用。参数提供了连接的另一端的更多细节。Session 表明两个 WebSocket 端点对话连接的另一端,可以理解为类似 HTTPSession 的概念。

OnClose 在连接被终止时调用。参数 closeReason 可封装更多细节,如为什么一个 WebSocket 连接关闭。

更高级的定制如 @Message 注释,MaxMessageSize 属性可以被用来定义消息字节最大限制,在示例程序中,如果超过 6 个字节的信息被接收,就报告错误和连接关闭。

注意:早期不同应用服务器支持的 WebSocket 方式不尽相同,即使同一厂商,不同版本也有细微差别。

WebSocket 客户端 API

对于 WebSocket 客户端,主流的浏览器(包括 PC 和移动终端)现已都支持标准的 HTML5 的 WebSocket API,这意味着客户端的 WebSocket JavaScirpt 脚本具备良好的一致性和跨平台特性,以下列举了常见的浏览器厂商对 WebSocket 的支持情况:

WebSocket 客户端支持

浏览器支持情况
ChromeChrome version 4+支持
FirefoxFirefox version 5+支持
FirefoxIE version 10+支持
FirefoxIOS 5+支持
Android BrowerAndroid 4.5+支持

客户端 WebSocket API 基本上已经在各个主流浏览器厂商中实现了统一,因此使用标准 HTML5 定义的 WebSocket 客户端的 JavaScript API 即可,当然也可以使用业界满足 WebSocket 标准规范的开源框架,如 Socket.io。

以下以一段代码示例说明 WebSocket 的客户端实现:

WebSocket 客户端 API 示例

var ws = new WebSocket("ws://echo.websocket.org”);
ws.onopen = function(){ws.send("Test!); };
ws.onmessage = function(evt){console.log(evt.data);ws.close();};
ws.onclose = function(evt){console.log("WebSocketClosed!);};
ws.onerror = function(evt){console.log("WebSocketError!);};

第一行代码是在申请一个 WebSocket 对象,参数是需要连接的服务器端的地址,同 HTTP 协议开头一样,WebSocket 协议的 URL 使用 ws://开头,另外安全的 WebSocket 协议使用 wss://开头。

第二行到第五行为 WebSocket 对象注册消息的处理函数,WebSocket 对象一共支持四个消息 onopen, onmessage, onclose 和 onerror,有了这 4 个事件,我们就可以很容易很轻松的驾驭 WebSocket。

当 Browser 和 WebSocketServer 连接成功后,会触发 onopen 消息;如果连接失败,发送、接收数据失败或者处理数据出现错误,browser 会触发 onerror 消息;当 Browser 接收到 WebSocketServer 发送过来的数据时,就会触发 onmessage 消息,参数 evt 中包含 Server 传输过来的数据;当 Browser 接收到 WebSocketServer 端发送的关闭连接请求时,就会触发 onclose 消息。我们可以看出所有的操作都是采用异步回调的方式触发,这样不会阻塞 UI,可以获得更快的响应时间,更好的用户体验。

Demo

Java

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/websocket/{user}")
public class WebScoketAction {

	public static List<Session> sessions = new ArrayList<Session>();

	private Session session;

	@OnOpen
	public void open(Session session, @PathParam(value = "user") String user) {
		this.session = session;

		System.out.println("*** WebSocket opened from sessionId " + session.getId());
		sessions.add(this.session);
	}

	@OnMessage
	public void inMessage(String message) {
		System.out.println("*** WebSocket Received from sessionId " + this.session.getId() + ": " + message);
	}

	@OnClose
	public void end() {
		System.out.println("*** WebSocket closed from sessionId " + this.session.getId());
		sessions.remove(this.session);
	}
	
	public static void sendMessage(String msg) {
		System.out.println("Send Message : " + msg);
		for (Iterator iter = WebScoketAction.sessions.iterator(); iter.hasNext();) {
			Session session = (Session) iter.next();
			try {
				if(session.isOpen()) {
					session.getBasicRemote().sendText("msg");
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

Jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test WebSocket</title>
<script type="text/javascript">

	// http://localhost:8080/TestWebSocket

	var ws = null;
	function startWebSocket() {
		if ('WebSocket' in window) {
			ws = new WebSocket("ws://localhost:8080/TestWebSocket/websocket/li");
		} else {
			alert("not support");
		}
		ws.onmessage = function(evt) {
			//接收到消息
			alert(evt.data);
		};

		ws.onclose = function(evt) {
			alert("close");
			// 异常关闭重新连接
			startWebSocket();
		};

		ws.onopen = function(evt) {
			alert("open");
		};
	}

	function sendMsg() {
		ws.send(document.getElementById('writeMsg').value);
	}
</script>
</head>
<body onload="startWebSocket();">    
<input type="text" id="writeMsg"></input>    
<input type="button" value="send" onclick="sendMsg()"></input>    
</body>
</html>

注:上面demo在Tomcat7.0.76和Weblogic12.1.3上均测试通过。

转载: https://developer.ibm.com/zh/articles/j-lo-WebSocket/

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值