WebSocket和Java

WebSocket是一项很酷的新技术,它允许浏览器与服务器之间进行实时双向通信,而几乎没有开销。 我在这里想要做的是,提供一个非常简洁但足够全面的概述,以介绍如何开始使用该技术。 因此,从以下几件事开始:

  • 在浏览器和服务器之间打开了一个tcp套接字连接,并且各方可以向另一方发送消息(即,只要有可用,服务器就可以推送数据-无需轮询,长时间轮询,iframe等)。
  • 并非所有浏览器都支持它-IE 10是第一个支持它的IE版本,Android仍然存在问题。 幸运的是,如果不支持WebSocket,则可以使用SockJS ,它可以回溯到其他推式仿真。
  • 并非所有代理服务器都支持/允许它,因此可能需要再次进行回退
  • 适用于游戏,交易应用程序,以及实际上任何需要服务器将数据推送到浏览器的事物
  • Java具有标准的API(JSR-356) ,您可以在服务器上使用它来处理WebSocket连接。
  • Spring在Java API之上提供了一个 API。 spring支持的好处是它具有对SockJS的服务器端支持,您可以轻松使用依赖注入。 Spring还为消息驱动的体系结构提供了STOMP支持 。 这两篇Spring文章都包含指向我推荐的GitHub示例项目的链接。

在继续一些示例代码之前,这里是套接字的生命周期,包括客户端和服务器(假设上述API之一):

  1. 浏览器发送带有特殊升级头的HTTP请求,其值是“ websocket”。
  2. 如果服务器“说” webocket,它将以状态101(交换协议)答复。 从现在开始,我们不再使用HTTP
  3. 当服务器接受tcp套接字连接时,将调用初始化方法,并在其中传递当前的websocket会话。 每个套接字都有一个唯一的会话ID。
  4. 每当浏览器向服务器发送消息时,就会在获取会话和消息有效负载的地方调用另一种方法。
  5. 基于某些有效负载参数,应用程序代码执行几种操作之一。 有效负载格式完全取决于开发人员。 但是,通常,它是一个JSON序列化的对象。
  6. 每当服务器需要发送消息时,它都需要获取会话对象,并使用它来发送消息。
  7. 当浏览器关闭连接时,会通知服务器,以便它可以清除与特定会话相关的任何资源。

当前,没有API或框架支持基于注释的路由。 Java API支持基于注释的终结点处理程序,但是它为每个连接URL提供一个类,并且通常您希望在单个连接上执行多个操作。 即,您连接到ws://yourserver.com/game/,然后要传递“ joinGame”,“ leaveGame”消息。 同样,服务器需要发回不止一种消息。 我的实现方式是通过一个枚举,包含所有可能的动作/事件类型,并使用switch构造确定要调用的内容。

因此,我决定为我的算法音乐作曲家制作一个简单的游戏 。 它使用的是Spring API。 这是我在我所工作的公司中所做的相关演示的幻灯片 。 下面是一些示例代码:

@Component
public class GameHandler extends WebSocketHandlerAdapter {
   private Map players = new ConcurrentHashMap<>();
   private Map playerGames = new ConcurrentHashMap<>();

   @Override
   public void afterConnectionEstablished(WebSocketSession session) throws Exception {
       Player player = new Player(session);
       players.put(session.getId(), player);
   }

@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
    leaveGame(session.getId());
}

@Override
protected void handleTextMessage(WebSocketSession session, TextMessage textMessage) throws Exception {
   try {
      GameMessage message = getMessage(textMessage); //deserializes the JSON payload

      switch(message.getAction()) { 
        case INITIALIZE: initialize(message, session); break;
        case JOIN: join(message.getGameId(), message.getPlayerName(), session); break;
        case LEAVE: leave(session.getId()); break;
        case START: startGame(message); break;
        case ANSWER: answer(message, session.getId()); break;
      }
    } catch (Exception ex) {
    logger.error("Exception occurred while handling message", ex);
  }
}

让我们看一个示例服务,服务器需要在其中向客户端发送消息。 让我们以一个玩家加入游戏为例,而所有其他玩家都需要收到新的通知。 系统中的核心类是Game,其中包含玩家列表,并且如您所见,Player包含对WebSocket会话的引用。 因此,当玩家加入游戏时,将调用以下Game方法:

public boolean playerJoined(Player player) {
   for (Player otherPlayer : players.values()) {
      otherPlayer.playerJoined(player);
   }
   players.put(player.getSession().getId(), player);
   return true;
}

然后player.playerJoined(..)通过基础连接发送一条消息,通知浏览器新玩家加入:

public void playerJoined(Player player) {
   GameEvent event = new GameEvent(GameEventType.PLAYER_JOINED);
   event.setPlayerId(player.getSession().getId()); 
   event.setPlayerName(player.getName());
   try {
      session.sendMessage(new TextMessage(event.toJson()));
   } catch (IOException e) {
      new IllegalStateException(e);
   }
  }

从服务器向浏览器发送消息也可能由计划的作业触发。

关键是要保留所有已连接浏览器的列表,以便可以将信息发送回去。 该列表可以是一个静态字段,但是对于单例spring bean,则不需要。

现在,两个重要方面–安全性和身份验证。 这是Heroku的一篇不错的文章 ,同时讨论了两者。 如果有任何敏感内容,您应该首选wss(相对于TLS,它是websocket)。 您还应该在两端验证您的输入,并且不应该依赖Origin标头,因为攻击者可能很容易欺骗浏览器。

身份验证可以依赖于HTTP会话cookie,但是显然,有些人更喜欢实现自己的类似于cookie的工作流,以获取短暂的令牌,该令牌可用于执行经过身份验证的操作。

WebSocket使DDD变得自然。 您不再需要使用贫血对象-您的对象具有各自的状态,并且在该状态下执行操作。 与此相关的是,websocket应用程序更易于测试。

这是开发WebSocket应用程序时要记住的一般事项。 请注意,您不必在所有地方都使用WebSocket –我将其仅限于需要“推送”的功能。

总体而言,WebSocket是一项很好的有趣技术,有望淘汰所有hacky推送仿真。

参考:来自Java出现日历博客的JCG合作伙伴 Glamdring的WebSocket和Java

翻译自: https://www.javacodegeeks.com/2013/12/websocket-and-java.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值