I'm trying to get Spring Websockets (Spring 4) to work in the project that I've been working on. So far I have opening Websocket, adding subscribers and sending messages.
Since yesterday I've been trying to figure out why my endpoint doesn't process the message that is being send by stomp client included in my HTML. There's actually no error in the console as well (ok, I'm not 100% sure what "connected to server undefined" here means).
Here's output from Chrome console:
Opening Web Socket...
Web Socket Opened...
>>> CONNECT
accept-version:1.1,1.0
heart-beat:10000,10000
<<< CONNECTED
version:1.1
heart-beat:0,0
user-name:xxx#yyy.com
connected to server undefined
Connected: CONNECTED
user-name:xxx#yyy.com
heart-beat:0,0
version:1.1
>>> SUBSCRIBE
id:sub-0
destination:/my-project/notify-open-documents/1
Sending message : {"documentId":"1"}
>>> SEND
destination:/my-project/ws/lock-document
content-length:19
{"documentId":"1"}
and here's my code:
Configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/notify-open-documents");
registry.setApplicationDestinationPrefixes("/ws");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/lock-document");
stompEndpointRegistry.addEndpoint("/lock-document").withSockJS();
}
}
The controller:
#Controller
public class DocumentWebsocketController {
#MessageMapping("/lock-document")
#SendTo("/notify-open-documents/{id}")
public Response response(#DestinationVariable("id") Long id, Message message) {
return new Response(message.getDocumentId());
}
}
and STOMP related part of my HTML:
<script type="text/javascript">
$(document).ready(function() {
connect();
});
var stompClient = null;
function connect() {
var socket = new SockJS('<spring:url value="/lock-document"/>');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('<spring:url value="/notify-open-documents/${id}"/>', function (messageOutput) {
console.log('Receiving message: ' + JSON.parse(messageOutput.body));
});
sendName();
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
console.log("Disconnected");
}
function sendName() {
console.log("Sending message : " + JSON.stringify({'documentId' : "${id}" }));
stompClient.send('<spring:url value="/ws/lock-document"/>', {}, JSON.stringify({'documentId': "${id}"}));
}
</script>
I'm really running out of ideas what might be wrong, my browser supports WebSockets, from what I see in logs, WebSocket is being open properly, the message is being send, but what I can't figure out is why my Controller is unable to process the incoming messages.
From your HTML snippet I see you are using the spring:url tag to generate STOMP destinations. The spring:url tag adds the context path which doesn't make sense for a STOMP destination.
The application destination prefix you are configuring is /ws and the broker destination prefix /notify-open-documents, none of these will match your context path which is /my-project (from your console output). Remove the context path from your subscription destinations and when sending messages (not from the SockJS URL):
stompClient.send('/ws/lock-document', {}, JSON.stringify({'documentId': "${id}"}));
Related
I have a simple websocket server built following this example
import { createServer, Server } from 'http';
import * as express from 'express';
import * as socketIo from 'socket.io';
export class MobileObjectServer {
public static readonly PORT:number = 8081;
private app: express.Application;
private server: Server;
private io: socketIo.Server;
private port: string | number;
constructor() {
this.createApp();
this.config();
this.createServer();
this.sockets();
this.listen();
}
private createApp() {
this.app = express();
}
private createServer() {
this.server = createServer(this.app);
}
private config() {
this.port = process.env.PORT || MobileObjectServer.PORT;
}
private sockets() {
this.io = socketIo(this.server);
}
private listen() {
this.server.listen(this.port, () => {
console.log('Running server on port %s', this.port);
});
this.io.on('connect', (socket: any) => {
console.log('Connected client on port %s.', this.port);
socket.on('message', m => {
console.log('[server](message): %s', JSON.stringify(m));
this.io.emit('message', m);
});
socket.on('disconnect', () => {
console.log('Client disconnected');
});
});
}
public getApp(): express.Application {
return this.app;
}
}
This code runs smoothly. I can launch the websocket server on my machine and I can connect if I use the socket.io-client library.
Now I would like to connect to such server from a client using the webSocket and WebSocketSubject facilities provided by RxJs but I am encountering some basic problems just trying to connect.
If I do
import { webSocket } from 'rxjs/observable/dom/webSocket';
webSocket('http://localhost:8081')
nothing happens, no connection is established.
If I use 'ws://localhost:8081' as connection string then I get an error like this
WebSocketSubject.js:142 WebSocket connection to 'ws://localhost:8081/' failed: Connection closed before receiving a handshake response
I am sure I am making a very basic mistake, but I have currently no clue on where.
It is more or less impossible to connect to a socket.io server with pure web sockets. Socket.io adds a lot on top of the Websocket standard that is not compatible with pure web sockets. if you want to skip using socket.io on the client side I would advice using some other library server side, one example would be ws
Now I'm using stomp to connect from web to server.
I know STOMP has a heartbeat mechanism after v1.1, also I've already set it successfully. But I don't know how to detect/catch PING/PONG frame and handle it.
The code in web like this:
this.stomp = Stomp.client(this.wsUrl);
this.stomp.heartbeat.outgoing = 5000;
this.stomp.heartbeat.incoming = 5000;
this.stomp.connect({}, (frame: any) => {
// ...
}, (error: any) => {
// ...
});
The code in server like this. we use spring stomp.
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic")
.setTaskScheduler(new DefaultManagedTaskScheduler())
.setHeartbeatValue(new long[] {5000, 5000});
config.setApplicationDestinationPrefixes("/signal");
}
I can see PING/PONG frame printed in the console panel in chrome.
I have done a lot of research online but can't find the solution. I have created a SignalR hub on the WebAPI backend and enabled the CORS feature.
I also have a client (Ionic) app try to connect to the SignalR hub for real-time chat. The problem is that when the Hybrid app tries to connect to the SignalR hub, I am getting
The ConnectionId is in the incorrect format.
I am getting OnDisconnected Event on the SignalR Hub BUT not the OnConnected Event!
This is my client code (ionic):
localhost:64965/signalr is my SignalR hub
SignalR Proxy also generated on localhost:64965/signalr/hubs
var signalr_chatHub = $.connection.ChatHub;
signalr_chatHub.client.welcomeMessage = function (message) {
console.log('WelcomeMessage', message);
};
$.connection.hub.url = "http://localhost:64965/signalr";
$.connection.hub.logging = true;
$.connection.hub.start().done(function () {
console.log('signal connection connected');
}).fail(function (err) {
console.log('Could not Connect!', err);
});
Chrome Console Errors:
public class ChatHub : Hub<IChat>
{
public override Task OnConnected()
{
var connectionId = Context.ConnectionId;
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
var connectionId = Context.ConnectionId;
return base.OnDisconnected(stopCalled);
}
public override Task OnReconnected()
{
return base.OnReconnected();
}
public void WelcomeMessage()
{
Clients.All.NewMessage("Welcome");
}
}
Cordova by any chance?
The reason this happens is because ripple creates a "cross domain proxy", this grabs all generated uri's from signalR and incorrectly encodes parts.
fix is: set ripple, settings => cross domain proxy to "disabled"
below is an example of a proxied request, note the http%3a8080...:
http://localhost:4400/ripple/xhr_proxy?tinyhippos_apikey=ABC&tinyhippos_rurl=http%3A//localhost%3A8080/signalr/start%3Ftransport%3DwebSockets%26clientProtocol%3D1.5%26connectionToken%3DAQAAANCMnd8BFdERjHoAwE%252FCl%252BsBAAAA7b4HHv1ZFkq9Xe5qXnUfYwAAAAACAAAAAAAQZgAAAAEAACAAAACU%252BVDaQ6ENxgEPcm8Tjmr39SnBszhmBjUib5UoPoXKxgAAAAAOgAAAAAIAACAAAADoJqphu6%252BQ48B4d6J6QxbK%252FqKemI3%252FJiDfnCJRKtDMuDAAAACd3g9DsBiiG3CFNcDf0maC534kevbjNczDyFFCNSHeZB%252BNfX%252FkAXX74kYLEEUeqYNAAAAAvtEsnhNjbThhsJd0L7EN%252FNsTuK7M3ijALDGtP161hI2iobBj7%252FcItg%252FQmADPDOWlKIl7SgsRXU1dLXoOumpv%252Fw%253D%253D%26connectionData%3D%255B%257B%2522name%2522%253A%2522myhub%2522%257D%255D%26_%3D1494802918927
This bug took 5 hours of my life, damn them tiny hippos.
This is the code taken from the book "The Definitive Guide to HTML5 websocket"
div id="output"></div>
<script>
function setup() {
output = document.getElementById("output");
ws = new WebSocket("ws://localhost:7777");
ws.onopen = function(e) {
log("Connected");
sendMessage("Hello Websocket!");
}
ws.onclose = function(e){
log("Disconnected: " + e.reason);
}
ws.onerror = function(e){
log("Error ");
}
ws.onmessage = function(e) {
log("Message received: " + e.data);
ws.close();
}
}
function sendMessage(msg){
ws.send(msg);
log("Message Sent");
}
function log(s){
var p = document.createElement("p");
p.style.wordWrap = "break-word";
Could anybody please let me know for what reason this below event is required ??.
ws.send(msg);
I understand that the below will call the onMessage method on server side as shown below
public void onMessage(String data) {
}
But the actual purpose of onMessage on server side is to send data from backend to the javascript which will inturn call onmessage of client side javascript .
could anybody please help me understand this .
In the above code the ws.onopen, ws.onclose, ws.onmessage are all events associated with WebSocket object.
Whereas ws.send() is a method associated with WebSocket object. There is a huge difference between them.
The following event ensures that the Web Socket is connected to the server i.e you've opened your connection
ws.onopen = function(e) {
//Once you've opened your connection
//you can begin transmitting data to the server
//using the following method
ws.send("You\'re message");
}
So the main purpose of ws.send() method is to transmit data from the client to the server which is done by simply calling the WebSocket object's send() [in your case ws.send(msg)].
And also send data only takes place once a connection is established with the server by defining an onopen event handler.
I've been attempting to learn enough html, css, and Dart to create my first web page and all is going well, except that I do not understand how to create a simple page and a server side web-socket server that will just echo it back. The examples that I find tend to illustrate other Dart tools and either connect to echo server on the web or do other things that make their code not simple for a newbie.
I've tried to simplify Seth Ladd's example "dart-example-web-sockets-client" as the 'best' example. I can receive what is sent from the page, repackage it and think i'm sending it back but absolutely nothing happens on the web page. I start the page by clicking on the URL returned when the web-server is run from inside the Dart editor. Since the page is not, AFAIK, run in the debugger I'm hampered in diagnosing the error.
Here is simplified code from Seth's server:
void handleEchoWebSocket(WebSocket webSocket) {
log.info('New WebSocket connection');
// Listen for incoming data. We expect the data to be a JSON-encoded String.
webSocket
.map((string) => JSON.decode(string))
.listen((json) {
// The JSON object should contain a 'request' entry.
var request = json['request'];
switch (request) {
case 'search':
var input = json['input'];
log.info("Received request '$request' for '$input'");
var response = {
'response': request,
'input': input,
};
webSocket.add(JSON.encode(response)); // can't detect page receiving this.
log.info("Echoed request..$request $input"); // correct data
break;
default:
log.warning("Invalid request: '$request'");
}
}, onError: (error) {
log.warning('Bad WebSocket request');
});
}
This example took the user input using it as input to two search engines, packaged the results and returned them to the page for display creating new DOM elements on the fly.
I just need to be pointed to a simple example that will echo what is submitted.
Here is a simple websocket client/server echo example. Messages doesn't show in browser window, but they are printed in console window. You have to start server.dart and main.dart separately. Both processes print messages to their own console window.
Edit: I added an output div for displaying the message also in browser.
bin\ws_server.dart:
import "dart:convert";
import 'dart:io';
import 'package:route/server.dart' show Router;
void handleWebSocket(WebSocket webSocket) {
// Listen for incoming data. We expect the data to be a JSON-encoded String.
webSocket
.map((string)=> JSON.decode(string))
.listen((json) {
// The JSON object should contains a 'echo' entry.
var echo = json['echo'];
print("Message to be echoed: $echo");
var response='{"response": "$echo"}';
webSocket.add(response);
}, onError: (error) {
print('Bad WebSocket request');
});
}
void main() {
int port = 9223;
HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port).then((server) {
print("Search server is running on "
"'http://${server.address.address}:$port/'");
var router = new Router(server);
// The client will connect using a WebSocket. Upgrade requests to '/ws' and
// forward them to 'handleWebSocket'.
router.serve('/ws')
.transform(new WebSocketTransformer())
.listen(handleWebSocket);
});
}
web\index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Websocket echo</title>
</head>
<body>
<p>Websocket test</p>
<div id="output"></div>
<script type="application/dart" src="main.dart"></script>
</body>
</html>
web\main.dart:
library main;
import 'dart:async';
import 'dart:convert';
import 'dart:html';
class WebsocketService {
WebSocket webSocket;
WebsocketService() {
connect();
}
void connect() {
webSocket = new WebSocket('ws://127.0.0.1:9223/ws');
webSocket.onOpen.first.then((_) {
onConnected();
sendws("Hello websocket server");
webSocket.onClose.first.then((_) {
print("Connection disconnected to ${webSocket.url}");
onDisconnected();
});
});
webSocket.onError.first.then((_) {
print("Failed to connect to ${webSocket.url}. "
"Please run bin/server.dart and try again.");
onDisconnected();
});
}
void onConnected() {
webSocket.onMessage.listen((e) {
onMessage(e.data);
});
}
void onDisconnected() {
print("Disconnected, trying again in 3s");
new Timer(new Duration(seconds:3), (){
connect();
});
}
void onMessage(data) {
var json = JSON.decode(data);
var echoFromServer = json['response'];
print("Received message: $echoFromServer");
var output=querySelector('#output');
output.text="Received message: $echoFromServer";
new Timer(new Duration(seconds:3), (){ //Send a new message to server after 3s
String now = new DateTime.now().toString();
sendws("Time: $now");
});
}
void sendws(String msg){
var request = '{"echo": "$msg"}';
print("Send message to server: $request");
webSocket.send(request);
}
}
void main() {
WebsocketService ws=new WebsocketService();
}