问题
I want to send a message through a web socket to a specific user. So far I can open a web socket and read message from client like that:
@ServerEndpoint(value = "/wsep")
public class WebSocketEndpoint {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketEndpoint.class);
private Session session;
@OnOpen
public void onOpen(Session session) {
this.session = session;
try {
session.getBasicRemote().sendText("You are connected. Your ID is " + session.getId());
} catch (Exception e) {
LOGGER.error("Error on open web socket", e);
}
}
@OnMessage
public void onClientMessage(String message, Session session) {
LOGGER.info("Message from {} is: {}", session.getId(), message);
}
@OnClose
public void onClose(Session session) {
this.session = null;
LOGGER.info("{} disconnected", session.getId());
}
}
I have an independent service which creates message in destination to a user. My Message
class is a simple POJO:
public class Message {
private String fromUserName;
private String toUserName;
private String content;
...
}
When a new message is created in my MessageService
, I want to inform the receiver if he is connected. I think I have to add a method WebSocketEndpoint.onServerMessage
:
public void onServerMessage(Session session, Message message) {
session.getBasicRemote().sendText(message.getContent());
}
But I don't know how to do something like that which works.
回答1:
There will be one instance of the ServerEndpoint for all your users. So, it should store all client sessions. As Vu.N suggested, one way you can do it is using a map:
Map<String, Session> sessions = new ConcurrentHashMap<>();
public void onOpen(Session session) {
String username = [...]
sessions.put(username, session);
}
Then, it will be easy to send the message to the user:
public void onServerMessage(Session session, Message message) {
sessions.get(message.getToUserName())
.getBasicRemote() // see also getAsyncRemote()
.sendText(message.getContent());
}
Now the hardest part is to get the username
?
In my past works, I've done it in 3 ways:
The client connects to a URL that has some "key". This "key" would be used to find the right username. The WebSocket server endpoint would be like this:
@ServerEndpoint(value="/wsep/{key}") // the URL will have an extra "key" public class WebSocketEndpoint { [...] @OnOpen public void onOpen(Session session, @PathParam("key") String key) { String username = getUserNameWithKey(key); sessions.add(username, session); }
The client sends some information in the first message. You just ignore the @OnOpen part:
@OnOpen public void onOpen(Session session) { LOGGER.info("Session open. ID: {}", session.getId()); } @OnMessage public void onMessage(Session session, String message) { String username = getUserNameFromMessage(message); sessions.add(username, session); }
Some user info can be obtained from a cookie, JWT, or something. You'll need a
ServerEndpointConfig.Configurator
to get that information from the request. For example:public class CookieServerConfigurator extends ServerEndpointConfig.Configurator { @Override public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) { Map<String,List<String>> headers = request.getHeaders(); sec.getUserProperties().put("cookie", headers.get("cookie")); } }
Then, the server endpoint will point to that configurator:
@ServerEndpoint(value = "/example2/", configurator = CookieServerConfigurator.class) public class WebSocketEndpoint {
and you can get the information like this:
@OnOpen public void onOpen(Session session, EndpointConfig endpointConfig) { String username = getUsername((List<String>)endpointConfig.getUserProperties().get("cookie"));
You can see some working examples here: https://github.com/matruskan/websocket-example
For more complex systems, you can also have a "tagging system", instead of using a Map. Then, each session can receive messages sent to any of its tags, and a message sent to a tag can be directed to many sessions.
来源:https://stackoverflow.com/questions/42804923/how-to-send-a-message-through-web-socket-to-a-connected-user