#OnOpen
public void onOpen(Session session) {
session.getBasicRemote.sendText("Echo me");
session.addMessageHandler(new MessageHandler.Whole<String>() {
public void onMessage(String arg0) {
System.out.println(arg0);
}
});
}
I send a message, and it's echoed back to me. Well what if I wanted to send another message?
How would I do that?
The reason I ask is because I'm logging in to a server, and they send me back a message stating if I'm successfully logged in or not.
After I've been successfully logged in, I want to send another message querying the server about different information I require.
WebSocketContainer.connectToServer(...) method returns Session object. When client is connected, you can use this to send messages (see Session.getBasicRemote().sendText(...) or Session.getBasicRemote().sendBinary(...)).
Related
I am new to CometD.I have a written a basic CometD Server in java and simple CometD Client.I am getting Successful response from postman for /meta/handshake,/meta/connect,/meta/subscribe channels. But when i start using my cometD java client(which i reused from the https://protect-us.mimecast.com/s/vLH6CNk58of1Ow1GsmVz4u?domain=github.com), handshake is failing with the below message.
Failing {supportedConnectionTypes=[long-polling],
channel=/meta/handshake, id=22, version=1.0}
I am using cometdVersion - '4.0.0',jettyVersion - '9.4.0.v20161208', springbootVersion - '1.5.14.RELEASE' in my code.
I have done a dynamic servlet registration to AnnotationCometDServlet and added /notifications as mapping.
I have created a channel as below in bayeuxServer configuration class.
bayeuxServerImpl.createChannelIfAbsent("/updates",
(ServerChannel.Initializer) channel -> channel.addAuthorizer(GrantAuthorizer.GRANT_ALL));
In client code, i have used /notifications as the defaulturl and channel as /updates
#Service("cometListener")
#Slf4j
public class BayeuxListener implements BayeuxServer.SessionListener {
#Inject
private BayeuxServer bayeuxServer;
#Session
private ServerSession serverSession;
#Configure({"/updates**,/notifications**"})
protected void configureChannel(ConfigurableServerChannel channel) {
channel.addAuthorizer(GrantAuthorizer.GRANT_ALL);
channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
channel.setPersistent(true);
}
#Listener("/meta/*")
public void monitorMeta(ServerSession session, ServerMessage message) {
log.info("monitoring meta"+message.toString()+"channel "+message.getChannel()+"session id "+session.getId());
}
#Listener("/meta/subscribe")
public void monitorSubscribe(ServerSession session, ServerMessage message) {
log.info("Monitored Subscribe from " + session + " for " + message.get(Message.SUBSCRIPTION_FIELD));
}
#Listener("/meta/unsubscribe")
public void monitorUnsubscribe(ServerSession session, ServerMessage message) {
log.info("Monitored Unsubscribe from " + session + " for " + message.get(Message.SUBSCRIPTION_FIELD));
}
#Listener("/updates")
public void handlesrgUpdates(ServerSession client, ServerMessage message) {
ServerSession cilentSession = bayeuxServer.getSession(client.getId());
client.deliver(cilentSession,"/updates", "Received message back from client");
}
}
You have a strange combination of CometD version, Jetty version and Spring Boot version. I recommend that you stick with the default versioning declared in the CometD POM, i.e. CometD 4.0.2, Jetty 9.4.14 and Spring Boot 2.0.6.
The handshake failure you mention is not complete or it is not a failed handshake reply. This is because handshake replies have the successful field, and what you mention {supportedConnectionTypes=[long-polling], channel=/meta/handshake, id=22, version=1.0} looks like the handshake request. As such it's difficult to say what the problem is, because the failure reason is typically reported in the handshake reply.
If you have dynamically registered the CometD Servlet under the /notifications Servlet mapping, then the client should have a URL that ends with /notifications.
Note that the Servlet mapping /notifications and the CometD channel /notifications are two different things and are not related - they just happen to have the same name.
Your code is mostly fine, but contains a few errors.
#Configure({"/updates**,/notifications**"})
protected void configureChannel(ConfigurableServerChannel channel) {
channel.addAuthorizer(GrantAuthorizer.GRANT_ALL);
channel.addAuthorizer(GrantAuthorizer.GRANT_PUBLISH);
channel.setPersistent(true);
}
The code above must instead be:
#Configure({"/updates/**,/notifications/**"})
protected void configureChannel(ConfigurableServerChannel channel) {
channel.addAuthorizer(GrantAuthorizer.GRANT_ALL);
channel.setPersistent(true);
}
Note that the channel globbing must be after a /.
There is no need to GRANT_PUBLISH after a GRANT_ALL, which include GRANT_PUBLISH.
The configuration method should be public, not protected.
#Listener("/updates")
public void handlesrgUpdates(ServerSession client, ServerMessage message) {
ServerSession cilentSession = bayeuxServer.getSession(client.getId());
client.deliver(cilentSession,"/updates", "Received message back from client");
}
There is no need to retrieve clientSession from bayeuxServer because it is already been passed as parameter client to the method.
The method can be better implemented as:
#Listener("/updates")
public void handlesrgUpdates(ServerSession client, ServerMessage message) {
client.deliver(serverSession, "/updates", "Received message back from client");
}
Note how the "sender" of the message is the serverSession reference that has been injected as a field of the class.
The code above is still possibly wrong.
Because /updates is a broadcast channel, if the client is subscribed to the /updates channel, when the client publishes a message to the /updates channel it will receive it back from the server (because the client is subscribed to the /updates channel) and the code above will also send another message to the client on the /updates channel via deliver(), so the client will receive two different messages on the /updates channel.
This may be what you want, but most of the times it's not.
Please have a read at the difference between broadcast channels and service channels.
Update the question with the details of your handshake failure, and use consistent versioning for CometD, Jetty and Spring Boot.
This is my current setup for an Undertow websocket server:
Undertow server = Undertow.builder()
.addHttpListener(8080, "localhost")
.setHandler(path()
.addPrefixPath("/", websocket((exchange, channel) -> {
channel.getReceiveSetter().set(new AbstractReceiveListener() {
#Override
protected void onFullTextMessage(WebSocketChannel channel, BufferedTextMessage message) {
final String messageData = message.getData();
for (WebSocketChannel session : channel.getPeerConnections()) {
WebSockets.sendText(messageData, session, null);
}
}
});
channel.resumeReceives();
}))).build();
This is copied from one of their demo files. I believe onFullTextMessage here broadcasts any messages it receives to all clients.
I want to be able to trigger this behavior on demand. So instead of having to receive a message and using an event handler to send out more messages, I want to be able to arbitrarily do server.send() and send a message to all connected clients.
I've looked around and haven't found anything that seems capable of achieving this. I don't relish the idea of tracking all WebSocketChannels and sending to each client manually. Surely, there's a method somewhere that I've overlooked?
I'd appreciate some pointers if it's not something that's just not possible!
You can broadcast the message to all the clients on the channel by getting all the connections to this canal and sending the message :
channel.getPeerConnections().forEach(connection -> {
WebSockets.sendText(messageData, connection, null);
});
I have written a client-server programme using netty IO in which I am able to send a message to the server and server responds back but unfortunately, any of the listener methods in my Client Handler class is not getting invoked ie.. response is not received at the client level back. This is the piece of code used in my client to send a request message to the server.
ChannelFuture cf =this.ctx.channel().writeAndFlush(Unpooled.copiedBuffer(getEchoISOMessage(), CharsetUtil.ISO_8859_1));
this.ctx.writeAndFlush(Unpooled.EMPTY_BUFFER);
cf.awaitUninterruptibly();
if (!cf.isSuccess()) {
System.out.println("Send failed: " + cf.cause());
}
Below is the code used to receive any response message from the server.
#Override
public void ChannelRead(ChannelHandlerContext ctx, ByteBuf in)
{
System.out.println("Received a response from Server..");
String responseMessage="";
responseMessage=in.toString(CharsetUtil.ISO_8859_1);
System.out.println("Received data as echo response"+responseMessage+"from "+ctx.channel().remoteAddress());
}
But this method is not getting invoked at all.
What does your handler extend from? It should be an inbound handler if it is to receive a response from the server. Are you sure your code compiles with the #Override annotation? The callback method in ChannelInboundHanlder is public void void channelRead(ChannelHandlerContext ctx, Object msg). Make sure your annotate with #Override and make sure the code compiles. Then your client will/should receive the response from the server. If it doesn't help, consider sharing the full code.
I am trying to send PongMessage to a server from web-socket client (heart-beat message you may say).
In server, I have written a method like this inside a class Annotated with #ServerEndpoint :
#OnMessage
public void onPong(PongMessage pongMessage, Session session) {
LOGGER.log(Level.INFO, " -- Got Hit Yaay!! -- ");
try {
session.getBasicRemote().sendText(PONG_RECEIVED);
} catch (IOException e) {
e.printStackTrace();
}
}
PongMessage Accepts ByteBuffer according to an oracle documentation. I tried generating a bytebuffer in server and sending exact same generated bytebuffer value from socket client. Server still throws an exception saying it Cannot decode a string.
I am not able to hit onPong() method. Is there any format for pongmessage that client can send to a websocket server so that onPong() method gets invoked? What am I doing wrong here? Any Ideas ?
I have found an old post that Ping/Pong messages should be handled by the browser. If that is still true (i'm not sure), then you should be moving to a "custom" solution: don't use PongMessage. Receive some JSON defined by you.
I use below code to send messages.
// Assume we've created an XMPPConnection name "connection".
ChatManager chatmanager = connection.getChatManager();
Chat newChat = chatmanager.createChat("jsmith#jivesoftware.com", new MessageListener() {
public void processMessage(Chat chat, Message message) {
System.out.println("Received message: " + message);
}
});
try {
newChat.sendMessage("Howdy!");
}
catch (XMPPException e) {
System.out.println("Error Delivering block");
}
And below for receiving messages sent to my JabberID, asynchronously.
// Create a packet filter to listen for new messages from a particular
// user. We use an AndFilter to combine two other filters.
PacketFilter filter = new AndFilter(new PacketTypeFilter(Message.class),
new FromContainsFilter("mary#jivesoftware.com"));
// Assume we've created an XMPPConnection name "connection".
// First, register a packet collector using the filter we created.
PacketCollector myCollector = connection.createPacketCollector(filter);
// Normally, you'd do something with the collector, like wait for new packets.
// Next, create a packet listener. We use an anonymous inner class for brevity.
PacketListener myListener = new PacketListener() {
public void processPacket(Packet packet) {
// Do something with the incoming packet here.
}
};
// Register the listener.
connection.addPacketListener(myListener, filter);
Sending message is ok.
But receiving message from another JabberID don't achived until I send a message to that JabberID.
And after that I receive messages sent by it properly.
Note that I often need to receive messages from jabberIDs that are not in my list and often My application is not the side that begins a chat.
Upper codes are smack samples but my code is completely same except I don't create PacketListener implementation inline.
My problem solved when I stopped using Jabber Client with the same user logined during I test my program.
In other words code is correct but Jabber client catches sent messages and remain no things for my program to catch.
It's been quite a while since I worked with smack, but I managed to start chats based on incoming messages.
If I remember well, I had some sort of "ChatRegistry", a simple Map<String, Chat> where the key was equal to the chat partners id. Then I listened to incoming messages, extracted the jabber id and looked up the active chat session with this partner. If there wasn't an active session, I created a new Chat and added the new key/vale pair to the registry.
Just a bit confused. You say
Upper codes are smack samples but my code is completely same except I don't create PacketListener implementation inline.
How do you receive messages without having a PacketListener implementation? I would think that you would always receive messages from chats you started because of the code below
Chat newChat = chatmanager.createChat("jsmith#jivesoftware.com", new MessageListener() {
public void processMessage(Chat chat, Message message) {
System.out.println("Received message: " + message);
}
});
But in order to asynchronously wait for incoming messages, I would think you will need a PacketListener.
I might have totally misunderstood the problem you are facing though
(This should have been a comment, but I can't figure how to add one)
It's been a while, did you manage to solve this? why are you creating FromContainsFilter? In that way, your listeners processes only packets from the given user, not all packets.