文中案例在apache-tomcat-8.0.15和jdk1.8.0_25环境下开发,运行。不过标题上是Java7,并不造成影响,代码中没有涉及任何与jdk1.8.x相关的代码。另外之所以要讲清软件版本,一是为了保证案例能够有个明确的实现背景,二是websocket实现tomcat7.x与tomcat8.x有很大差异。
在JavaEE规范集中我们这里主要看Java API for WebSocket(JSR 356)。Websocket-api提供了Java实现Websocket的接口,其中最重要的几个类和注解如下图:
上图解释:
1.最上面4个注解OnClose,OnError, OnOpen, OnMessage用来标注一个POJO用来处理WebSocket请求的方法;
2.Endpoint和EndpointConfig分别定义了端点和端点相关配置的接口方法;
3.ClientEndpoint和ServerEndpoint分别定义了客户端和服务器端端点的接口方法;
4.Decoder和Encoder分别是解码和编码的接口方法定义;
5.Session是与Endpoint相关的WebSocket Session接口方法定义。
另外还有其它接口,这里作为初步了解Java websocket api仅列出最有必要的一些。
Tomcat8.x提供了JavaEE7的标准实现,其中WebSocket 1.1规范给予实现。在使用其开发的依赖环境是Tomcat8.x JDK7。Tomcat8.x提供了服务器端的实现,客户端实现需要借助其他实现如java_websocket。
Tomcat8.x对WebSocket实现感觉很明朗化了,既然API中定义了WebSocket相关的注解和Session那么Tomcat8.x实现中自然会有相应的处理和实现,下图是简单的关系描述。
上图解释:
tomcat的WsSession类实现了Java WebSocket API中的Session接口
PojoEndpointBase以及其子类处理与Endpoint相关的类或注解
EndpointConfig,Endpoint都与Session的实现类之间存在依赖关系
通过了解Java Websocket API和Tomcat8.x对其的实现,认为使用Java WebSocket需要熟悉其中关键类或接口如:Endpoint,EndpointConfig,Client和Server,Encoder和Decoder,Session,MessageHandler。
上面大致介绍了Tomcat8.x对Java Websocket的实现,有关WebSocket规范的了解信息可以参考:
RFC6455: http://tools.ietf.org/html/rfc6455
WebSocket服务器端实现:https://java.net/projects/websocket-spec/pages/WebSocketAPIs
下面是通过代码示例来展示基于WebSocket的实时聊天
1.服务器端实现
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
package
t8j8.examples;
import
java.io.IOException;
import
java.util.Set;
import
java.util.concurrent.CopyOnWriteArraySet;
import
javax.websocket.OnClose;
import
javax.websocket.OnError;
import
javax.websocket.OnMessage;
import
javax.websocket.OnOpen;
import
javax.websocket.Session;
import
javax.websocket.server.PathParam;
import
javax.websocket.server.ServerEndpoint;
@ServerEndpoint
(value =
"/ws/chat/{nickName}"
)
public
class
Chat {
/**
* 连接对象集合
*/
private
static
final
Set<Chat> connections =
new
CopyOnWriteArraySet<Chat>();
private
String nickName;
/**
* WebSocket Session
*/
private
Session session;
public
Chat() {
}
/**
* 打开连接
*
* @param session
* @param nickName
*/
@OnOpen
public
void
onOpen(Session session,
@PathParam
(value =
"nickName"
) String nickName) {
this
.session = session;
this
.nickName = nickName;
connections.add(
this
);
String message = String.format(
"System> %s %s"
,
this
.nickName,
" has joined."
);
Chat.broadCast(message);
}
/**
* 关闭连接
*/
@OnClose
public
void
onClose() {
connections.remove(
this
);
String message = String.format(
"System> %s, %s"
,
this
.nickName,
" has disconnection."
);
Chat.broadCast(message);
}
/**
* 接收信息
*
* @param message
* @param nickName
*/
@OnMessage
public
void
onMessage(String message,
@PathParam
(value =
"nickName"
) String nickName) {
Chat.broadCast(nickName +
">"
+ message);
}
/**
* 错误信息响应
*
* @param throwable
*/
@OnError
public
void
onError(Throwable throwable) {
System.out.println(throwable.getMessage());
}
/**
* 发送或广播信息
*
* @param message
*/
private
static
void
broadCast(String message) {
for
(Chat chat : connections) {
try
{
synchronized
(chat) {
chat.session.getBasicRemote().sendText(message);
}
}
catch
(IOException e) {
connections.remove(chat);
try
{
chat.session.close();
}
catch
(IOException e1) {
}
Chat.broadCast(String.format(
"System> %s %s"
, chat.nickName,
" has bean disconnection."
));
}
}
}
}
|
说明:
代码中ServerEndpoint注解的value值中有个nickName的参数占位符,该参数占位符称为路径参数,路径参数可以通过方法参数注解(@PathParam)进行设置并且获取;
在Endpoint注解的类中OnClose,OnOpen,OnError注解的方法只有一个,OnMessage注解的方法可以有多个这个容易理解因为WebSocket可能处理的信息有text,binary,textStream等。
2.客户端实现
如果使用Maven的话,可以添加java_websocket jar的依赖。
1
2
3
4
5
6
|
<
dependency
>
<
groupId
>org.java-websocket</
groupId
>
<
artifactId
>Java-WebSocket</
artifactId
>
<
version
>1.3.0</
version
>
<
scope
>runtime</
scope
>
</
dependency
>
|
如果开发Web客户端则可以选择支持WebSocket的浏览器,使用HTML5技术。
下面是使用java_websocket来实现客户端的代码:
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
50
|
package
t8j8.examples.client;
import
java.net.URI;
import
java.net.URISyntaxException;
import
java.util.Scanner;
import
org.java_websocket.client.WebSocketClient;
import
org.java_websocket.drafts.Draft_17;
import
org.java_websocket.handshake.ServerHandshake;
public
class
TestTocatWebSocket {
public
static
void
main(String[] args)
throws
URISyntaxException {
String url =
"ws://localhost:8080/t8j8/ws/chat/"
+ args[
0
];
WebSocketClient wc =
new
WebSocketClient(
new
URI(url),
new
Draft_17()) {
@Override
public
void
onOpen(ServerHandshake handshakedata) {
System.out.println(handshakedata.getHttpStatusMessage());
}
@Override
public
void
onMessage(String message) {
System.out.println(message);
}
@Override
public
void
onError(Exception ex) {
}
@Override
public
void
onClose(
int
code, String reason,
boolean
remote) {
}
};
wc.connect();
while
(
true
) {
Scanner scanner =
new
Scanner(System.in);
String message = scanner.nextLine();
if
(message.equals(
"q"
)) {
wc.close();
break
;
}
scanner.close();
wc.send(message);
}
}
}
|
说明:
客户端中要说明的是new Draft_17()对象的创建,通过类名可以得知草案的意思。由于Websocket标准迟迟没有发布,因而之前实现都是依据草案进行,而这里的Draft_17对应的WebSocket版本正是:【"Sec-WebSocket-Version", "13" 】可惜,java_websocket包迟迟没有更新内容,名称出现误解人的地方。
3.开始聊天
部署服务器端程序,启动客户端程序(需要添加nickName的参数)。
下面是聊天的截图,分别为Tom和Jack。
至此,关于使用Tomcat8和JDK7基于WebSocket实现的聊天示例就结束了。
本文转自 secondriver 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1584957,如需转载请自行联系原作者