websocket 的概念如下链接:

点击这里

Java Websocket

如果要使用 Java 搭建一个 websocket 服务的话,在 J2EE7 当中给出了 websocket 的规范,需要创建一个 web 项目,并且在 pom 当中引入 jar:

1
2
3
4
5
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>

JSR 356 是 websocket 的规范说明,并且也是 JavaEE7 的一部分,首先了解下几个概念。

Endpoint

Endpoint 代表可以处理 websocket 当中的对话。一般有两种方式可以创建 Endpoint:

  • 基于注解
  • 基于继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@ServerEndpoint("/wstest")
public class Test1EndPoint {

@OnClose
public void out(Session session, CloseReason closeReason) {
}

@OnOpen
public void onOpen(Session session, EndpointConfig config) {
}

@OnMessage
public void onMessage(String message, Session session) {
}

@OnError
public void onError(Session session, Throwable thr) {
}
}



public class TestEndPoint extends Endpoint {

@Override
public void onOpen(Session session, EndpointConfig arg1) {
}

@Override
public void onClose(Session session, CloseReason closeReason) {
super.onClose(session, closeReason);
}

@Override
public void onError(Session session, Throwable thr) {
super.onError(session, thr);
}
}

Session

Session 表示 EndPoint 与 Client 之间的一系列交互。对于与一个 EndPoint 交互的每个 Client,有唯一一个 Session 实例表示该交互。

从上面的代码示例当中,我们可以看到 Session 的生命周期,onOpen -> onMessage -> onClose。

注解 ServerEndpoint

看下 ServerEndpoint 的详细代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ServerEndpoint {

//表示映射的地址,必须以‘/’开头,是否以‘/’结尾都行
public String value();

public String[] subprotocols() default {};

//解码器,从client来的消息先走解码器
public Class<? extends Decoder>[] decoders() default {};

//编码器,从server返回的消息走编码器
public Class<? extends Encoder>[] encoders() default {};

//ServerEndpointConfig 的配置信息,可以配置握手协议等信息
public Class<? extends ServerEndpointConfig.Configurator> configurator() default ServerEndpointConfig.Configurator.class;
}

再来看 decoders 的几个基本用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class MyDecoder implements Decoder.Text<JSONObject> {

@Override
public void init(EndpointConfig config) {
System.out.println("====MyDecoder====");
}

@Override
public void destroy() {

}

@Override
public JSONObject decode(String s) throws DecodeException {
System.out.println("decode====" + s);
return JSONObject.fromObject(s);
}

@Override
public boolean willDecode(String s) {
System.out.println("willDecode====" + s);
return true;
}
}


@ServerEndpoint(value = "/testws", decoders=MyDecoder.class)
public class WsServiceEndpoint {

@OnClose
public void out(Session session, CloseReason closeReason) {
}

@OnOpen
public void onOpen(Session session, EndpointConfig config) {
System.out.println("onOpen======");
}

@OnMessage
public void onMessage(JSONObject message, Session session) {
try {
System.out.println(message);
session.getBasicRemote().sendText("go");
} catch (IOException e) {
e.printStackTrace();
}
}

}

代码比较简单,不多解释,编码也是如此。

ServerEndpoint 的 value 属性,可以类似于 spring mvc 当中的 pathvariable :

1
2
3
4
5
6
7
8
9
@ServerEndpoint("/bookings/{guest-id}")
public class BookingServer {
@OnMessage
public void processBookingRequest(
@PathParam("guest-id") String guestID, String message,
Session session) {
// process booking from the given guest here
}
}

消息类型

从 Decoder 的几个接口可以看出来,websocket 可以处理的数据类型:

  • Binary(ByteBuffer)
  • BinaryStream(InputStream)
  • Text(String)
  • TextStream(Reader)

部署

有两种方式部署 websocket:

  • 使用 war 包,放到 web 容器当中
  • 使用 ServerContainer 部署编程式的 EndPoint

使用 war 包的情况,web 容器会自动扫描:

  1. 实现 javax.websocket.ServerApplicationConfig 的类
  2. 有 javax.websocket.server.ServerEndpoint 注解的类
  3. 实现 javax.websocket.Endpoint 的类

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyApplicationConfigOne implements ServerApplicationConfig {
public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> endpointClasses);
Set<Class<? extends Endpoint>> s = new HashSet<Class<? extends Endpoint>>;
s.add(ProgrammaticEndpointOne.class);
return s;
}

public Set<Class> getAnnotatedEndpointClasses(Set<Class<?>> scanned);
Set<Class<?>> s = new HashSet<Class<?>>;
s.add(AnnotatedEndpointOne.class);
return s;
}
}

【参考资料】

  1. https://tyrus.java.net/documentation/1.5/index/getting-started.html
  2. http://www.code123.cc/468.html

—EOF—