作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
托马斯Dąbrowski的头像

托马斯Dąbrowski

Tomasz拥有超过10年的Java应用开发经验, 为惠普这样的公司工作过, 以及硅谷的创业公司.

以前在

惠普(hewlett - packard)
分享

网络Socket协议是使应用程序处理实时消息的方法之一. 最常见的替代方案是长轮询和服务器发送的事件. 每种解决方案都有其优点和缺点. 在本文中,我将向您展示如何使用春天 Boot Framework实现尚. 我将介绍服务器端和客户端设置, 我们将使用STOMP在网络Socket协议上相互通信.

服务器端将完全用Java编码. 但, 在客户端的情况下, 我将展示用Java和JavaScript (SockJS)编写的代码片段, 通常, 尚客户端嵌入在前端应用程序中. 代码示例将演示如何使用发布-订阅模型向多个用户广播消息,以及如何仅向单个用户发送消息. 在本文的下一部分中, 我将简要讨论如何保护网络Socket,以及如何确保我们基于网络Socket的解决方案在环境不支持网络Socket协议的情况下仍能正常运行.

请注意,这里只简要介绍保护尚的主题,因为它是一个足够复杂的主题,可以单独写一篇文章. 由于这一点,以及我在书中提到的其他几个因素 生产中的网络Socket? 节最后, 我建议在生产环境中使用此设置之前进行修改,一直读到最后,以获得具有适当安全措施的生产就绪设置.

网络Socket和STOMP协议

网络Socket协议允许在应用程序之间实现双向通信. 重要的是要知道HTTP仅用于初始握手. 事情发生后, HTTP连接升级为网络Socket使用的新打开的TCP/IP连接.

网络Socket协议是一个相当低级的协议. 它定义了如何将字节流转换为帧. 一个帧可以包含文本或二进制消息. 因为消息本身不提供有关如何路由或处理它的任何附加信息, 如果不编写额外的代码,很难实现更复杂的应用程序. 幸运的是, 网络Socket规范允许使用在更高层次上运行的子协议, 应用程序级别. 其中一个由春天框架支持的是STOMP.

STOMP是一种简单的基于文本的消息传递协议,最初是为Ruby等脚本语言创建的, Python, 和Perl连接到企业消息代理. 多亏了STOMP, 使用不同语言开发的客户端和代理可以相互发送和接收消息. 网络Socket协议有时被称为用于网络的TCP. 类似地,STOMP被称为面向网络的HTTP. 它定义了一些映射到尚帧的帧类型,例如.g., 连接, 订阅, 退订, , or 发送. 一方面, 这些命令在管理通信时非常方便, 另一方面, 它们允许我们实现具有更复杂功能(如消息确认)的解决方案.

服务器端:春天 Boot和尚

构建网络Socket服务器端, 我们将利用春天 Boot框架,它可以显著加快Java中独立和web应用程序的开发. 春天 Boot包括 spring-网络Socket 模块,该模块兼容Java 网络Socket API标准(jsr - 356).

用春天 Boot实现网络Socket服务器端并不是一项非常复杂的任务,只包括几个步骤, 我们会一个一个地讲.

步骤1. 首先,我们需要添加网络Socket库依赖项.


  org.springframework.boot            
  spring-boot-starter-websocket

如果您计划使用JSON格式传输消息, 您可能还需要包括GSON或Jackson依赖项. 很可能,您还需要一个安全框架,例如春天 security.

步骤2. 然后,我们可以配置春天来启用网络Socket和STOMP消息传递.

