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. java 接口测试

    学习java 时间也不算太长,大学有点基础,学起来也顺手多了,其实我感觉更是因为学习完python之后,在学习别的语言的时候就简单多了,几乎所有的语言学起来都差不多.前几篇写过的都是python接口测 ...

  2. 网页设计——3.html运行原理,基本标签

    一.运行原理 (1)本地运行 自己写的html网页用电脑上浏览器打开,就是实现了本地运行. 可以把浏览器当成一个软件,可以打开html文件. (2)远程访问 远程访问就是你用自己电脑在网上浏览一些网站 ...

  3. Python的字典

    1.  Python的字典 1.1.  字典的定义 在Python中,字典是一种key-value的数据类型,也是唯一的映射类型:字典还是另一种可变容器类型,且可存储任意类型对象,其中也可包括其他容器 ...

  4. 查看主机DNSserver

    一.Nslookup(name server lookup)( 域名查询):是一个用于查询 Internet 域名信息或诊断DNS server问题的工具.nslookup能够指定查询的类型,能够查到 ...

  5. HDU_1698 Just a Hook(线段树+lazy标记)

    pid=1698">题目请点我 题解: 接触到的第一到区间更新,须要用到lazy标记.典型的区间着色问题. lazy标记详情请參考博客:http://ju.outofmemory.cn ...

  6. mac os x 触摸板点击无效

    macbook默认轻击触摸板无效,这样是为了防止误点击.可是习惯了windows笔记本的我对这一设置非常不习惯. 能够在"system preference"的"Trac ...

  7. 谈谈PBOC3.0中使用的国密SM2算法

    转载请注明出处 http://blog.csdn.net/pony_maggie/article/details/39780825 作者:小马 一 知识准备 SM2是国密局推出的一种他们自己说具有自主 ...

  8. 003Java语言环境搭建

    JRE,JDK JRE(Java Runtime Environment java运行环境):包括java虚拟机和java程序所需要的核心类库, 如果要运行一个开发好的java程序,计算机中只需要安装 ...

  9. ASP.NET Core 指定环境发布(hosting environment)

    ASP.NET Core 应用程序发布命令: dotnet publish [<PROJECT>] [-f|--framework] [-r|--runtime] [-o|--output ...

  10. 一个简单的java网络通信例子

    先建立2个项目,分别是请求端和响应端,端口改成不一样的就行,比如请求端是8080,响应端是8081 废话不多说,直接上代码 请求端的Controller层 @GetMapping("/req ...