WebSocket

一、WebSocket的产生

用户希望Web页面可以进行交互,用于解决这个问题的技术是JavaScript,现在Web上有许多的可用的JavaScript框架,在使用极少的JavaScript的情况下就可以创建出丰富的单页面Web——Ajax技术(异步JavaScript和XML)。

在采用了Ajax之后,浏览器中的Web应用程序可以与服务器端的组件进行通信,而不需要改变浏览器页面或者刷新。这个通信过程不需要用户知道,并且它可以用于向服务器发送新数据或者从服务器获得新数据。

但是,浏览器只可以从服务器专区新的数据,但是浏览器并不知道数据什么时候使用,只有服务器知道什么时候有新数据发送到浏览器,而浏览器并不知道。

解决方法1,频繁轮询

频繁轮询服务器获取新数据,以一个固定的频率,通常是每秒一次,浏览器将发送Ajax请求到服务器查询新数据。如果浏览器有新的数据发送到服务器,数据将被添加到轮询请求中一同发送给浏览器(但是大量请求会被浪费)。

解决方法2,长轮询

服务器只有在发送数据时才会响应浏览器(如果浏览器在服务器响应之前有新数据要发送,浏览器就必须要创建一个新的并行请求,或者终止当前的请求;TCP和HTTP规定了连接超时的情况;HTTP存在着强制的连接限制)。

解决方法3,分块编码

服务器可以在不声明内容长度的情况下响应请求。在响应中,每个块的开头一次是:一个用于表示块长度的数字、一系列表示块扩展的可选字符和一个CRLF(回车换行)序列。接着是块包含的数据和另一个CRLF。浏览器将创建一个连接到“下游端点”的长生命连接,并且服务器将使用该连接以块的方式向浏览器发送更新。

解决方法4,Applet和Adobe Flash

创建连接到服务器的普通TCP套接字连接,当浏览器有了新的数据要发送到服务器,它将由浏览器插件暴露出的JavaScript DOM函数调用Java或Flash方法,然后该方法吧数据转发到服务器上。

解决方法5,WebSocket

WebSocket连接首先将使用非正常的HTTP请求以特定的模式访问一个URL,WebSocket是持久的全双工通信协议。在握手完成之后,文本和二进制消息将可以同时在两个方向上进行发送,而不需要关闭和重新连接。

WebSocket的优点:

  1. 连接端口在80(ws)和433(wss),所以不会被防火墙阻塞。
  2. 使用HTTP握手,可以自然地集成到网络浏览器和HTTP服务器上。
  3. 使用ping和pong保持WebSocket一直处于活跃状态。
  4. 当消息启动和它的内容到达时,服务器和客户端都可以知道。
  5. WebSocket在关闭连接时会发送特殊的关闭消息。
  6. 可以支持跨区域连接。

二、WebSocket API

WebSocket并不只是在浏览器和服务器的通信,两个以任何框架编写、支持WebSocket的应用程序都可以创建WebSocket连接进行通信。

WebSocket的Java API包含在javax.websocket中,并指定了一组类和接口包含所有的常见功能。

客户端API

客户端API基于ContainerProvider类和WebSocketContainer、RemoteEndpoint和Session接口构建。

WebSocketContainer提供了对所有WebSocket客户端特性的访问,而ContainerProvider类听了静态的getWebSocketContainer方法用来获取底层WebSocket客户端的实现。

WebSocketContainer提供了4个重载的connectToServer方法,它们都将接受一个URI,用于连接远程终端和初始化握手。

  1. 标注了@ClientEndpoint的任意类型的POJO
  2. 标注了@ClientEndpoint的任意类型的POJO的Class<?>
  3. Endpoint类的实例或者一个Class<? extends EndPoint>。

当握手完成是,connectToServer方法将返回一个Session。

其中WebSocket的Endpoint有3个方法,onOpen、onClose和onError,它们将在这些时间发生时进行调用。

而@ClientEndpoint类标注了@onOpen、@onClose和@onError的方法。

  1. @OnOpen方法可以有:一个可选的Session参数,一个可选的EndpointConfig参数。
  2. @OnClose方法可以有:一个可选的Session参数,一个可选的CloseReason参数。
  3. @OnError方法可以有:一个可选的Session参数,一个可选的Throwable参数。
  4. @OnMessage方法可以有:一个可选的Session参数,其它参数的组合。

这是一个WebSocket创建多人游戏的服务器终端代码:

