因此,我一直在研究Tyrus (JSR 356 WebSocket for Java规范的参考实现)。 因为我一直在寻找测试工具,所以我对在Java中同时运行客户端和服务器端感兴趣。 因此,恐怕此博客文章中没有HTML5。
在此示例中,我们想来回发送JSON,因为我像这样老式,所以我希望能够绑定到POJO对象。 我将为此使用Jackson,因此我的Maven文件如下所示:
<dependencies>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0-rc3</version>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-client</artifactId>
<version>1.0-rc3</version>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-server</artifactId>
<version>1.0-rc3</version>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-container-grizzly</artifactId>
<version>1.0-rc3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.2.0</version>
</dependency>
</dependencies>
因此,我们需要做的第一件事就是定义Encode / Decoder接口的实现来为我们完成这项工作。 这将对bean类是什么进行一些简单的反映。 像使用JAX-WS一样,将它们放在同一个类中会更容易。 请注意,我们使用接口的流版本,并且仅处理文本内容。 (暂时忽略发送二进制数据的能力)
package websocket;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
public abstract class JSONCoder<T>
implements Encoder.TextStream<T>, Decoder.TextStream<T>{
private Class<T> _type;
// When configured my read in that ObjectMapper is not thread safe
//
private ThreadLocal<ObjectMapper> _mapper = new ThreadLocal<ObjectMapper>() {
@Override
protected ObjectMapper initialValue() {
return new ObjectMapper();
}
};
@Override
public void init(EndpointConfig endpointConfig) {
ParameterizedType $thisClass = (ParameterizedType) this.getClass().getGenericSuperclass();
Type $T = $thisClass.getActualTypeArguments()[0];
if ($T instanceof Class) {
_type = (Class<T>)$T;
}
else if ($T instanceof ParameterizedType) {
_type = (Class<T>)((ParameterizedType)$T).getRawType();
}
}
@Override
public void encode(T object, Writer writer) throws EncodeException, IOException {
_mapper.get().writeValue(writer, object);
}
@Override
public T decode(Reader reader) throws DecodeException, IOException {
return _mapper.get().readValue(reader, _type);
}
@Override
public void destroy() {
}
}
Bean类非常简单,带有Coder的静态子类,我们以后可以使用它。
package websocket;
public class EchoBean {
public static class EchoBeanCode extends
JSONCoder<EchoBean> {
}
private String _message;
private String _reply;
public EchoBean() {
}
public EchoBean(String _message) {
super();
this._message = _message;
}
public void setMessage(String _message) {
this._message = _message;
}
public String getMessage() {
return _message;
}
public void setReply(String _reply) {
this._reply = _reply;
}
public String getReply() {
return _reply;
}
}
因此,我们需要实现服务器端点,因此可以采用两种方式之一,即注释POJO或扩展端点。 我要为服务器使用第一个,为客户端使用第二个。 实际上,此服务所做的全部工作就是将消息发布回客户端。 注意编码和解码器的注册。 在这种情况下是相同的类。
package websocket;
import java.io.IOException;
import javax.websocket.EncodeException;
import javax.websocket.EndpointConfig;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import static java.lang.System.out;
@ServerEndpoint(value="/echo",
encoders = {EchoBean.EchoBeanCode.class},
decoders = {EchoBean.EchoBeanCode.class})
public class EchoBeanService
{
@OnMessage
public void echo (EchoBean bean, Session peer) throws IOException, EncodeException {
//
bean.setReply("Server says " + bean.getMessage());
out.println("Sending message to client");
peer.getBasicRemote().sendObject(bean);
}
@OnOpen
public void onOpen(final Session session, EndpointConfig endpointConfig) {
out.println("Server connected " + session + " " + endpointConfig);
}
}
让我们看一下客户端bean,这次扩展了标准Endpoint类并为消息添加了特定的侦听器。 在这种情况下,当收到消息时,只需关闭连接即可简化我们的测试案例。 在现实世界中,管理这种连接显然会更加复杂。
package websocket;
import java.io.IOException;
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.EncodeException;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import static java.lang.System.out;
@ClientEndpoint(encoders = {EchoBean.EchoBeanCode.class},
decoders = {EchoBean.EchoBeanCode.class})
public class EchoBeanClient
extends Endpoint
{
public void onOpen(final Session session, EndpointConfig endpointConfig) {
out.println("Client Connection open " + session + " " + endpointConfig);
// Add a listener to capture the returning event
//
session.addMessageHandler(new MessageHandler.Whole() {
@Override
public void onMessage(EchoBean bean) {
out.println("Message from server : " + bean.getReply());
out.println("Closing connection");
try {
session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "All fine"));
} catch (IOException e) {
e.printStackTrace();
}
}
});
// Once we are connected we can now safely send out initial message to the server
//
out.println("Sending message to server");
try {
EchoBean bean = new EchoBean("Hello");
session.getBasicRemote().sendObject(bean);
} catch (IOException e) {
e.printStackTrace();
} catch (EncodeException e) {
e.printStackTrace();
}
}
}
现在,使用Tyrus来独立运行WebSocket确实非常简单,您只需实例化服务器并启动它。 请注意,这会启动守护程序线程,因此您需要确保它是否在main方法中,并且您需要执行一些操作来保持JVM的生命。
import org.glassfish.tyrus.server.Server;
Server server = new Server("localhost", 8025, "/", EchoBeanService.class);
server.start();
因此客户相对简单; 但是在执行声明式方法时,我们需要在注册客户端类时显式注册编码器和解码器。
import javax.websocket.ClientEndpointConfig;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.Session;
import org.glassfish.tyrus.client.ClientManager;
// Right now we have to create a client, which will send a message then close
// when it has received a reply
//
ClientManager client = ClientManager.createClient();
EchoBeanClient beanClient = new EchoBeanClient();
Session session = client.connectToServer(
beanClient,
ClientEndpointConfig.Builder.create()
.encoders(Arrays.<Class<? extends Encoder>>asList(EchoBean.EchoBeanCode.class))
.decoders(Arrays.<Class<? extends Decoder>>asList(EchoBean.EchoBeanCode.class))
.build(),
URI.create("ws://localhost:8025/echo"));
// Wait until things are closed down
while (session.isOpen()) {
out.println("Waiting");
TimeUnit.MILLISECONDS.sleep(10);
}
现在,其输出如下所示:
Server connected SessionImpl{uri=/echo, id='e7739cc8-1ce5-4c26-ad5f-88a24c688799', endpoint=EndpointWrapper{endpointClass=null, endpoint=org.glassfish.tyrus.core.AnnotatedEndpoint@1ce5bc9, uri='/echo', contextPath='/'}} javax.websocket.server.DefaultServerEndpointConfig@ec120d
Waiting
Client Connection open SessionImpl{uri=ws://localhost:8025/echo, id='7428be2b-6f8a-4c40-a0c4-b1c8b22e1338', endpoint=EndpointWrapper{endpointClass=null, endpoint=websocket.EchoBeanClient@404c85, uri='ws://localhost:8025/echo', contextPath='ws://localhost:8025/echo'}} javax.websocket.DefaultClientEndpointConfig@15fdf14
Sending message to server
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Sending message to client
Message from server : Server says Hello
Closing connection
Waiting
有趣的是,这是第一次运行,会有一个暂停,我怀疑这是由于杰克逊进行了设置,但我没有时间进行分析。 我确实发现,这种漫长的延迟发生在第一篇文章中-尽管显然,这比一般地传递纯文本消息要慢。 差异是否对您很重要取决于您的应用程序。
将纯文本的性能与JSON流API(例如由新JSR提供的JSON流API)以及将这些值绑定到JSON POJO的版本进行比较会很有趣。 也许是另一天的事情。
翻译自: https://www.javacodegeeks.com/2013/05/using-java-websockets-jsr-356-and-json-mapped-to-pojos.html