websocket 和传统意义上的socket编程虽然存在差别,但也存在相通概念,也分服务端和客户端。
主要区别
-
对于websocket,客户端的编写方式是通过JS编写回调函数完成交互;而传统socket,则需要连接端口,通过输入输出流来传递信息,完成交互;
-
传统的socket,服务端则需要绑定端口,通过accept 方法,等待客户端的连接。websocket 规范则把处理细节由web服务器来完成。
数据处理是websocket的一项主要工作。按工作阶段划分,主要包括以下方面。
-
信息发送
-
信息解码
-
信息编码
-
信息接收
按传递数据划分,分为
-
文本信息
-
二进制流信息
-
ping/pong信息
上面的内容,仅限于服务端,websocket html5相关暂时不讨论
1.信息发送( Sending Messages)
表示服务端,将消息传递给客户端(peer)
1.1将消息广播到所有连接客户端
1
2
3
4
5
6
7
8
9
10
11
12
|
@ServerEndpoint
(
"/echoall"
)
public
class
EchoAllEndpoint {
@OnMessage
public
void
onMessage(Session session, String msg) {
//1
try
{
for
(Session sess : session.getOpenSessions()) {
if
(sess.isOpen())
sess.getBasicRemote().sendText(msg);
//2,3
}
}
catch
(IOException e) { ... }
}
}
|
信息发送三步
1.Obtain the Session
object from the connection.(通过在参数中添加Session,获取Session)
2.Use the Session
object to obtain a RemoteEndpoint
object.
3.Use the RemoteEndpoint
object to send messages to the peer.
1.2返回值,作为信息发送
1
2
3
4
5
|
@OnMessage
public
String onMessage(String message,Session session) {
System.out.println(
"Received : "
+ message);
return
message+
"-"
+session.getId();
}
|
2.信息解码和编码(codec)
拿打电话为例,如果电话双方,都用一样的标准普通话沟通,就没必要用翻译器了。如果电话双方,一边用着标准的牛津话,一方操着标准的山东土话,想想也能想想出来,沟通直接乱掉了,这时候必须要用到双方必须都需要翻译器了。websocket的编码和解码部分就是“翻译器”的角色。
编码及解码器位置
可以简单理解为:解码就是反序列化的过程;编码就是序列化的过程。
2.1 使用Encoders 反序列化对象
响应信息需要转换成二进制,才能进行网络传递。
来自j2ee的例子
1.实现Encoder.Text<T> 或Encoder.Binary<T>接口
1
2
3
4
5
6
7
8
9
10
11
|
public
class
MessageATextEncoder
implements
Encoder.Text<MessageA> {
@Override
public
void
init(EndpointConfig ec) { }
@Override
public
void
destroy() { }
@Override
public
String encode(MessageA msgA)
throws
EncodeException {
// Access msgA's properties and convert to JSON text...
return
msgAJsonString;
}
}
|
2.将第一步中新建的Encoder,添加到ServerEndpoint 注解的encoders 属性中
1
2
3
4
5
|
@ServerEndpoint
(
value =
"/myendpoint"
,
encoders = { MessageATextEncoder.
class
, MessageBTextEncoder.
class
}
)
public
class
EncEndpoint { ... }
|
3.使用RemoteEndpoint.Basic or RemoteEndpoint.Async 的sendObject发送对象。
1
2
3
4
|
MessageA msgA =
new
MessageA(...);
MessageB msgB =
new
MessageB(...);
session.getBasicRemote.sendObject(msgA);
session.getBasicRemote.sendObject(msgB);
|
2.2 使用Decoders 序列化信息
将请求信息转换成对象,才方便ServerPoint处理响应
与Encoders处理步骤类似
1.实现Decoder.Text<T>或Decoder.Binary
<T>接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public
class
MessageTextDecoder
implements
Decoder.Text<Message> {
@Override
public
void
init(EndpointConfig ec) { }
@Override
public
void
destroy() { }
@Override
public
Message decode(String string)
throws
DecodeException {
// Read message...
if
(
/* message is an A message */ )
return new MessageA(...);
else if ( /* message is a B message */
)
return
new
MessageB(...);
}
@Override
public
boolean
willDecode(String string) {
// Determine if the message can be converted into either a
// MessageA object or a MessageB object...
return
canDecode;
}
}
|
请注意willDecode方法,决定是否进行解码
2. 新建的Decoder,添加到ServerEndpoint 注解的decoders 属性中
1
2
3
4
5
6
|
@ServerEndpoint
(
value =
"/myendpoint"
,
encoders = { MessageATextEncoder.
class
, MessageBTextEncoder.
class
},
decoders = { MessageTextDecoder.
class
}
)
public
class
EncDecEndpoint { ... }
|
3. 这时候就可以在@OnMessage注解的参数方法中直接使用Decoder.decode返回的对象类型了。
1
2
3
4
5
6
7
8
|
@OnMessage
public
void
message(Session session, Message msg) {
if
(msg
instanceof
MessageA) {
// We received a MessageA object...
}
else
if
(msg
instanceof
MessageB) {
// We received a MessageB object...
}
}
|
具体例子,见我的github项目。https://github.com/janecms/websocket_example
3.信息接收(Receiving Messages)
3.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
|
package
com.sample.websocket.endpoint;
import
javax.websocket.OnMessage;
import
javax.websocket.PongMessage;
import
javax.websocket.Session;
import
javax.websocket.server.ServerEndpoint;
import
java.nio.ByteBuffer;
@ServerEndpoint
(
"/receive"
)
public
class
ReceiveEndpoint {
@OnMessage
public
void
textMessage(Session session, String msg) {
System.out.println(
"Text message: "
+ msg);
}
@OnMessage
public
void
textMessage(Session session, String msg) {
System.out.println(
"Text message: "
+ msg);
}
@OnMessage
public
void
binaryMessage(Session session, ByteBuffer msg) {
System.out.println(
"Binary message: "
+ msg.toString());
}
@OnMessage
public
void
pongMessage(Session session, PongMessage msg) {
System.out.println(
"Pong message: "
+ msg.getApplicationData().toString());
}
}
|
一个奇怪的情况,不同版本的Tomcat,对@OnMessage注解的方法限制有所不同。也进一步说明,websocket规范是个快速变化中的规范。有的直接编译期出错;有的运行时错误。(有可能和我选择不同版本的开发工具有关系,不太确定,一样的代码,表现不同的行为,蹊跷)
需要注意的是被OnMessage注解的三个方法,分别接收不同的消息类型。
3.2 错误代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
@ServerEndpoint
(
"/echo"
)
public
class
EchoEndpoint {
@OnMessage
public
String onMessage(String message,Session session) {
System.out.println(
"Received : "
+ message);
return
message+
"-"
+session.getId();
}
@OnMessage
public
String onMessage2(String message,Session session) {
System.out.println(
"Received : "
+ message);
return
message+
"####"
+session.getId();
}
}
|
代码,报运行时异常。
javax.servlet.ServletException: javax.websocket.DeploymentException: Duplicate annotation
一种消息类型,只能对应一个OnMessage方法。
本文转自 randy_shandong 51CTO博客,原文链接:http://blog.51cto.com/dba10g/1855978,如需转载请自行联系原作者