I try to assign a value to a variable in onmessage function here:
socket = new WebSocket(ws_scheme +"127.0.0.1:8000/chat/");
var incomeData;
socket.onmessage = function(e) {
incomeData='hello'+ e.data;
console.log('inside function:'+incomeData)
}
socket.onopen = function() {
socket.send(" from websocket");
}
console.log(incomeData)
the console will show the first log as undefined and the second "inside function: hello from websocket". How can I get the variable assigned and keep the value outside the function too? Why second console.log appears first ?
incomeData is defined at global scope and is not assigned anything when first is printed with console.log(incomeData) thus you get the undefined error message. Then the socket onmessage is called and you got is assigned the e.data value that console.log then prints out.
You can handle the socket.onmessage and pass the e.data to a callback function like this:
socket = new WebSocket(ws_scheme +"127.0.0.1:8000/chat/");
socket.onmessage = function(e) {
socket_onmessage_callback(e.data);
}
socket.onopen = function() {
socket.send(" from websocket");
}
function socket_onmessage_callback(data) {
console.log('inside function socket_onmessage_callback:', data)
}
You can even use incomeData global variable if you want, but I think it does not required. Can update my answer if you need to use incomeData.
Once you get the main point of websocket protocol, you can easily update your global variables via a callback function which you pass to websocket.onmessage method of the protocol. The main point is that the backend server sends information to the client NOT in response to client's GETs or POSTs or whatever, but when it wants. There is no way to force a websocket endpoint to return you anything, it does it when it wants, even if you haven't asked for it. Once you have opened the ws:// connection, get ready for receiving data through this connection ANY unexpected time. What you need to do is to make sure that your app won't crash on an initial (or an undefined) value of the variable to be assigned, and just let the server update your variable when the server (not you!) wants. Here is a working code example with a self-invoking ws:// connection initialization (the (function(...) {...})(...); construction) and a callback:
<script>
var yourGlobalField = undefined;
console.log("Value before update: ", yourGlobalField);
(function initWebsocketConnection() {
websocket = new WebSocket("ws://your.websocket.URI");
websocket.onmessage = function (evt) {
theFunctionNeededToMakeSureWeHaveReceivedRealData(evt, yourSuccessCallbackFunction(evt));
};
websocket.onerror = function(err) {
networkError(err);
}
})();
function theFunctionNeededToMakeSureWeHaveReceivedRealData(evt) {
console.log("we have received some data!");
}
function yourSuccessCallbackFunction(data) {
yourGlobalField = JSON.parse(data); //just assuming you receive a JSON object
console.log("Value after update: ", yourGlobalField)
}
function networkError(err) {
console.log('Request Failed', err);
}
</script>
Related
here i am working on socket server and connecting it to the angular client every thing goes well the problem i am facing is when ever i call getRequest it should renders the get data from the message that i am getting
getRequest() {
this.sock.getData();
this.sock.socket.on('message', (data) => {
this.getresponseData = data;
});
this.sock.socket.on('disconnect',()=> {
console.log('A user disconnected');
});
}
here after this when i am calling the another method like postRequest() then the emitted message from the post request is triggering message event in get request()
below is my post request
postRequest() {
this.sock.postData(this.postData);
this.sock.socket.on('message', (data) => {
this.responseData = data;
});
this.sock.socket.on('disconnect',()=> {
console.log('A user disconnected');
});
}
in short when ever i call a postReuest the data emitted from mesasge is also going to message event in get request that should not happen.
On the Socket server side both the request i am using
socket.send(data);
Socket inherits every method of Emitter class which declares a once method for single shot listener.
The listener is deregistered after it is invoked the first time.
getRequest() {
this.sock.getData();
this.sock.socket.once('message', (data) => {
this.getresponseData = data;
});
this.sock.socket.on('disconnect',()=> {
console.log('A user disconnected');
});
}
You don't want to subscribe message on both the methods.
if you want to get response according to the different methods you should register different keys. just like "message". And send the message according to key from server.
this.sock.socket.on('getmessage', (data) => {
this.responseData = data;
});
this.sock.socket.on('postmessage', (data) => {
this.responseData = data;
});
Another approach would be to have some identifier in api response.
this.sock.socket.on('message', (data) => {
if(data.m.type == 'get')
this.responseData = data;
else
this.responsePostData = data;
});
Socket IO Ref
Another approach would be to use socket.ëmit
What you are doing wrong is you are explicitly calling
this.sock.socket.on('message', (data) => {
this.getresponseData = data;
});
above each time. This connects a single client multiple times which is not desired at all.
This function must be used singleton for all the lifecycle of your app except you are calling socket.removeAllListeners('disconnect'); explicitly.
Use only once and if you want to get stream of a data after you do a http request. Your choice is wrong. You may wanna use classical http request there.
I am using Laravel 5.1. I am trying to fire a UserAddedFriend event called on related function and get caught by Socket.io and Redis.
So I run node socket.js on the serverside. Everything seems okay to me, and when I open 2 browsers and login with 2 different users (X and Y), both gets connected.
When I call the function 'addFriend' function, event gets fired. On the server, node socket.js prints Received text as well; however nothing gets returned on any of the browsers in console.
What am I missing or doing wrong?
So on 'addFriend()' function, I fire this event:
\Event::fire(new UserAddedFriend($username));
On class UserAddedFriend:
class UserAddedFriend extends Event implements ShouldBroadcast
public $username;
public function __construct($username)
{
$this->username = $username;
}
public function broadcastOn()
{
return ['test-channel'];
}
Server:
var server = require('http').Server();
var io = require('socket.io')(server);
var Redis = require('ioredis');
var redis = new Redis();
redis.subscribe('test-channel', function(err, count) {
//
});
redis.on('message', function(channel, message) {
console.log("Received"); // gets printed
message = JSON.parse(message);
io.emit(channel + ':' + message.event, message.data);
});
server.listen(3000);
And lastly, Client:
<script>
var socket = io('http://192.168.10.10:3000');
socket.on('test-channel:UserAddedFriend', function(data) {
console.log('a'); // nothing logged
console.log(data); // nothing logged
});
</script>
To add, event handler is not used; but when I use event handle function and and use a die&dump $event, it dumps successfully.
public function handle(UserAddedFriend $event)
{
dd($event->username . ' added you.');
}
I found my problem.
socket.on('test-channel:UserAddedFriend', function(data)
should be
socket.on('test-channel:App\\Events\\UserAddedFriend', function(data)
I spent a while trying to diagnose this error.
First I had created a subclass of EventEmitter
File Client.js
var bindToProcess = function(fct) {
if (fct && process.domain) {
return process.domain.bind(fct)
}
return fct
};
function Client(){
EventEmitter.call(this);
}
util.inherits(Client, EventEmitter);
Client.prototype.success =
function(fct) {
this.on('success', bindToProcess(fct))
return this;
}
Client.prototype.login = function(username, password) {
body = {
username : username,
password : password
};
var self = this;
request.post(url, { json:true, body: body }, function (error, response, body) {
if (error ||response.statusCode != HTTPStatus.OK ) {
return self.emit('error', error);
}
return self.emit('success', body);
});
return this;
}
module.exports = Client
Then in another file in my Express App
File user.js
var Client = require('../utils/client');
var client = new Client();
// GET '/login'
exports.login = function(req, res){
client.login(username, password).success( function(user) {
res.redirect('/');
}).error( function(error) {
res.redirect('login');
});
}
The thing is though on the second request, the server crashes with the error:
Error: Can't set headers after they are sent.
In the interim I've solved the problem by created the Client inside the middleware rather than having it a global variable. I'm just curious why this is happening ?
Thanks,
(hopefully there is enough information)
What happens here is the call of the event handling function from the first request during second request because the variable client is shared between the requests.
At first, the client is created in the global scope. Then two handlers are attached to its events and then the request is actually performed and corresponding handler is called.
On the second request, two more handlers are attached to the same object and then on either success or fail two handlers (from previous call and from the current) are notified.
So you need to move the client creation to the action method or change how the code responds on the events - I can suggest promises-like approach: pass two callbacks as parameters to one method; or just the standard callback approach: pass the error result as first argument of the callback.
Note: I'm using Autobahn.js for the client-side WAMP implementation, and when.js for promises.
I'm trying to create re-usable code so that only one websocket 'session', or connection exists, and whenever a dev wants to subscribe to a topic using autobahn, they can just use the current connection object to do so if it already exists; else a new one is created.
My issue is that, if the connection already exists, I have to use a setTimeout() to wait for a second to make sure it's actually connected, and then duplicate all the subscription code - I don't like this at all.
Here's my current code:
(function() {
var connection = null;
subscribeTo('subject', __userId, __token, function(onconnect) {
console.log('Yay, connected');
});
function subscribeTo(subject, userId, token, onConnect, onDisconnect) {
if (connection === null)
{
connection = new ab.Session('ws://localhost:8080', function(onopen) {
connection.subscribe(JSON.stringify({subject: subject, userId: userId, token: token}), function(subscription, data) {
data = $.parseJSON(data);
// Do something with the data ...
});
if (typeof onConnect === 'function') {
onConnect();
}
}, function(onclose) {
if (typeof onDisconnect === 'function') {
onDisconnect();
}
}, { 'skipSubprotocolCheck': true });
}
}
})();
Great. Now the issue is, what if I have another subscribeTo() straight after the previous one? Connection won't be null any more, but it also won't be connected. So the following is what I have to do:
// subscribeTo() multiple times at the top ...
subscribeTo('subject', __userId, __token, function(onconnect) {
console.log('Yay, connected');
});
subscribeTo('anothersubject', __userId, __token, function(onconnect) {
console.log('Yay, connected');
});
// The first one works, the second one requires a setTimeout() for the connection
// if connection is NOT null...
} else {
setTimeout(function() {
connection.subscribe(topic... etc...) // Really!?
}, 1000);
}
Remove the setTimeout() and you'll get an error saying that "Autbahn is not connected".
Is there a better way to have a single, re-usable connection, without code-duplication, or am I doomed to create a new connection for each subscription because of the promises (perhaps I can use promises to my advantage here, although I haven't used them before this)?
This is all way too complex, unneeded and wrong. You want to do your subscribes in response to a session being created:
var session = null;
function start() {
// turn on WAMP debug output
//ab.debug(true, false, false);
// use jQuery deferreds instead of bundle whenjs
//ab.Deferred = $.Deferred;
// Connect to WAMP server ..
//
ab.launch(
// WAMP app configuration
{
// WAMP URL
wsuri: "ws://localhost:9000/ws",
// authentication info
appkey: null, // authenticate as anonymous
appsecret: null,
appextra: null,
// additional session configuration
sessionConfig: {maxRetries: 10, sessionIdent: "My App"}
},
// session open handler
function (newSession) {
session = newSession;
main();
},
// session close handler
function (code, reason, detail) {
session = null;
}
);
}
function main() {
session.subscribe("http://myapp.com/mytopic1", function(topic, event) {});
session.subscribe("http://myapp.com/mytopic2", function(topic, event) {});
session.subscribe("http://myapp.com/mytopic3", function(topic, event) {});
}
start();
The ab.launch helper will manage automatic reconnects for you (and also do WAMP-CRA authentication if required). init() is then automatically called again when a reconnect happens. Using raw Session object is not recommended (unless you know what you are doing).
Also: topics must be URIs from the http or https scheme. Using serialized objects (JSON) is not allowed.
I'm trying to use WebSockets for the first time, and I'm making a todo list app. I'm using Express and Socket.io and Redis.
In the following code, socket.emit('items', {items: redisItems}) is failing, saying that emit can't be done on undefined. I know that client.hgetall is an asynchronous call and that I have to somehow get it to execute the after. How do I fix it? Do I use something like Futures or can this be done with a small fix on what I have right now?
io.sockets.on('connection', function (socket) {
socket.on('getItems', function (socket){
var redisItems = new Array;
callback = function(err,obj){
for ( att in obj) {
item = new Object();
item.key = att;
item.done = obj[att];
redisItems.push(item);
console.log(redisItems.length);
console.log(item.key);
}
socket.emit('items', {items: redisItems})
};
client.hgetall("items", callback);
});
});
Sidenote: What I currently have goes like this: 1) browser requests page 2) Once the browser has the page, it connects through WebSocket and asks for the todo items (so 2 requests).
In the future, I would prefer something like 1) browser makes request for page 2) server gives page and sends items once it's done.
You're overwriting your socket object in the getItems callback. While the initial io.sockets connection event returns the socket, subsequent socket.on events don't return another socket object, they return data for the event. Try something like:
io.sockets.on('connection', function (socket) {
socket.on('getItems', function (data){
var redisItems = new Array;
callback = function(err,obj){
for ( att in obj) {
item = new Object();
item.key = att;
item.done = obj[att];
redisItems.push(item);
console.log(redisItems.length);
console.log(item.key);
}
socket.emit('items', {items: redisItems})
};
client.hgetall("items", callback);
});
});