Websocket介绍  

  在一个 WebSocket应用中, 服务器发布一个 WebSocket端点, 客户端使用这个端点的URI来连接服务器。建立连接之后,websocket协议是对称的;客户端和服务器可以在连接打开的任何时间相互发送消息,而且它们可以在任何时间关闭连接。客户端总是只连接到一 个服务器,而服务器可以接受多个客户端的连接。
  WebSocket协议有两部分: 握手和数据传输。 客户端使用一个 WebSocket端点的 URI向它发送一个请求,发起握手过程。这个握手过程与现有基于 HTTP的基石出设施是兼容的:web服务器把它解释为一个 HTTP连接升级请求。
WebSocket支持文本消息(编码为 UTF-8 )和二进制消息。 WebSocket中的控制帧是close、 ping和pong(对ping帧的响应)。 ping和 pong帧可能还包含应用数据。websocket端点由采用以下形式的 URI表示:

  • ws://host:port/path?query
  • wss://host:port/path?query    

ws模式表示一个未加密的 WebSocket连接, wss模式表示一个加密的连接。 port是可选的;对于未加密的连接,默认的端口号是80,对于加密连接。默认的端口号是443。 path部分指示服务器中一个端点的位置。 query部分是可选的。现代 web浏览器实理了 WebSocket协议, 并提供一个 Javascript API来连接端点,发送消息,以及为WebSocket事件指定回调方法(如打开连接、接收消息、关闭连接)。

在Java 中创建WebSocket应用  

WebSocket Java API包括以下包:

  • javax.websocket.server 包含创建和配置服务端点的注解、类和接口。
  • javax.websocket包含客户端和服务端点公共的注解、类、和接口。

  WebSocket端点是javax.websocket.Endpoint美的实例. WebSocket Java API允许创建两类端点: 可编程端点和注解端点。

可编程端点

  通过扩展Endpoint类来创建一个端点:

package com.zhang;
import java.io.IOException;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
public class EchoEndpoint extends Endpoint{
@Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MessageHandler.Whole<String>() {
@Override
public void onMessage(String msg) {
try {
session.getBasicRemote().sendText("echo:" + msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}); }
}

  在父类Endpoint有三个生命周期方法:onOpen、onClose、和onError,所以只需实现onOpen即可,后面两个默认为空操作。
  创建完端点后,还需要把它启动起来,是通过ServerContainer.addEndpoint来启动。

package com.zhang;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import javax.websocket.DeploymentException;
import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
@WebListener
public class EndpointListener implements ServletContextListener{
@Override
public void contextInitialized(ServletContextEvent context) {
ServerContainer container = (ServerContainer) context.getServletContext()
.getAttribute(ServerContainer.class.getName());
ServerEndpointConfig config = ServerEndpointConfig.Builder.create(EchoEndpoint.class, "/echo").build();
try {
container.addEndpoint(config);
} catch (DeploymentException e) {
e.printStackTrace();
}
} @Override
public void contextDestroyed(ServletContextEvent arg0) { }
}