public class TicTacToeServer
{
private static Map<Long, Game> games = new Hashtable<>();
private static ObjectMapper mapper = new ObjectMapper(); @OnOpen
public void onOpen(Session session, @PathParam("gameId") long gameId,
@PathParam("username") String username)
{
try
{
TicTacToeGame ticTacToeGame = TicTacToeGame.getActiveGame(gameId);
if(ticTacToeGame != null)
{
session.close(new CloseReason(
CloseReason.CloseCodes.UNEXPECTED_CONDITION,
"This game has already started."
));
} List<String> actions = session.getRequestParameterMap().get("action");
if(actions != null && actions.size() == 1)
{
String action = actions.get(0);
if("start".equalsIgnoreCase(action))
{
Game game = new Game();
game.gameId = gameId;
game.player1 = session;
TicTacToeServer.games.put(gameId, game);
}
else if("join".equalsIgnoreCase(action))
{
Game game = TicTacToeServer.games.get(gameId);
game.player2 = session;
game.ticTacToeGame = TicTacToeGame.startGame(gameId, username);
this.sendJsonMessage(game.player1, game,
new GameStartedMessage(game.ticTacToeGame));
this.sendJsonMessage(game.player2, game,
new GameStartedMessage(game.ticTacToeGame));
}
}
}
catch(IOException e)
{
e.printStackTrace();
try
{
session.close(new CloseReason(
CloseReason.CloseCodes.UNEXPECTED_CONDITION, e.toString()
));
}
catch(IOException ignore) { }
}
} @OnMessage
public void onMessage(Session session, String message,
@PathParam("gameId") long gameId)
{
Game game = TicTacToeServer.games.get(gameId);
boolean isPlayer1 = session == game.player1; try
{
Move move = TicTacToeServer.mapper.readValue(message, Move.class);
game.ticTacToeGame.move(
isPlayer1 ? TicTacToeGame.Player.PLAYER1 :
TicTacToeGame.Player.PLAYER2,
move.getRow(),
move.getColumn()
);
this.sendJsonMessage((isPlayer1 ? game.player2 : game.player1), game,
new OpponentMadeMoveMessage(move));
if(game.ticTacToeGame.isOver())
{
if(game.ticTacToeGame.isDraw())
{
this.sendJsonMessage(game.player1, game,
new GameIsDrawMessage());
this.sendJsonMessage(game.player2, game,
new GameIsDrawMessage());
}
else
{
boolean wasPlayer1 = game.ticTacToeGame.getWinner() ==
TicTacToeGame.Player.PLAYER1;
this.sendJsonMessage(game.player1, game,
new GameOverMessage(wasPlayer1));
this.sendJsonMessage(game.player2, game,
new GameOverMessage(!wasPlayer1));
}
game.player1.close();
game.player2.close();
}
}
catch(IOException e)
{
this.handleException(e, game);
}
} @OnClose
public void onClose(Session session, @PathParam("gameId") long gameId)
{
Game game = TicTacToeServer.games.get(gameId);
if(game == null)
return;
boolean isPlayer1 = session == game.player1;
if(game.ticTacToeGame == null)
{
TicTacToeGame.removeQueuedGame(game.gameId);
}
else if(!game.ticTacToeGame.isOver())
{
game.ticTacToeGame.forfeit(isPlayer1 ? TicTacToeGame.Player.PLAYER1 :
TicTacToeGame.Player.PLAYER2);
Session opponent = (isPlayer1 ? game.player2 : game.player1);
this.sendJsonMessage(opponent, game, new GameForfeitedMessage());
try
{
opponent.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
}

服务器API

服务器API依赖于完整的客户端API,它只添加了少数的类和接口,ServerContainer集成了WebSocketContainer,在Servlet环境中调用ServletContext.getAttribute("javax.websocket.server.ServerCOntainer")可以获得ServerContainer实例,在独立运行的应用程序中,需要按照特定的WebSocket实现的指令获取ServerContainer实例。

不过,其实可以使用@ServerEndPoint标注服务器终端类即可,WebSocket实现可以扫描类的注解,并自动选择和注册服务器终端,容器在每次收到WebSocket连接时创建对应终端的实例,在连接关闭之后在销毁实例。

在使用@ServerEndPoint,至少需要制定必须的value特性目标是该终端可以做出像的应用程序相对应的URL:

@ServerEndpoint("/ticTacToe/{gameId}/{username}")

如果应用程序部署到的地址为:http://www.example.org/app,那么该服务器终端会响应地址:ws://www.example.org/app/ticTacToe/1/andre等,然后服务器终端中所有的@OnOpen、@OnClose、@OnError和@OnMessage方法都可以只用@PathParam(“{gameId}/{username}”)标注出一个可选的额外参数,并且其内容为改参数的值(1/andre)。

服务器终端中的时间处理方法将和客户端中的时间处理方法一样工作,区别只存在于握手阶段,之后并没有服务器和客户端的差别。

Java Web高级编程(四)的更多相关文章

  1. Java Web高级编程(二)

    使用会话维持状态 一.会话 为了实现关联同一个用户端的多个请求和这些请求之间数据的共享,需要用到会话,会话用于维持请求和请求之间的状态.从服务器的角度,当用户的Web浏览器打开第一个链接到服务器的套接 ...

  2. Java Web高级编程(一)

    Servlet 一.创建Servlet类 在Java EE中,Servlet用来接收和响应终端用户的请求.Servlet是所有Web应用程序的核心类,是唯一既可以直接处理和响应用户请求,也可以将处理工 ...

  3. Java Web高级编程(三)

    使用过滤器改进应用程序 一.过滤器的目的 过滤器是可以拦截访问资源的请求.资源的响应或者同时拦截两者的应用组件.过滤器可以检测和修改请求和响应,同时也可以拒绝.重定向或转发请求.javax.servl ...

  4. java web高级编程 笔记1

    chapter1:了解web应用程序 web应用程序主要组件: Servlet 过滤器 监听器 JSP chapter2:各类web容器介绍 略 chapter3:Servlet介绍 Servlet是 ...

  5. java web学习总结(四) -------------------HTTP协议

    一.什么是HTTP协议 HTTP是hypertext transfer protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的 ...

  6. C++面向对象高级编程(四)基础篇

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. 一.Static 二.模板类和模板函数 三.namespace 一.Static 静态成员是“类级别”的,也就是它和类的地位等同,而普通成员是“ ...

  7. java web开发入门四(spring)基于intellig idea

    spring 1.spring简介 Spring框架,可以解决对象创建以及对象之间依赖关系的一种框架. 且可以和其他框架一起使用:Spring与Struts,  Spring与hibernate (起 ...

  8. java web 学习十四(JSP原理)

    一.什么是JSP? JSP全称是Java Server Pages,它和servle技术一样,都是SUN公司定义的一种用于开发动态web资源的技术. JSP这门技术的最大的特点在于,写jsp就像在写h ...

  9. Java web切面编程

    在我们的 web开发中  我们在 对公用的 一些方法 我们需要抽取出来   这样达到 代码的冗余   今天 我利用项目上用的AOP的 实际 应用做了一个整理 首先  xml配置  扫描 <?xm ...

随机推荐

  1. 3、C#基础 - C# 的 Hello World

    HelloWorld,是学会程序的通用起手式了,哈哈. 打开我们的VS:文件\新建\项目 选择"控制台应用(.NET Framework)",名称改为"HelloWorl ...

  2. Dos命令的巧用

    豪华绚丽的Windows让人们把DOS抛到遥远的记忆角落,然而,真正有价值的东西不会轻易退出历史的舞台.很多人都已经习惯于 Windows的图形化用户界面,熟不知古老的DOS命令却可以轻易的办到很多事 ...

  3. Sqoop2安装记录

    我是採用的源代码编译的包安装的, 主要是考虑到会对部分功能做裁剪或增强, 详细源代码编译方式能够參考另外一篇博文<编译Sqoop2错误解决>.然后从dist/target文件夹下拷贝sqo ...

  4. Comparable和Comparator的差别

    原文地址:http://leihuang.org/2014/11/16/Comparable-Vs-Comparator/ Comparable和Comparator都是用来实现集合中元素的比較.排序 ...

  5. Android自己定义组件之日历控件-精美日历实现(内容、样式可扩展)

    需求 我们知道.Android系统本身有自带的日历控件,网络上也有非常多开源的日历控件资源.可是这些日历控件往往样式较单一.API较多.不易于在实际项目中扩展并实现出符合详细样式风格的,内容可定制的效 ...

  6. this问题

    this问题 弹出层是一种很常见的页面显示部件,利用require js的模块化可以使部分功能组件重用性更高.以弹出层为例,探讨关于this的问题 首先来看这样一段代码 Html部分代码 <bo ...

  7. ASP.NET Core 使用 Redis 客户端

    Mac OS 安装 Redis(用于连 Redis 服务器,方便查看数据):https://redis.io/topics/quickstart wget http://download.redis. ...

  8. 用枚举来处理java自定义异常

    在系统开发过程中,总少不免要自己处理一些异常信息,然后将异常信息变成友好的提示返回到客户端的这样一个过程,之前都是new一个自定义的异常,当然这个所谓的自定义异常也是继承RuntimeExceptio ...

  9. Python数据可视化——使用Matplotlib创建散点图

    Python数据可视化——使用Matplotlib创建散点图 2017-12-27 作者:淡水化合物 Matplotlib简述: Matplotlib是一个用于创建出高质量图表的桌面绘图包(主要是2D ...

  10. Html转JSP样式变型问题解决

    一.问题描述 在我没将写好的 html 页面转变为 java web 中的 JSP 页面时.有时会发现,我们将 css .js 都引入到了页面中.当样式和我们想象的不一样,那么我们就要去解决这个问题. ...