一篇文章让你学习使用websocket
学习教程
跟着下面两篇教程的任意一篇写一遍,WebSocket基本上就算掌握了。
- Using WebSocket for Real-Time Communication in Java Platform, Enterpise Edition 7
- Java EE 7: Building Web Applications with WebSocket, JavaScript and HTML5
WebSocket介绍
web应用的出现需要客户端和服务端更多的交互,http只能在客户端每次请求服务端的时候建立短暂的连接,已经不能满足需求。虽然long-poll和Ajax也可以实现服务端向浏览器发送数据,但它们这种轮询的方式存在一些弊端,比如请求延迟和http过度请求。在需要实时连接(real-time)的应用中,这种弊端更加明显。
websocket是HTML5中新加入的内容。通过websocket(协议),每个浏览器(客户端)和服务端建立一个长连接,双方都可以随时主动地向对方发送消息。
Java EE 7 中已经集成了websocket,通过使用注解,可以很方便地创建一个websocket应用,主要有下面五个注解:
@ServerEndpoint
:定义websocket的地址;
@OnOpen
:服务端和客户端建立连接时调用;
@OnMessage
:发送数据时调用;
@OnClose
:关闭连接时调用;
@OnError
:出错时调用。
教程主要内容
下面是第一篇教程的主要内容,你可以到这里下载源码。
创建websocket服务
@ServerEndpoint
定义了websocket的服务端地址,还定义了编译发送出去的信息和解析收到的信息的类。
package com.ws.sticker;
import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint(
value="/story/notifications",
encoders={StickerEncoder.class},
decoders={StickerDecoder.class})
public class StoryWebSocket {
// 保存所有的 sticker
private static final List<Sticker> stickers = Collections.synchronizedList(new LinkedList<Sticker>());
// 保存所有客户端的 session
private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<Session>());
@OnMessage
public void onMessage(Session session, Sticker sticker){
// 有消息从客户端发送过来,保存到列表中,然后通知所有的客户端
stickers.add(sticker);
for(Session openSession : sessions){
try {
openSession.getBasicRemote().sendObject(sticker);
} catch (IOException | EncodeException e) {
sessions.remove(openSession);
}
}
}
@OnOpen
public void onOpen(Session session) throws IOException, EncodeException{
// 有新的客户端连接时,保存此客户端的session,并且把当前所有的sticker发送给它
sessions.add(session);
for(Sticker sticker : stickers){
session.getBasicRemote().sendObject(sticker);
}
}
@OnClose
public void onClose(Session session){
// 有客户端断开连接时 ,从session列表中移除此客户端的session
sessions.remove(session);
}
}
定义编译类
StickerEncoder.class
编译将要发送出去的信息:将要发送的Sticker
对象转化成JsonObject
对象,然后通过JsonWriter
发送到客户端。
package com.ws.sticker;
import java.io.IOException;
import java.io.Writer;
import javax.json.JsonObject;
import javax.json.JsonWriter;
import javax.json.spi.JsonProvider;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
public class StickerEncoder implements Encoder.TextStream<Sticker> {
@Override
public void init(EndpointConfig config) {
// TODO Auto-generated method stub
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void encode(Sticker sticker, Writer writer) throws EncodeException, IOException {
JsonProvider provider = JsonProvider.provider();
JsonObject jsonSticker = provider.createObjectBuilder()
.add("action", "add")
.add("x", sticker.getX())
.add("y", sticker.getY())
.add("sticker", sticker.getImage())
.build();
JsonWriter jsonWriter = provider.createWriter(writer);
jsonWriter.write(jsonSticker);
}
}
定义解析类
StickerDecoder.class
解析服务端收到的信息:通过Reader
获得接收到的信息JsonObject
,然后转化成Sticker
对象。
package com.ws.sticker;
import java.io.IOException;
import java.io.Reader;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.spi.JsonProvider;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
public class StickerDecoder implements Decoder.TextStream<Sticker> {
// Do not create a JsonReader object. To create readers and writes, use the
// JsonProvider class.
@Override
public void init(EndpointConfig config) {
// TODO Auto-generated method stub
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public Sticker decode(Reader reader) throws DecodeException, IOException {
JsonProvider provider = JsonProvider.provider();
JsonReader jsonReader = provider.createReader(reader);
JsonObject jsonSticker = jsonReader.readObject();
Sticker sticker = new Sticker();
sticker.setX(jsonSticker.getInt("x"));
sticker.setY(jsonSticker.getInt("y"));
sticker.setImage(jsonSticker.getString("sticker"));
return sticker;
}
}
创建websocket客户端
var socket = null;
function initialize() {
...
// 连接到websocket服务端
socket = new WebSocket('ws://localhost:8080/learn/story/notifications');
// 定义接收消息时执行的方法
socket.onmessage = onSocketMessage;
}
function onSocketMessage(event) {
if(event.data){
var receivedSticker = JSON.parse(event.data);
log("Received Object: " + JSON.stringify(receivedSticker));
...
}
}
window.onload = initialize;
观察现象
运行Java web程序,在多个浏览器中打开 http://localhost:8080/learn/story/notifications ,当其中一个浏览器的内容改变的时候,其他浏览器也会相应地改变。
总结
通过上面的练习,主要学到了:
- 通过 java API 创建 websocket server;
- 创建JSON 编译、解析类读写socket的数据;
- 使用JavaScript创建 websocket client。