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

Tomasz has 10+ years of experience with Java 应用程序s, 和 worked for companies like 惠普(hewlett - packard), as well as Silicon Valley startups.

以前在

惠普(hewlett - packard)
分享

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

The server-side will be coded purely in Java. 但, 在客户端的情况下, 我将展示用Java和JavaScript (SockJS)编写的代码片段, 通常, 尚 clients are embedded in front-end 应用程序lications. 代码示例将演示如何使用发布-订阅模型向多个用户广播消息,以及如何仅向单个用户发送消息. In a further part of the article, 我将简要讨论如何保护网络Socket,以及如何确保我们基于网络Socket的解决方案在环境不支持网络Socket协议的情况下仍能正常运行.

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

网络Socket 和 STOMP Protocols

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

The 网络Socket protocol is a rather low-level protocol. 它定义了如何将字节流转换为帧. A frame may contain a text or a binary message. 因为消息本身不提供有关如何路由或处理它的任何附加信息, 如果不编写额外的代码,很难实现更复杂的应用程序. 幸运的是, 网络Socket规范允许使用在更高层次上运行的子协议, 应用程序级别. One of them, supported by the 春天 Framework, is STOMP.

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

The Server-side: 春天 Boot 和 尚

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

用春天 Boot实现网络Socket服务器端并不是一项非常复杂的任务,只包括几个步骤, which we will walk through one by one.

步骤1. First, we need to add the 网络Socket library dependency.


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

If you plan to use JSON format for transmitted messages, you may want to include also the GSON or Jackson dependency. 很可能,您还需要一个安全框架,例如春天 security.

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

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

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

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

该方法 配置ureMessageBroker 做两件事:

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

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

How messages are h和led on server-side (source: 春天文档)


回到上面的代码片段,您可能已经注意到对该方法的调用 withSockJS ()—it enables SockJS fallback options. 为了言简意赅, 即使网络浏览器不支持网络Socket协议,它也可以让我们的网络Socket工作. I will discuss 这 主题 in greater detail a bit further.

There is one more thing that needs clarifying—why we call setAllowedOrigins () 方法。. 这通常是必需的,因为网络Socket和SockJS的默认行为是只接受同源请求. So, if your client 和 the server-side use different domains, 需要调用此方法以允许它们之间的通信.

步骤3. Implement a controller that will h和le user requests. 它将向订阅给定主题的所有用户广播收到的消息.

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

@MessageM应用程序ing(“/新闻”)
@SendTo(“/主题/新闻”)
public void broadcastNews(@Payload String message) {
  返回消息;
}

而不是注释 @SendTo,你也可以用 SimpMessagingTemplate which you can autowire inside your controller.

@MessageM应用程序ing(“/新闻”)
public void broadcastNews(@Payload String message) {
  这.simpMessagingTemplate.convertAndSend("/主题/新闻", message)
}

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

Building the 网络Socket Client

Implementing a client is an even simpler task.

步骤1. Autowire 春天 STOMP client.

@ autowired
private 网络SocketStompClient stompClient;

步骤2. 打开连接.

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

StompSession stompSession = stompClient.connect(日志记录器ServerQueueUrl, 
会话H和ler).get ();

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

stompSession.send("主题/greetings", "Hello new user");

It is also possible to subscribe for messages.

会话.subscribe("主题/greetings", 这);

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

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

春天 makes sending private messages a lot easier. We only need to annotate a Controller’s method with @SendToUser. 然后, 这 destination will be h和led by UserDestinationMessageH和ler, which relies on a 会话 identifier. 在客户端,当客户端订阅以 /用户,此目标将转换为该用户的唯一目标. 在服务器端,用户目的地是基于用户的 主要.

Sample server-side code with @SendToUser 注释:

@MessageM应用程序ing("/greetings")
@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.connect({}, function (frame) {
   stompClient.subscribe('/用户/队列/问候', function (greeting) {
     showGreeting (JSON.解析(问候.身体).的名字);
   });
 });
}

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

你可能已经注意到了, 接收私人信息, the client needs to subscribe to a general destination /队列/问候 前缀与 /用户. It does not have to bother with any unique identifiers. 但是,客户端需要登录到应用程序之前,所以 主要 object on the server-side is initialized.

确保尚

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

现在, web应用程序通常使用REST api作为后端,并使用OAuth/JWT令牌进行用户身份验证和授权. 网络Socket协议没有描述服务器应该如何在HTTP握手期间验证客户端. In practice, st和ard HTTP headers (e.g., Authorization) are used for 这 purpose. Unfortunately, it is not supported by all STOMP clients. 春天 Java的STOMP客户端允许设置握手头:

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

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

SockJS回退选项

Integration with 网络Socket may not always go smoothly. 部分浏览器(e).g., IE 9) do not support 尚. 而且, 限制性代理可能会导致无法执行HTTP升级,或者它们会切断开放时间过长的连接. In such cases, SockJS comes to the rescue.

SockJS传输分为三大类, HTTP流媒体, 和HTTP长轮询. The communication starts with SockJS sending GET /信息 to obtain basic information from the server. 基于响应,SockJS决定要使用的传输. The first choice are 尚. 如果它们不被支持,那么,如果可能的话,使用Streaming. 如果也不可能使用此选项,则选择Polling作为传输方法.

生产中的网络Socket?

While 这 setup works, it isn’t the “best.春天 Boot允许您使用带有STOMP协议的任何成熟的消息传递系统.g., ActiveMQ, RabbitMQ),并且外部代理可能支持更多的STOMP操作(例如.g., acknowledges, receipts) than the simple broker we used. 践踏网络Socket 提供了关于尚和STOMP协议的有趣信息. 它列出了处理STOMP协议的消息传递系统,这些系统可能是在生产中使用的更好的解决方案. 特别是如果由于请求数量多,需要对消息代理进行集群. (春天的简单消息代理不适合集群.) 然后, instead of enabling the simple broker in 网络SocketConfig, 需要启用Stomp代理中继,该中继将消息转发到外部消息代理或从外部消息代理发送消息. 总而言之,外部消息代理可以帮助您构建更具可伸缩性和健壮性的解决方案.

If you’re ready to continue your Java开发人员 journey exploring 春天 Boot, try reading Using 春天 Boot for OAuth2 和 JWT REST Protection 下一个.

了解基本知识

  • 什么是STOMP?

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

  • What are 尚 used for?

    尚通常用于使web应用程序更具交互性. They can be helpful when implementing social feeds, 在线聊天, 新闻更新, 或者基于位置的应用程序.

  • 网络Socket是如何工作的?

    尚在单个TCP连接上提供双向通信通道. 客户端通过称为网络Socket握手的过程建立持久连接. The connection allows exchanging messages in real time.

  • What is 春天 Boot 和 why it is used?

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

Hire a Toptal expert on 这 主题.
现在雇佣
托马斯Dąbrowski

托马斯Dąbrowski

验证专家 在工程

波兰华沙

Member since October 30, 2015

作者简介

Tomasz has 10+ years of experience with Java 应用程序s, 和 worked for companies like 惠普(hewlett - packard), as well as Silicon Valley startups.

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

以前在

惠普(hewlett - packard)

World-class articles, delivered weekly.

Subscription implies consent to our 隐私政策

World-class articles, delivered weekly.

Subscription implies consent to our 隐私政策

Toptal开发者

加入总冠军® 社区.