In other spring platform , I used websocket to send binary message,like this:
ByteBuffer bf = ByteBuffer.wrap(bytes);
old.getBasicRemote().sendBinary(bf);
But with spring-boot, I make my class extends TextWebSocketHandler. But in the method handleTextMessage(WebSocketSession session, TextMessage message) , only have param WebSocketSession and it has no method to send binary.
I try to use BinaryMessage,like this:
session.sendMessage(new BinaryMessage(bytes));
But the client side get the result as Blob(js type), I don't what to do...
you can use BinaryWebSocketHandler to handle binary message communication.
full example
#Configuration
#EnableAutoConfiguration
#EnableWebSocket
public class AppWebSocket {
public static void main(String[] args) {
SpringApplication.run(AppWebSocket.class, args);
}
#Component
public static class MyWebSocketConfigurer implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyTextHandler(), "/text").withSockJS();
registry.addHandler(new MyBinaryHandler(), "/binary").withSockJS();
}
}
#Component
public static class MyTextHandler extends TextWebSocketHandler {
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
session.sendMessage(new TextMessage("hello world"));
}
}
#Component
public static class MyBinaryHandler extends BinaryWebSocketHandler {
public void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
try {
session.sendMessage(new BinaryMessage("hello world".getBytes()));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Related
I am using websockets without Stomp. What is the correct way to decide to whom USer WebSocketSession belongs to?
In my WsConfig i use:
#Configuration
#EnableWebSocket
public class WebSocketServerConfiguration implements WebSocketConfigurer {
protected final CustomWebSocketHandler webSocketHandler;
#Autowired
public WebSocketServerConfiguration(CustomWebSocketHandler webSocketHandler) {
this.webSocketHandler = webSocketHandler;
}
#SuppressWarnings("NullableProblems")
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler, "/ws")
.addInterceptors();
}
}
my WsHandler is currently empty:
#Service
public class SplitBillWebSocketHandler extends TextWebSocketHandler {
#Override
public void handleTransportError(WebSocketSession session, Throwable throwable) throws Exception {
}
#Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//determine User for session
User user = determineUser(session);
sessionStorage.add(user.getName(),session);
}
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage jsonTextMessage) throws Exception {
}
}
What is the way to determine the user? Or what is the best practice to do this?
Do I need to pass some parameter to websocket URL from client ( which isn't standard as far as I am aware ), or how to identify the session?
Thanks for help!
I have implemented a console application using Spring and WebSockets. The application works fine if one or more participants are connected to the base method which is anotated like this.
#MessageMapping("/chat")
#SendTo("/topic/messages")
I will copy the configuration and the implementation which i have made o be more clear.
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
}
#Controller
public class ChatController {
#MessageMapping("/chat")
#SendTo("/topic/messages")
public OutputMessage send(#Payload Message message) {
return new OutputMessage(message.getFrom(), message.getText());
}
#MessageMapping("/chat/{room}")
#SendTo("/topic/messages/{room}")
public OutputMessage enableChatRooms(#DestinationVariable String room, #Payload Message message) {
return new OutputMessage(message.getFrom(), message.getText());
}
}
#Service
public class SessionHandlerService extends StompSessionHandlerAdapter {
private String nickName;
public SessionHandlerService() {
this.nickName = "user";
}
private void sendJsonMessage(StompSession session) {
ClientMessage msg = new ClientMessage(nickName, " new user has logged in.");
session.send("/app/chat", msg);
}
#Override
public Type getPayloadType(StompHeaders headers) {
return ServerMessage.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
System.err.println(payload.toString());
}
#Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
session.subscribe("/topic/messages", new SessionHandlerService());
sendJsonMessage(session);
}
}
The problem which i face is that when i subscribe to /topic/messages and session.send("/app/chat", msg);everything works fine. But if i choose something like session.send("/app/chat/room1", msg); and /topic/messages/room1 the participans can not see each other messages like they are in different chat rooms.
I have created a spring boot application in which I want to use Web Sockets. When I am using it without parameters its working fine. Below is the code without the parameters
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ABC(), "/getABC").setAllowedOrigins("*");
registry.addHandler(new XYZ(), "/getXYZ").setAllowedOrigins("*");
}
}
But now I need to pass a parameter to it using #PathParam. I am not able to use it in this configuration like
registry.addHandler(new XYZ(), "/getXYZ{someId}").setAllowedOrigins("*");
My Handler code:
public class XYZ extends TextWebSocketHandler {
static List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
String someId;
public XYZ() {
}
public XYZ(#PathParam(value = "someId") String someId) {
this.someId= someId;
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// the messages will be broadcasted to all users.
sessions.add(session);
}
}
I think there is some problem with the syntax, try using
public XYZ(#PathParam("someId") String someId)
I have 4 cxf interceptors, each of which extend AbstractPhaseInterceptor. I am ordering them in the following way :
Class A:
public class AInterceptor extends AbstractPhaseInterceptor<Message> {
public AuthenticationInterceptor() {
super(Phase.PRE_INVOKE);
super.addAfter(OInInterceptor.class.getName());
super.addAfter(HInterceptor.class.getName());
}
public void handleMessage(Message message) throws Fault {
System.out.println("Into AInterceptor");
System.out.println(super.getAfter()); //This is what prints the last line.
}
}
Class H:
public class HInterceptor extends AbstractPhaseInterceptor<Message> {
public HInterceptor() {
super(Phase.PRE_INVOKE);
super.addBefore(PInterceptor.class.getName());
}
public void handleMessage(Message message) throws Fault {
System.out.println("Into HInterceptor ...");
}
}
Class O:
public class OInterceptor extends AbstractPhaseInterceptor<Message> {
private ServerPolicyEvaluator serverPolicyEvaluator;
public OrderZeroPolicyInInterceptor() {
super(Phase.PRE_INVOKE);
super.addBefore(PInterceptor.class.getName());
super.addAfter(HInterceptor.class.getName());
}
#Override
public void handleMessage(Message message) throws Fault {
System.out.println("Into OInterceptor ...");
}
}
Class P:
public class PInterceptor extends AbstractPhaseInterceptor<Message> {
public PInterceptor() {
super(Phase.PRE_INVOKE);
super.addAfter(HInterceptor.class.getName());
}
public void handleMessage(Message message) throws Fault {
System.out.println("Into PInterceptor ...");
}
}
However when I run the application I get the following output:
Into HInterceptor ...
Into AInterceptor
[com.xyz.interceptors.H, com.xyz.interceptors.O]
OInterceptor should have been executed before AInterceptor. Why are the interceptors being executed in the incorrect order ?
This is probably obvious, but I am new to this paradigm. I create a Jetty Server and register my websocket class as follows:
Server server = new Server(8080);
WebSocketHandler wsHandler = new WebSocketHandler()
{
#Override
public void configure(WebSocketServletFactory factory)
{
factory.register(MyEchoSocket.class);
}
};
server.setHandler(wsHandler);
The websocket receives messages fine. I would like to also be able to send messages out from the server without having first received a message from the client. How do I access the MyEchoSocket instance that's created when the connection opens? Or, more generally, how do I send messages on the socket outside of the onText method in MyEchoSocket?
Two common techniques, presented here in a super simplified chatroom concept.
Option #1: Have WebSocket report back its state to a central location
#WebSocket
public class ChatSocket {
public Session session;
#OnWebSocketConnect
public void onConnect(Session session) {
this.session = session;
ChatRoom.getInstance().join(this);
}
#OnWebSocketMessage
public void onText(String message) {
ChatRoom.getInstance().writeAllMembers("Hello all");
}
#OnWebSocketClose
public void onClose(int statusCode, String reason) {
ChatRoom.getInstance().part(this);
}
}
public class ChatRoom {
private static final ChatRoom INSTANCE = new ChatRoom();
public static ChatRoom getInstance()
{
return INSTANCE;
}
private List<ChatSocket> members = new ArrayList<>();
public void join(ChatSocket socket)
{
members.add(socket);
}
public void part(ChatSocket socket)
{
members.remove(socket);
}
public void writeAllMembers(String message)
{
for(ChatSocket member: members)
{
member.session.getRemote().sendStringByFuture(message);
}
}
public void writeSpecificMember(String memberName, String message)
{
ChatSocket member = findMemberByName(memberName);
member.session.getRemote().sendStringByFuture(message);
}
public ChatSocket findMemberByName(String memberName)
{
// left as exercise to reader
}
}
Then simply use the central location to talk to the websockets of your choice.
ChatRoom.getInstance().writeSpecificMember("alex", "Hello");
// or
ChatRoom.getInstance().writeAllMembers("Hello all");
Option #2: Have WebSocket be created manually with WebSocketCreator
#WebSocket
public class ChatSocket {
public ChatRoom chatroom;
public ChatSocket(ChatRoom chatroom)
{
this.chatroom = chatroom;
}
#OnWebSocketConnect
public void onConnect(Session session) {
chatroom.join(this);
}
#OnWebSocketMessage
public void onText(String message) {
chatroom.writeAllMembers(message);
}
#OnWebSocketClose
public void onClose(int statusCode, String reason) {
chatroom.part(this);
}
}
public class ChatCreator implements WebSocketCreator
{
private ChatRoom chatroom;
public ChatCreator(ChatRoom chatroom)
{
this.chatroom = chatroom;
}
public Object createWebSocket(UpgradeRequest request,
UpgradeResponse response)
{
// We want to create the Chat Socket and associate
// it with our chatroom implementation
return new ChatSocket(chatroom);
}
}
public class ChatHandler extends WebSocketHandler
{
private ChatRoom chatroom = new ChatRoom();
#Override
public void configure(WebSocketServletFactory factory)
{
factory.setCreator(new ChatCreator(chatroom));
}
}
At this point you can use the same techniques as above to talk to the websockets of your choice.