配置
@Enable网络SocketMessageBroker
网络SocketConfig实现网络SocketMessageBrokerConfigurer {

  @Override
  registerStompEndpoints(注册点
   注册表){
    注册表.addEndpoint (" / mywebsockets”)
        .setAllowedOrigins(“mydomain.com”).withSockJS ();
  }

  @Override
  公共无效配置ureMessageBroker(MessageBrokerRegistry配置){ 
    配置.enableSimpleBroker(“/主题/”,“队列/”);
    配置.setApplicationDestinationPrefixes(“/ 应用程序”);
  }
}

该方法 配置ureMessageBroker 做两件事:

  1. 在内存中创建 消息代理 具有用于发送和接收消息的一个或多个目的地. 在上面的例子中,定义了两个目的地前缀: 主题队列. 它们遵循约定,即通过发布-订阅模型将消息传递到所有订阅客户端的目的地应该带有前缀 主题. 另一方面,私有消息的目的地通常以。作为前缀 队列.
  2. 定义前缀 应用程序 的方法处理的目标 @MessageM应用程序ing 你会在控制器中实现哪个. 控制器在处理消息后将其发送给代理.

春天 Boot 网络Socket:如何在服务器端处理消息

消息是如何在服务器端处理的(来源: 春天文档)


回到上面的代码片段,您可能已经注意到对该方法的调用 withSockJS ()-启用SockJS回退选项. 为了言简意赅, 即使网络浏览器不支持网络Socket协议,它也可以让我们的网络Socket工作. 我将进一步更详细地讨论这个主题.

还有一件事需要澄清——我们为什么打电话 setAllowedOrigins () 方法。. 这通常是必需的,因为网络Socket和SockJS的默认行为是只接受同源请求. So, 如果您的客户机和服务器端使用不同的域, 需要调用此方法以允许它们之间的通信.

步骤3. 实现一个处理用户请求的控制器. 它将向订阅给定主题的所有用户广播收到的消息.

下面是将消息发送到目的地的示例方法 /主题/新闻.

@MessageM应用程序ing(“/新闻”)
@SendTo(“/主题/新闻”)
公共无效broadcastNews(@Payload String消息){
  返回消息;
}

而不是注释 @SendTo,你也可以用 SimpMessagingTemplate 你可以在你的控制器中自动装配哪些.

@MessageM应用程序ing(“/新闻”)
公共无效broadcastNews(@Payload String消息){
  这.simpMessagingTemplate.convertAndSend(“/主题/新闻”,消息)
}

在后面的步骤中,您可能希望添加一些额外的类来保护端点,例如 ResourceServerConfigurerAdapter or 网络SecurityConfigurerAdapter 从春天 Security框架中获取. 也, 实现消息模型通常是有益的,这样可以将传输的JSON映射到对象.

构建网络Socket客户端

实现客户机是一项更简单的任务.

步骤1. 自动弹簧STOMP客户端.

@ autowired
private 网络SocketStompClient;

步骤2. 打开连接.

会话H和ler = new custommstomp会话h和ler ();

StompSession StompSession = stompClient.连接(日志记录器ServerQueueUrl 
会话H和ler).get ();

一旦完成,就可以将消息发送到目的地. 消息将发送给订阅某个主题的所有用户.

stompSession.发送(“主题/问候”,“你好,新用户”);

还可以订阅消息.

会话.订阅(“主题/问候”,这个);

@Override
public void h和leFrame(StompHeaders headers, Object payload) {
    Message 味精 =(消息)有效载荷;
    日志记录器.info("Received: " + 味精.getText()+“from:”+ 
    味精.getFrom ());
}

有时需要只向专用用户发送消息(例如在实现聊天时)。. 然后, 客户端和服务器端必须使用专用于此私有对话的单独目的地. 可以通过将唯一标识符附加到通用目标名称来创建目标名称, e.g., /队列/ chat-user123. HTTP会话或STOMP会话标识符可用于此目的.

春天使发送私人消息变得容易得多. 我们只需要用 @SendToUser. 然后,这个目的地将由 UserDestinationMessageH和ler,它依赖于会话标识符. 在客户端,当客户端订阅以 /用户,此目标将转换为该用户的唯一目标. 在服务器端,用户目的地是基于用户的 主要.

示例服务器端代码 @SendToUser 注释:

@MessageM应用程序ing(“问候”)
@SendToUser(“/队列/问候”)
public String reply(@Payload String message)
   主用户){
 返回"Hello " +消息;
}

或者你可以用 SimpMessagingTemplate:

字符串username = ...
这.simpMessagingTemplate.convertAndSendToUser ();
   username, "/队列/问候", "Hello " + user的名字);

现在让我们看看如何实现一个能够接收私人消息的JavaScript (SockJS)客户机,这些消息可以由上面示例中的Java代码发送. 值得知道的是,尚是HTML5规范的一部分,大多数现代浏览器都支持(Internet Explorer从版本10开始支持)。.

函数connect() {
 var socket = new SockJS('/greetings');
 stompClient = Stomp./(套);
 stompClient.连接({},函数(框架){
   stompClient.订阅('/用户/队列/问候', function (greeting) {
     showGreeting (JSON.解析(问候.身体).的名字);
   });
 });
}

函数sendName() {
 stompClient.Send ("/应用程序/greetings", {}, $("#name")).瓦尔());
}

你可能已经注意到了, 接收私人信息, 客户端需要订阅一个通用目的地 /队列/问候 前缀与 /用户. 它不需要使用任何唯一标识符. 但是,客户端需要登录到应用程序之前,所以 主要 对象在服务器端初始化.

