如何仅从服务器向特定用户发送websocket消息?
我的webapp具有弹簧安全设置并使用websocket.我遇到了一个棘手的问题,试图只从服务器向特定用户发送消息.
我阅读本手册的理解来自我们可以做的服务器
simpMessagingTemplate.convertAndSend("/user/{username}/reply", reply);
Run Code Online (Sandbox Code Playgroud)
在客户端:
stompClient.subscribe('/user/reply', handler);
Run Code Online (Sandbox Code Playgroud)
但我永远无法调用订阅回调.我尝试了许多不同的路径,但没有运气.
如果我将它发送到/ topic/reply它可以工作,但所有其他连接的用户也会收到它.
为了说明问题,我在github上创建了这个小项目:https://github.com/gerrytan/wsproblem
重现步骤:
1)克隆并构建项目(确保您使用的是jdk 1.7和maven 3.1)
$ git clone https://github.com/gerrytan/wsproblem.git
$ cd wsproblem
$ mvn jetty:run
Run Code Online (Sandbox Code Playgroud)
2)导航到http://localhost:8080,使用bob/test或jim/test登录
3)单击"请求用户特定消息".预期:仅对此用户的"仅收到消息给我"旁边显示消息"hello {username}",实际:未收到任何消息
我目前正在开发一个项目,要求客户端请求大量工作并将其发送到服务器.然后,服务器将作业分开并响应一组URL,以便客户端进行GET调用并回送数据.我是该项目的新手,我目前正在使用Spring websockets来提高效率.而不是客户端不断ping服务器以查看它是否已准备好返回结果,websocket现在只需直接联系客户端万岁!
让websockets从头到尾管理整个过程是不是一个坏主意?我正在使用带有Spring websockets的STOMP,是否仍然存在抛弃REST的主要问题?
我应该在非80端口上使用WebSocket吗?它是否会毁掉使用现有Web/HTTP基础架构的整个目的?我认为它不再符合非80端口上的Web Socket 名称.
如果我在其他端口上使用WebSocket,为什么不直接使用TCP呢?或者WebSocket协议本身有什么特别的好处吗?
由于当前的WebSocket握手是HTTP UPGRADE请求的形式,这是否意味着我必须在端口上启用HTTP协议,以便可以完成WebSocket握手?
根据这里:
HTTP Upgrade标头请求服务器将应用层协议从HTTP切换到WebSocket协议.
客户端握手在IE10和服务器之间建立了HTTP-on-TCP连接.在服务器返回其101响应之后,应用层协议从HTTP切换到使用先前建立的TCP连接的WebSockets.
此时HTTP完全脱离了图片.使用轻量级WebSocket线协议,任何端点现在都可以随时发送或接收消息.
所以,我的理解是,在第一个客户端完成与服务器的握手后,服务器的80端口将被WebSocket协议垄断.并且HTTP不再在80端口上运行.
那么第二个客户端如何与服务器交换握手.所有WebSocket握手都是HTTP格式.
谢谢你到目前为止的所有答案.他们真的很有帮助.
现在我明白同一台服务器的80端口是由多个连接共享的TCP.这种共享完全没问题,因为TCP连接是由5元素元组标识Jan-Philip Gehrcke出来的.
我想补充几点想法.
这两个WebSocket和HTTP仅仅是应用层协议.通常他们都依赖TCP协议作为他们的运输.
WebSocket设计有意为握手和后续通信选择服务器的端口80 .我认为设计人员希望从传输级别的角度看 WebSocket通信看起来像普通的HTTP通信(即服务器端口号仍然是80).但根据jfriend00答案,这个技巧并不总是欺骗网络基础设施.
来自RFC 6455 - WebSocket协议
基本上,考虑到Web的限制,它应尽可能地将原始TCP暴露给脚本.它的设计也使其服务器可以与HTTP服务器共享一个端口,使其握手成为有效的HTTP升级请求.可以在概念上使用其他协议来建立客户端 - 服务器消息传递,但WebSockets的目的是提供一个相对简单的协议,该协议可以与HTTP和部署的HTTP基础结构(例如代理)共存,并且尽可能安全地接近TCP.在给定安全性考虑的情况下使用此类基础结构,通过有针对性的添加来简化使用并简化简单事务(例如添加消息语义).
所以我认为我在下面的陈述中错了:
握手请求模仿 HTTP请求,但后面的通信没有.握手请求到达端口80上的服务器. 由于它是80端口,服务器将使用HTTP协议对其进行处理.这就是为什么WebSocket握手请求必须是HTTP格式的原因. 如果是这样,我认为必须修改/扩展HTTP协议以识别那些特定于WebSocket的东西.否则它将无法实现它应该屈服于 WebSocket协议.
我认为应该这样理解:
WebSocket通信以从客户端到服务器的有效 …
是否可以向特定会话发送消息?
我在客户端和Spring servlet之间有一个未经验证的websocket.当异步作业结束时,我需要向特定连接发送未经请求的消息.
@Controller
public class WebsocketTest {
@Autowired
public SimpMessageSendingOperations messagingTemplate;
ExecutorService executor = Executors.newSingleThreadExecutor();
@MessageMapping("/start")
public void start(SimpMessageHeaderAccessor accessor) throws Exception {
String applicantId=accessor.getSessionId();
executor.submit(() -> {
//... slow job
jobEnd(applicantId);
});
}
public void jobEnd(String sessionId){
messagingTemplate.convertAndSend("/queue/jobend"); //how to send only to that session?
}
}
Run Code Online (Sandbox Code Playgroud)
正如您在此代码中看到的,客户端可以启动异步作业,当它完成时,它需要结束消息.显然,我只需要向申请人发送信息而不是向所有人广播.有一个@SendToSession注释或messagingTemplate.convertAndSendToSession方法会很棒.
UPDATE
我试过这个:
messagingTemplate.convertAndSend("/queue/jobend", true, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, sessionId));
Run Code Online (Sandbox Code Playgroud)
但这会向所有会话广播,而不仅仅是指定的会话.
更新2
使用convertAndSendToUser()方法进行测试.这个测试是和正式的Spring教程的破解:https://spring.io/guides/gs/messaging-stomp-websocket/
这是服务器代码:
@Controller
public class WebsocketTest {
@PostConstruct
public void init(){
ScheduledExecutorService statusTimerExecutor=Executors.newSingleThreadScheduledExecutor();
statusTimerExecutor.scheduleAtFixedRate(new Runnable() …Run Code Online (Sandbox Code Playgroud) 我正在使用Spring Websocket和STOMP,Simple Message Broker.在我的@Controller使用方法级别@SubscribeMapping,它应该将客户端订阅到主题,以便客户端之后将接收该主题的消息.假设客户订阅主题"聊天":
stompClient.subscribe('/app/chat', ...);
当客户订阅"/ app/chat "而不是"/ topic/chat"时,此订阅将转到使用以下方法映射的方法@SubscribeMapping:
@SubscribeMapping("/chat")
public List getChatInit() {
return Chat.getUsers();
}
Run Code Online (Sandbox Code Playgroud)
这是Spring ref.说:
默认情况下,@ SubsscribeMapping方法的返回值作为消息直接发送回连接的客户端,并且不通过代理.这对于实现请求 - 回复消息交互很有用; 例如,在初始化应用程序UI时获取应用程序数据.
好的,这就是我想要的,但只是部分 !! 订阅后发送一些init-data,好吧.但订阅呢?在我看来,这里发生的事情只是一个请求 - 回复,就像一个服务.订阅只是消费.如果是这种情况,请澄清我.
如果在这里客户端没有订阅任何地方,我想知道为什么我们称之为"订阅"; 因为客户端只收到一条消息而不是后续消息.
编辑:
为了确保订阅已经实现,我尝试的内容如下:
服务器端:
组态:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hello").withSockJS();
} …Run Code Online (Sandbox Code Playgroud) spring stomp messagebroker spring-messaging spring-websocket
我正在使用Spring WebSockets开发WebSocket消息传递后端,它使用SockJS + STOMP协议.不使用普通WebSockets的原因是因为我需要利用SockJS在Spring WebSockets中提供的安全集成以及SockJS的其他整洁功能,例如房间,订阅等.我想知道这是否是一个很好的选择这样移动(iOS和Android)和Web客户端应用程序可以轻松连接到后端服务器并执行消息传递.如果是,那么我可以用于iOS和Android的库.
在SockJS GitHub页面上,他们还列出了可用的客户端库,但没有iOS和Android.所以,我想知道SockJS是否因此而值得使用.
我发现对于iOS客户端Primus-Objc(GitHub页面)库声称它们可以连接到那native WebSockets, Socket.IO, SockJS or perhaps engine.io.是一个真正的声明吗?事件如果是真的那么,那个图书馆的质量怎么样?
事件如果可以在后面使用SockJS,那么是否也可以显示iOS和Android的示例代码,以便我可以在移动设备上执行概念验证?
如果SockJS对我来说不是一个好选择,那么用Socket.io + Node.js构建我的消息传递应用程序会更好(使用JavaScript).在我看来,Socket.io有所有需要的iOS客户端库(Socket.IO的官方库)和Android(Socket.IO的官方库).
另一种选择可以是使用Netty-Socket.io库并手动构建每个端点,而无需Spring Framework(我正在考虑使用)的任何帮助,但是我需要解决的是小问题,即安全问题.实际上有人已经尝试过这样做(同一位作者在Netty-Socket.IO Github官方网页上提问),但看起来他还没有运气解决它.
我的错误显示在浏览器的控制台中:
"WebSocket connection to 'ws://localhost:32768/DspClusterWebServices/myHandler' failed: Unexpected response code: 200"
我正在使用Spring Websockets 4.1.5和Tomcat 8.0.18.我的WebSocketConfigurer实现类如下所示:
@Configuration
@Controller
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer
{
class MyHandler implements WebSocketHandler
{
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception
{
System.out.println("afterConntectionEstablished called");
}
...implements rest of functions with a System.out.println and false for supportsPartialMessages()
}
}
@Override registerWebSocketHandlers(WebSocketHandlerRegistry registry)
{
registry.addHandler(myHandler(), "myHandler").withSockJS();
}
@Bean
public WebSocketHandler myHandler()
{
return new MyHandler();
}
}
Run Code Online (Sandbox Code Playgroud)
我的testWebsocketClient.js尝试连接此代码,但错误代码为200:
websocket = new WebSocket("ws://localhost:8080/myApp/myHandler");
Run Code Online (Sandbox Code Playgroud)
我无法弄清楚下一步该尝试什么.我以为这会导致afterConnectionEstablished(WebSocketSession会话)方法被触发?代码200不好吗?
我正在实现一个版本的股票应用程序,其中服务器能够根据用户权限拒绝特定主题的主题订阅.spring-websocket有没有办法做到这一点?
例如:
在库存示例项目中我们有3个仪器的价格主题:Apple,Microsoft,Google并且有两个用户:User1,User2
User1应该可以访问Apple,Microsoft User2应该只能访问Google
如果User1订阅Google,他应该被拒绝回复,之后不应该向他广播消息.
spring spring-security publish-subscribe websocket spring-websocket
嗨,我需要将我的Spring Boot应用程序部署到Wildfly 8.1中,我遇到以下异常:
引起:java.lang.RuntimeException:java.lang.ClassCastException:org.apache.tomcat.websocket.server.WsServerContainer无法转发到io.undertow.servs.core.DeploymentManagerImpl的io.undertow.websockets.jsr.ServerWebSocketContainer.部署(DeploymentManagerImpl.java:219)org.wildfly.extension.undertow.deployment.UndertowDeploymentService.startContext(UndertowDeploymentService.java:87)org.wildfly.extension.undertow.deployment.UndertowDeploymentService.start(UndertowDeploymentService.java:72) at org.jboss.msc.service.ServiceControllerImpl $ StartTask.startService(ServiceControllerImpl.java:1948)[jboss-msc-1.2.2.Final.jar:1.2.2.Final] at org.jboss.msc.service.ServiceControllerImpl $ StartTask.run(ServiceControllerImpl.java:1881)[jboss-msc-1.2.2.Final.jar:1.2.2.Final] ... 3更多引起:java.lang.ClassCastException:org.apache.tomcat. websocket.server.WsServerContainer无法转发到io.undertow.websockets.jsr.Bootst的io.undertow.websockets.jsr.ServerWebSocketContainer rap $ WebSocketListener.contextInitialized(Bootstrap.java:69)at io.undertow.servlet.core.ApplicationListeners.contextInitialized(ApplicationListeners.java:173)at io.undertow.servlet.core.DeploymentManagerImpl.deploy(DeploymentManagerImpl.java:190) ......还有7个
似乎我的websockets和消息传递的设置可能是罪魁祸首?我正在看这个https://github.com/joshlong/boot-examples/issues/2并没有提出的解决方案似乎适合我.这是我的pom的依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.1.6.RELEASE</version>
</parent>
<dependencies>
<!--Testing-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${apache.httpcomponents.version}</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
</dependency>
<dependency> …Run Code Online (Sandbox Code Playgroud) spring-websocket ×10
spring ×6
websocket ×6
java ×4
http ×2
spring-mvc ×2
stomp ×2
android ×1
ios ×1
javascript ×1
rest ×1
spring-boot ×1
tomcat ×1
wildfly ×1