本节描述声明式WebSocket编程,可以与后一篇编程式WebSocket作对比学习:
首先上服务端:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@ServerEndpoint
(
"/chat"
)
public
class
DeclarativeServer {
@OnOpen
public
void
onOpen(Session session) {
System.out.println(
"Somebody is coming!"
);
}
@OnClose
public
void
onClose() {
}
@OnMessage
public
void
onMessage(String message, Session session)
throws
IOException {
System.out.println(message);
session.getBasicRemote().sendText(
"it is sickening"
);
}
@OnError
public
void
onError(Session session, Throwable error) {
error.printStackTrace();
}
}
|
通过ServerEndpoint注解将一个POJO声明为WebSocket Server端点(Endpoint和web service的概念endpoint类同)。
ServerEndpoint注解声明如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
@Retention
(RetentionPolicy.RUNTIME)
@Target
(ElementType.TYPE)
public
@interface
ServerEndpoint {
/**
* URI or URI-template that the annotated class should be mapped to.
* @return The URI or URI-template that the annotated class should be mapped
* to.
*/
String value();
String[] subprotocols()
default
{};
Class<?
extends
Decoder>[] decoders()
default
{};
Class<?
extends
Encoder>[] encoders()
default
{};
public
Class<?
extends
ServerEndpointConfig.Configurator> configurator()
default
ServerEndpointConfig.Configurator.
class
;
}
|
一般情况下,我们只需要用为ServerEndpoint注解配置value属性,表示该端点映射的URL路径。
subprotocols协议用于配websocket的子协议,比如superchat等,这一阶段我们先不理会它。
decoders,encoders用于定义编解码器,后面的文章我们会详细讨论他。
configurator属性,对于声明式编程的Server端点,可以不配值,会采用默认值ServerEndpointConfig.Configurator即可。
(有默认值,一般说明该属性不可或缺,在编程式WebSocketk中时,我们会看到Configurator的更多细节).
DeclarativeServer实现四个方法,分别带有注解 @OnOpen,@OnClose,@OnMessage , @OnError标示。
@OnOpen表明当有客户端连接到该端点,则回调@OnOpen标记的方法。
@OnClose当客户端断开连接时,即服务端收到连接断开指定,则回调@OnClose的方法。
@OnMessage当服务端接收到清息时,则回调该方法。
@OnError当服务端发现异常情况时,比如协议错误,则回调该方法。Error不代表连接需要关闭,很多错误是可恢复的。
将该类打入war包,部署到Tomcat上,一个WebSocket服务端就OK了。
本次我们不用javascript作为Client端点,而是采用胖客户端模式访问,即Java Application。
首先定义Client端点:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@ClientEndpoint
public
class
DeclarativeClient {
@OnOpen
public
void
onOpen(Session session) {
System.out.println(
"I was accpeted by her!"
);
}
@OnClose
public
void
onClose() {
}
@OnMessage
public
void
onMessage(String message, Session session) {
System.out.println(
"she say: "
+ message);
}
@OnError
public
void
onError(Session session, Throwable error) {
error.printStackTrace();
}
}
|
通过ClientEndpoint注解表示这是一个WebSocket的Client端点。
1
2
3
4
5
6
7
8
9
|
@Retention
(RetentionPolicy.RUNTIME)
@Target
(ElementType.TYPE)
public
@interface
ClientEndpoint {
String[] subprotocols()
default
{};
Class<?
extends
Decoder>[] decoders()
default
{};
Class<?
extends
Encoder>[] encoders()
default
{};
public
Class<?
extends
Configurator> configurator()
default
Configurator.
class
;
}
|
与上面的ServerEndpoint只差一个value属性,不用讲大家也知道为什么了。
各个方法注解与Server一样,不再重述。主函数:
1
2
3
4
5
6
7
8
9
|
public
class
Client {
public
static
void
main(String[] args)
throws
DeploymentException, IOException, InterruptedException {
WebSocketContainer ws = ContainerProvider.getWebSocketContainer();
String url =
"ws://localhost:8080/ChatWeb/chat"
;
Session session = ws.connectToServer(DeclarativeClient.
class
, URI.create(url));
session.getBasicRemote().sendText(
"Hello,chick!"
);
Thread.currentThread().sleep(
10000
);
}
}
|
运行Client之前,需要将Tomcat相关包导入,这里你可以全部导入,不再细说,有兴趣可自已研究。
ContainerProvider使有ServiceLoader机制加载ContainerProvider的实现类。并提供WebSocketContainer实例,
在Tomcat上,这个实例为WSWebSocketContainer类。
通过session.getBasicRemote()方法获取RemoteEndpoint.Basic实例来发送消息。
一个简单的WebSocket通信息就完成了。