  上面代码中,ServerContainer是通过context.getServletContext().getAttribute(ServerContainer.class.getName())来获得,这个在容器启动后,放在ServletContext中

注解端点

package com.zhang;
import java.io.IOException;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
@ServerEndpoint("/echo")
public class EchoEndpoint{
@OnMessage
public void onMessage(Session session,String msg) {
try {
session.getBasicRemote().sendText("echo:" + msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}

  用注解就比较简洁,不用通过在监听器里添加端点,容器启动后,自动添加端点。

收发二进制消息

  服务端接收消息onMessage(String msg),还是发消息session.getBasicRemote().sendText(“echo:”);都是字符串类,如果要收发二进制的内容,只需把参数名改成ByteBuffer或byte[]数组,例如onMessage(Session session,ByteBuffer buffer) {,session.getBasicRemote().sendBinary(ByteBuffer buffer)

维持客户端状态

  由于容器会为每一个连接创建一个端点类实例, 所以可以定义并使用实例变量来存储客户端状态信息。另外, session.getUserProperties方法提供了一个可修改的映射来存储用户属性。

使用编码器和解码器

  WebSocket Java API使用编码器和解码器为 WebSocket消息和定制 Java类型之间的转换提供支持。编码器取一个 Java对象,生成一种可以作为 WebSocket消息传输的表示;例如编码器通常会生成 JSON、 XML或二进制表示。解码器完成相反的功能,它读取一个WebSocket消息, 并创建一个 Java对象。这种机制可以简化 WebSocket应用, 因为这样可以使业务逻辑与对象的串行化和逆串行化解耦合。

编码器

  在端点中实现以下某个接口:

  • 对于文本消息实现 Enooder.Text;
  • 对于二进制消息实现 Encoder.Binary

以下是把Java编码成JSON字符串

package com.zhang.quick;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;
import com.alibaba.fastjson.JSONObject;
public class TextEncoder implements Encoder.Text<ChatMessage>{
@Override
public String encode(ChatMessage chat) throws EncodeException {
return JSONObject.toJSONString(chat);
}
...
}

在@ServerEndpoint注解中添加encoders

@ServerEndpoint(value="/chat",encoders={TextEncoder.class,MessageEncoder.class})
public class ChatEndpoint {

可以看到encoders是个Class数组,说明是可以有多个编码器。

解码器

  实现以下某个接口

  • 对于文本消息实现Decoder.Text。
  • 对于二进制消息实现Decoder.Binary。
public class MessageDecoder implements Decoder.Text<ChatMessage>{
@Override
public ChatMessage decode(String json) throws DecodeException {
return JSON.parseObject(json, ChatMessage.class);
}
...
}

接下来,为@ServerEndpoint注解增加decoder参数
在@ServerEndpoint注解中添加encoders

@ServerEndpoint(value="/chat",encoders={TextEncoder.class,MessageEncoder.class}
,decoders={MessageDecoder.class})
public class ChatEndpoint {

WebSocket JavaScript编程方式

var webSocket = new WebSocket(
'ws://localhost:8080/websocket/echo');
webSocket.onerror = function(event) {
onError(event)
};
webSocket.onopen = function(event) {
onOpen(event)
};
webSocket.onmessage = function(event) {
onMessage(event)
};
function onMessage(event) {
document.getElementById('messages').innerHTML += '<br />'
+ event.data;
}
function onOpen(event) {
document.getElementById('messages').innerHTML = 'Connection established';
}
function onError(event) {
alert(event.data);
}
function start() {
webSocket.send('hello');
return false;
}

代码中var webSocket = new WebSocket(‘ws://localhost:8080/websocket/echo’);里只是创建一个WebSocket对象,没有真正连接到服务器,只有当调用WebSocket.send时才开始连接。

参考资料

  • 《Java EE 7权威指南》

最新文章

  1. day7_subprocess模块和面向对象,反射
  2. Wen前端性能优化
  3. css高度自适应
  4. SingalR--介绍
  5. OC基础--Hello Shit
  6. EffectiveJava——接口优于抽象类
  7. (五)转载:通用定时器PWM输出
  8. git的某些默认行为--会推送pull的内容,即使commit的时候不显示
  9. IOS下载资源zip到本地然后读取
  10. sublime 3 3083验证码
  11. Shell遍历文件,对每行进行正则匹配
  12. Extjs 兼容IE10
  13. hdu 1587
  14. 【Android】创建Popwindow弹出菜单的两种方式
  15. POJ 1696 Space Ant 极角排序(叉积的应用)
  16. flex布局元素操作详情
  17. JavaEE三大框架整合
  18. Uncaught DOMException: Failed to construct &#39;WebSocket&#39;: The URL &#39;/qibao/websocket/service1000&#39; is invalid.
  19. 解码字符串 Decode String
  20. python实现常量const

热门文章

  1. 浏览器兼容性-JS篇
  2. 深入理解定位父级offsetParent及偏移大小
  3. JDBC三层架构
  4. SPRING SECURITY 拦截静态资源
  5. ACL
  6. $.each()
  7. [ACM_水题] Yet Another Story of Rock-paper-scissors [超水 剪刀石头布]
  8. protobuf-net
  9. 三星电视删除USB播放记录
  10. Java如何连接到MySQL数据库的
  11. [转] Nginx + CGI/FastCGI + C/Cpp
  12. Android -------- 用XmlPullParser解析器解析XML文件
  13. 【题解】P2922 [USACO08DEC]秘密消息Secret Message
  14. linux下python安装
  15. JAVA多线程之线程间的通信方式
  16. HDU-1078.FatMouseandCheese(线性dp + dfs)
  17. flex 布局下,css 设置文本不换行时,省略号不显示的解决办法
  18. timerTask任务定时器
  19. linq筛选唯一
  20. .NET Core中使用Dapper操作Oracle存储过程最佳实践