为了发送WebSocket消息,无论注解式端点还是编程式端点,其API都是相同的:RemoteEndpoint和它的子类(RemoteEndpoint.Basic和RemoteEndpoint.Async)提供了发送消息的所有方法。
Ping和Pong消息通常被开发人员用来检查WebSocket底层连接的健康性。
从Session对象获得的RemoteEndpoint实例提供方法:
public void sendPing(ByteBuffer applicationData) throws IOException, IllegalArgumentException;
public void sendPong(ByteBuffer applicationData) throws IOException, IllegalArgumentException;
同步发送 RemoteEndpoint.Basic接口
//发送文本消息
public void sendText(String text) throws IOException;
//使用Writter API发送String消息
public Writter getSendStream() throws IOException;
//以分片的方式发送文本消息
public void sendText(String partialMessage, boolean isLast) throws IOException;
//发送二进制消息
public void sendBinary(ByteBuffer data) throws IOException;
//分片发送二进制消息
public void sendBinary(ByteBuffer partialByte, boolean isLast) throws IOException;
//使用流发送二进制消息
public Outputstream getSendStream() throws IOException;
//发送Java对象消息
public void sendObject(Object data) throws IOException, EncodeException;
Websocket是如何实现把对象编程WebSocket消息的,答案取决于你传什么类型的对象
- 如果你传一个Java基本类型对象(或者其等值装箱类),则WebSocket实现会把数据转换成一个标准的Java字符串对象(使用toString()方法)
- 如果传入的是其他对象,那么要为WebSocket实现提供一个javax.websocket.Encoder接口的实现
public String encode(T object) throws EncodeException;
public class DrawingEncoder implements Encoder.Text<DrawingObject> {
@Override
public String encode(DrawingObject drawingObject) throws EncodeException {
...
}
@Override
public void init(EndpointConfig endpointConfig) {}
@Override
public void destroy() {}
}
javax.websocket.Encoder.Text
是最通用的接口,T就是想发送的对象类型(DrawingObject
)还可以灵活选择其他的Encoder接口
Encoder.TextStream
T转换成WriterEncoder.Binary
T转换成ByteBuffer 对象编码成二进制消息Encoder.CharacterStream
或Encoder.BinaryStream
T转换成OutputStream 把对象编码成Java I/O流
创建好编码器后,要选择如何在端点上配置它
注解式:
@ServerPoint(value = "/fruit", encoders = {MyAppleEncoder.class})
public class ... {}
编程式:
//为编程式客户端端点配置编码器
List<Class<? extends Encoder>> encoders = new ArrayList<>();
encoders.add(MyAppleEncoder.class);
ClientEndpointConfig config = ClientEndpointConfig.Builder.create().encoders(encoders).build();
1
//接收文本消息
@OnMessage
public void handleTextMessage(String textMessage) {
...
}
//接收二进制消息
@OnMessage
public void handleBinaryMessage(byte[] messageData) {
...
}
@OnMessage
public void handleBinaryMessage(ByteBuffer messageData) {
...
}
//接收Pong消息,需要声明一个类型为 javax.websocket.PongMessage 的方法参数
@OnMessage
public String handlePongMessage(PongMessage pongMessage) {
return "I got " + pongMessage.getApplicationData().length + " bytes of data.";
}
//处理文本消息流
@OnMessage
public void handle(Reader message) {}
//
//处理到达的文本消息片段
@OnMessage
public void handlePartial(String textMessagePart, boolean isLast) {
}
//处理到达的二进制消息片段
@OnMessage
public void handlePartial(byte[] data, boolean isLast) {}
@OnMessage
public void handlePartial(ByteBuffer data, boolean isLast) {}
把接收到的消息转换成Java对象,需要解码类
提供Decoder接口的实现
public class DrawingDecoder implements Decoder.Text<DrawingObject> {
@Override
public DrawingObject decode(String s) throws DecodeException {
...
}
@Override
public boolean willDecode(String s) {
...
}
@Override
public void init(EndpointConfig endpointConfig) {}
@Override
public void destroy() {}
}
Decoder.Text
把入站的文本消息转换成T类型的对象willDecode
判断消息格式是否要用此解码还有其他类型的解码器
Decoder.TextStream
Reader转换成TDecoder.Binary
ByteBuffer转换成TDecoder.BinaryStream
InputStream转换成T- Java基本类型和它的等价类可以自动转换(Integer、Duble …)
如果想解码的原始数据无法转换成期望的Java对象,将会抛出DecodeException异常。在所有这些解码失败中,引起失败的入站消息不会被传递,但解码器中产生的DecodeException异常会被传递给端点的错误处理方法。
@OnMessage
注解的方法的返回值类型决定了寄回消息给发送者的消息类型
Java WebSocket在接收消息上有一个严格的限制: 每个注解式端点最多只有一个消息处理方法处理每种本地WebSocket消息类型(即文本消息、二进制消息 和 Pong消息)
例如:
@ServerEndpoint(value = "/orchard", decoders = "{OrangeDecoder.class}")
public class FruitTree {
@OnMessage
public void handleString(String message) {
...
}
@OnMessage
public void handleOrange(Orange orange) {
...
}
}
编程式端点使用什么方式接收消息取决于MessageHandler接口和它的子类性
public class ProgrammaticEchoServer extends Endpoint {
@Override
public void onOpen(Session session, EndpointConfig endpointConfig) {
final Session mySession = session;
mySession.addMessageHandler(new MessageHandler.Whole<String>() {
@Override
public void onMessage(String incomingMessage) {
try {
mySession.getBasicRemote().sendText("I got this (" + incomingMessage + ") so I am sending it back.");
} catch (IOException e) {
System.out.println("something went wrong:" + e.getMessage());
}
}
});
}
}
MessageHandler.Whole
文本消息 String
MessageHandler.Whole
文本消息 Java I/O流
MessageHandler.Whole
二进制 Java NIO ByteBuffer
MessageHandler.Whole
二进制 Byte数组
MessageHandler.Whole
二进制 Java I/O流
MessageHandler.Partial
文本片段 Java字符串序列
MessageHandler.Partial
二进制片段 ByteBuffer序列
MessageHandler.Partial
二进制片段 字节数组序列
MessageHandler.Whole
和 Decoder.Text
或者 Decoder.TextStream
文本消息 对象类型T
MessageHandler.Whole
和 Decoder.Binary
或者 Decoder.BinaryStream
二进制消息 对象类型T
//在编程式服务器端点中配置Decoder
public class MyServerApplicationConfig implements ServerApplicationConfig {
@Override
public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> set) {
Set<ServerEndpointConfig> configSet = new HashSet<>();
List<Class<? extends Endpoint>> decoders = new ArrayList<>();
decoders.add(MyOrangeDecoder.class);
ServerEndpointConfig serverEndpointConfig = ServerEndpointConfig.Builder.create(MyEndpoint
.class, "/fruit").decoders(decoders).build();
configSet.add(serverEndpointConfig);
return configSet;
}
@Override
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> set) {
return set;
}
}
限制条件与注解式相同,每个端点只允许最多一个方法来处理每个基本WebSocket消息类型。
xxx ↩︎