JavaEE 7教程
WebSocket详解
WebSocket可提供一个在 单一TCP连接全双工双向通信协议。全双工意味着客户端和服务器可以独立发送信息给 对方。双向意味着客户端可以向服务器发送信息, 反之亦然。
WebSocket是定义于IETF RFC 6455协议和W3C JavaScript API。该协议在TCP层定义了一个开放的握手和基本的讯息。该API允许Web页使用WebSocket协议双向 与远程主机通信。
不同于HTTP,它不需要创建一个新的TCP连接就能发送一个消息,可以持久不断地用于客户端和服务器之间的每一个交换。一旦最初通过HTTP 握手),客户端 和服务器可以独立互相发送信息。
客户端和服务器之间的通信是对称的,但有两个 差异:
- 客户端发起连接到服务器,是监听一个WebSocket请求。
- 客户端使用URI连接到一个服务器。一个服务器可以监听同一个URI多个客户端。
客户端和服务器来回传输数据的单位称消息。消息可以是文本或二进制数据。 还携带用于协议级的信令数据。
Java API定义了WebSocket应用的标准API ,支持:
- 使用注释和接口创造一个WebSocket客户端和服务器端点
- 生产和消费WebSocket的文本,二进制,和控制消息
- 发起和拦截WebSocket的生命周期事件
- WebSocket会话配置和管理,如超时,重试等
- WebSocket应用如何在Java EE 安全模型上工作
注释服务器端点
可以将一个普通Java对象(POJO)使用@serverendpoint作为WebSocket服务器的端点 。这个端点也被称为注释的终点:
@ServerEndpoint("/chat")
public class ChatServer {
@OnMessage
public String receiveMessage(String message) {
//. . .
}
}
注释的类必须有一个公共的 无参数构造函数。
@onMessage注解的Java方法用于接收传入的WebSocket信息。 这个信息可以是文本,二进制。
接受字符串信息:
public void receiveMessage(String s) {
//. . .
}
接受Java原始类型信息:
public void receiveMessage(int i) {
//. . .
}
接受一个大文本,如果接收到最后一部分是布尔参数为真,否则为假。
public void receiveBigText(String message, boolean last) {
//. . .
}
使用一个Reader接收整个文本消息阻塞流:
@OnMessage
public void processReader(Reader reader) {
//. . .
}
使用字节[ ]或字节缓冲区接收整个二进制消息:
public void receiveMessage(ByteBuffer b) {
//. . .
}
使用字节[ ]和字节缓冲接受大的二进制,如果接收到的最后一部分是布尔参数为真:
public void receiveBigBinary(ByteBuffer buf, boolean last) {
//. . .
}
使用InputStream得到完整的二进制消息作为阻塞流:
public void processStream(InputStream stream) {
//. . .
}
使用pongmessage接受pong消息:
public void processPong(PongMessage pong) {
//. . .
}
使用了Java的字符串:
@ServerEndpoint("/chat/{room}")
public class MyEndpoint {
@OnMessage
public void receiveMessage(String message, @PathParam("room")String room) {
//. . .
}
}
“pathparam用于标注方法参数,获取在服务器端点URI/chat/{room}的room参数值
public void receiveMessage(String message, Session session) {
//. . .
}
Session表明两个WebSocket端点对话 连接的另一端。在这种情况下,响应到客户端可能
返回:
public void receiveMessage(String message, Session session) {
session.getBasicRemote().sendText(...);
}
参数可以以任何顺序列出。该方法可以有一个void返回类型。这一消息是消耗在终点 没有返回响应。
MaxMessageSize属性可以被用来定义 消息字节最大大小:
@Message(maxMessageSize=6)
public void receiveMessage(String s) {
//. . .
}
在这个程序中,如果超过6个字节的信息被接收,就报告错误 和连接关闭。 使用@OnClose生命周期回调拦截,你还可以得到精确的错误代码和消息的。默认值为1指示 没有最大值。
MaxMessageSize属性不适用使用流 或reader获取的传入消息。
高级定制
@OnOpen
public void open(Session s) {
//. . .
}
@OnClose
public void close(CloseReason c) {
//. . .
}
@OnError
public void error(Throwable t) {
//. . .
}
OnOpen在这个端点一个新的连接建立时被调用。 参数提供了连接的另一端的更多细节。
OnClose在连接被终止时被调用,。参数c 提供更多细节,为什么一个WebSocket连接关闭。
在Java EE平台部署端点,符合CDI规范描述支持完全依赖注入, 。可在所有的WebSocket端点类实现字段,方法和构造器注入。
@ServerEndpoint("/chat")
public class ChatServer {
@Inject User user;
//. . .
}
端点类继承:
@ServerEndpoint("/chat")
public class ChatServer {
}
public class CustomChatServer extends ChatServer {
//. . .
}
在这段代码中,该chatserver类被确定为一个WebSocket端点;然而, customchatserver不是。如果它需要被公认为一个WebSocket端点,然后 必须明确标有@serverendpoint注释。
你可以用@clientendpoint标注一个POJO作为WebSocket客户端点:
@ClientEndpoint
public class MyClientEndpoint {
//. . .
}
您可以通过指定截获的生命周期事件”,@OnClose,和@OnError 注释方法:
@ClientEndpoint
public class MyClientEndpoint {
@OnOpen
public void open(Session s) {
//. . .
}
@OnClose
public void close(CloseReason c) {
//. . .
}
@OnError
public void error(Throwable t) {
//. . .
}
}
在开放的方法使用session:
@OnOpen
public void onOpen(Session session) {
try {
session.getBasicRemote().sendText("Duke");
} catch (IOException ex) {
//. . .
}
}
消息接受是任何Java方法需要标注 @ onMessage:
@OnMessage
public void processMessage(String message, Session session) {
//. . .
}
客户可以通过containerprovider连接端点:
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
String uri = "ws://localhost:8080/myApp/websocket";
container.connectToServer(MyClient.class, URI.create(uri));
客户端的JavaScript WebSocket
你可以使用定义的JavaScript API 调用一个WebSocket端点。 指定URL和一个可选的连接到一个WebSocket端点 :
var websocket = new WebSocket("ws://localhost:8080/myapp/chat");
该API还定义了不同的生命周期方法调用事件处理程序:
OnOpen一个新的连接启动时被调用,。
onerror收到错误消息时被调用。
OnClose连接终止时被调用:
websocket.onopen = function() {
//. . .
}
websocket.onerror = function(evt) {
//. . .
}
websocket.onclose = function() {
//. . .
}
文本或二进制数据都可以发送:
websocket.send(myField.value);
websocket.binaryType = "arraybuffer";
var buffer = new ArrayBuffer(myField.value);
var bytes = new Uint8Array(buffer);
for (var i=0; i<bytes.length; i++) {
bytes[i] = i;
}
websocket.send(buffer);
消息可以通过onmessage事件句柄接收:
websocket.onmessage = function(evt) {
console.log("message received: " + evt.data);
}
与Java EE安全集成
WebSocket 端点建立是基于servlet定义的身份验证和授权安全机制。 一个WebSocket试图启动一个连接时必须先前认证 。通常情况下,这是 通过HTTP验证执行(也许是基本或基于表单的Web应用程序) 。
你可以通过web.xml部署描述符建立基本的认证:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<security-constraint>
<web-resource-collection>
<web-resource-name>WebSocket Endpoint</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>g1</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>file</realm-name>
</login-config>
<security-role>
<role-name>g1</role-name>
</security-role>
</web-app>