确保尚

许多web应用程序使用基于cookie的身份验证. 例如, 我们可以使用春天 Security来限制只有登录用户才能访问某些页面或控制器. 然后通过基于cookie的HTTP会话维护用户安全上下文,该会话随后与为该用户创建的网络Socket或SockJS会话相关联. 尚端点可以像任何其他请求一样被保护.g.,在春天。 网络SecurityConfigurerAdapter.

现在, web应用程序通常使用REST api作为后端,并使用OAuth/JWT令牌进行用户身份验证和授权. 网络Socket协议没有描述服务器应该如何在HTTP握手期间验证客户端. 在实践中,标准HTTP标头(例如.g.,授权)用于此目的. 不幸的是,并非所有STOMP客户机都支持它. 春天 Java的STOMP客户端允许设置握手头:

网络SocketHttpHeaders h和shakeHeaders = new 网络SocketHttpHeaders();
h和shakeHeaders.add (principalRequestHeader principalRequestValue);

但是SockJS JavaScript客户端不支持发送带有SockJS请求的授权头. 但是,它允许发送可用于传递令牌的查询参数. 这种方法需要在服务器端编写自定义代码,从查询参数中读取令牌并对其进行验证. 确保令牌没有与请求一起记录(或者日志得到很好的保护)也很重要,因为这可能会导致严重的安全违规.

SockJS回退选项

与网络Socket的集成可能并不总是顺利进行. 部分浏览器(e).g.(ie9)不支持尚. 而且, 限制性代理可能会导致无法执行HTTP升级,或者它们会切断开放时间过长的连接. 在这种情况下,SockJS就会挺身而出.

SockJS传输分为三大类, HTTP流媒体, 和HTTP长轮询. 通信从SockJS发送开始 GET /信息 获取服务器的基本信息. 基于响应,SockJS决定要使用的传输. 首选是尚. 如果它们不被支持,那么,如果可能的话,使用Streaming. 如果也不可能使用此选项,则选择Polling作为传输方法.

生产中的网络Socket?

虽然这种设置有效,但它并不是“最好的”.春天 Boot允许您使用带有STOMP协议的任何成熟的消息传递系统.g., ActiveMQ, RabbitMQ),并且外部代理可能支持更多的STOMP操作(例如.g.(致谢,收据)比我们使用的简单的代理要好. 践踏网络Socket 提供了关于尚和STOMP协议的有趣信息. 它列出了处理STOMP协议的消息传递系统,这些系统可能是在生产中使用的更好的解决方案. 特别是如果由于请求数量多,需要对消息代理进行集群. (春天的简单消息代理不适合集群.)然后,不启用简单的代理 网络SocketConfig, 需要启用Stomp代理中继,该中继将消息转发到外部消息代理或从外部消息代理发送消息. 总而言之,外部消息代理可以帮助您构建更具可伸缩性和健壮性的解决方案.

如果你准备好继续你的 Java开发人员 探索春靴之旅,试着阅读 使用春天 Boot实现OAuth2和JWT REST保护 下一个.

了解基本知识

  • 什么是STOMP?

    STOMP是简单(或流)面向文本的消息传递协议. 它使用一组命令,如连接、发送或订阅来管理会话. 用任何语言编写的STOMP客户机都可以与支持该协议的任何消息代理进行通信.

  • 尚的用途是什么?

    尚通常用于使web应用程序更具交互性. 它们在执行社交feed时很有帮助, 在线聊天, 新闻更新, 或者基于位置的应用程序.

  • 网络Socket是如何工作的?

    尚在单个TCP连接上提供双向通信通道. 客户端通过称为网络Socket握手的过程建立持久连接. 该连接允许实时交换消息.

  • 什么是春天 Boot ?为什么要使用它?

    春天 Boot是一个基于java的框架,它使实现独立应用程序或微服务变得更加容易. 它被广泛使用,因为它极大地简化了与各种产品和框架的集成. 它还包含一个嵌入式web服务器,因此不需要部署WAR文件.

聘请Toptal这方面的专家.
现在雇佣
托马斯Dąbrowski的头像
托马斯Dąbrowski

位于 波兰华沙

成员自 2015年10月30日

作者简介

Tomasz拥有超过10年的Java应用开发经验, 为惠普这样的公司工作过, 以及硅谷的创业公司.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

以前在

惠普(hewlett - packard)

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

世界级的文章,每周发一次.

订阅意味着同意我们的 隐私政策

Toptal开发者

加入总冠军® 社区.