Socket.io api inconsistency or how does it work? - socket.io

When I do:
socket.join('room');
socket.to('room').emit('online');
the 'online'-event is never received on the client side.
When I do:
socket.join('room');
io.to('room').emit('online');
the 'online'-event is received on the client side.
When I do:
socket.join('room', () => {
socket.to('room').emit('online');
});
the 'online'-event is never received on the client side.
The docs state the following:
http://socket.io/docs/server-api/#socket#to(room:string):socket
If I read this the call to:
socket.to('room').emit('online');
should be emitted to the room.
If I use the call:
socket.broadcast.to('room').emit('online');
the event should emit to the room except the socket.
Now it looks like that:
socket.to('room').emit('online');
and
io.to('room').emit('online');
are different. But what I read from the docs I read that they are the same.
Can someone explain the differences and why behave different?

Related

How do establish a connection to a channel in feathersjs?

I'm new to node and to feathersjs, and for my first app, I'm trying to have different parts of it communicate using channels. I understand the operations and how they're used, but I don't understand how to establish a connection to a channel in the first place.
For example, here's some code from the official documentation:
app.on('login', (payload, { connection }) => {
if(connection && connection.user.isAdmin) {
// Join the admins channel
app.channel('admins').join(connection);
// Calling a second time will do nothing
app.channel('admins').join(connection);
}
});
Where does "connection" come from? There is no built-in function (unless I'm missing something obvious) in feathersjs to do this.
Thanks!
Channel is used in feathers to achieve real time.
In the server you need to configure socketio. Then it also requires the client to be connected to the server via socketio.
Where does "connection" come from?
connection is a js object that represents the connection the user has established by logging in.
Try doing a console.log(connection) to see what it contains.
connection is in this case passed by the Feathers framework in the function call to the function that you have quoted.
Once you have obtained this connection object then you can use it for adding the user to a channel, and many other things.

masstransit deferred respond in sagas

I am investigating using sagas in mass transit to orchestrate activities across several services. The lifetime of the saga is short - less than 2 seconds if all goes well.
For my use case, i would like to use the request/respond approach, whereby the client requests a command, the saga handles that command, goes through some state changes as messages are received and eventually responds to the first command that initiated the saga, at which point the client receives the response and can display the result of the saga.
From what i can see, by this point, the context is no longer aware of the initial request. How can I reply to a message that was received in this way? Is there something i can persist to the saga data when handling the first event, and use that to reply later on?
Thanks Alexey. I have realised that I can store the ResponseAddress and RequestId from the original message on the saga, and then construct a Send() later on.
Getting the response details from the original request
MassTransit.EntityFrameworkIntegration.Saga.EntityFramework
SagaConsumeContext<TSagaData, TMessage> payload;
if (ctx.TryGetPayload(out payload))
{
ResponseAddress = payload.ResponseAddress;
RequestId = payload.RequestId ;
}
Sending the response
var responseEndpoint = await ctx.GetSendEndpoint(responseAddress);
await responseEndpoint.Send(message, c => c.RequestId = requestId);
UPDATE: The documentation has been updated to include a more complete example.
Currently, the saga state machine can only do immediate response like this:
// client
var response = await client.Request(requestMessage);
// saga
During(SomeState,
When(RequestReceived)
.Then(...)
.Respond(c => MakeResponseMessage(c))
.TransitionTo(Whatever)
)
So you can respond when handling a request.
If you want to respond to something you received before, you will have to craft the request/response conversation yourself. I mean that you will have to have decoupled response, so you need to send a message and have a full-blown consumer for the reply message. This will be completely asynchronous business.

Differences between io.to(), io.in(), and socket.to() for emitting to all clients in a room

The Socket.io documentation seems to specify a few ways to emit an event to all connected clients in a room. They are as follows:
io.to(), as found in the first example here: https://socket.io/docs/server-api/#socket-join-room-callback
io.in(), as found in the emit cheatsheet, found here: https://socket.io/docs/emit-cheatsheet/
socket.to(), as found here: https://socket.io/docs/server-api/#socket-to-room
Other than the examples linked above, both io.to() and io.in() are not listed anywhere else in the documentation. What do these methods do exactly, and where can I find more information on them?
socket.to() can be used inside the io.on('connection', callback) event, like so:
io.on('connection', function(socket){
// to one room
socket.to('others').emit('an event', { some: 'data' });
// to multiple rooms
socket.to('room1').to('room2').emit('hello');
});
However, this does not make sense, as the socket object passed into this callback represents a connected client. How can the incoming socket object be used to broadcast to all other connected sockets, as shown in the above example?
Definitive explanations of the above are appreciated.
However, this does not make sense, as the socket object passed into this callback represents a connected client.
If you trace into those call in a debugger, you can see what is going on.
First off, the socket.to() creates a property on the socket named _rooms that is an array of room names. You can see the whole code in context here in the Github repository, but here's the relevant portion for .to():
Socket.prototype.to =
Socket.prototype.in = function(name){
if (!~this._rooms.indexOf(name)) this._rooms.push(name);
return this;
};
Each successive call to .to() just an addition room to the array.
Then, socket.emit() checks to see if the _rooms property exists and if it does, it calls this.adapter.broadcast(...) which grabs the adapter and tells it to broadcast this message to all sockets on that adapter except the current one. The whole code for socket.emit() is here on Github. The particular broadcast part of the code is this:
if (this._rooms.length || this.flags.broadcast) {
this.adapter.broadcast(packet, {
except: [this.id],
rooms: this._rooms,
flags: this.flags
});
} else {
// dispatch packet
this.packet(packet, this.flags);
}
How can the incoming socket object be used to broadcast to all other connected sockets, as shown in the above example?
Each socket contains a reference to the adapter and the adapter has a list of all sockets on that adapter. So, it's possible to get form the socket to the adapter, to all the other sockets.
I would agree that this is a bit of an odd overloading of functionality, but that's how they do it. I'm guessing they wanted to give people access to broadcast functionality when all you had a reference to was an individual socket.
FYI, the only way to really answer these types of questions yourself that are not documented is by looking at the code and that is certainly one of the huge advantages of using open source libraries. I find that the quickest way to get to the right source is to step into the method of interest in the debugger. Fire up the debugger, set a breakpoint in your code, then step into the function of choice and it will show you the relevant source code immediately. You can then further step through that function if you want to see what path it is taking.
For anyone coming across this question like me, here is the link to the docs for explanation:
https://socket.io/docs/v3/rooms/index.html

Sending events from server to client(s) in Meteor

Is there a way to send events from the server to all or some clients without using collections.
I want to send events with some custom data to clients. While meteor is very good in doing this with collections, in this case the added complexity and storage its not needed.
On the server there is no need for Mongo storage or local collections.
The client only needs to be alerted that it received an event from the server and act accordingly to the data.
I know this is fairly easy with sockjs but its very difficult to access sockjs from the server.
Meteor.Error does something similar to this.
The package is now deprecated and do not work for versions >0.9
You can use the following package which is originally aim to broadcast messages from clients-server-clients
http://arunoda.github.io/meteor-streams/
No collection, no mongodb behind, usage is as follow (not tested):
stream = new Meteor.Stream('streamName'); // defined on client and server side
if(Meteor.isClient) {
stream.on("channelName", function(message) {
console.log("message:"+message);
});
}
if(Meteor.isServer) {
setInterval(function() {
stream.emit("channelName", 'This is my message!');
}, 1000);
}
You should use Collections.
The "added complexity and storage" isn't a factor if all you do is create a collection, add a single property to it and update that.
Collections are just a shape for data communication between server and client, and they happen to build on mongo, which is really nice if you want to use them like a database. But at their most basic, they're just a way of saying "I want to store some information known as X", which hooks into the publish/subscribe architecture that you should want to take advantage of.
In the future, other databases will be exposed in addition to Mongo. I could see there being a smart package at some stage that strips Collections down to their most basic functionality like you're proposing. Maybe you could write it!
I feel for #Rui and the fact of using a Collection just to send a message feel cumbersome.
At the same time, once you have several of such message to send around is convenient to have a Collection named something like settings or similar where you keep these.
Best package I have found is Streamy. It allows you to send to everybody, or just one specific user
https://github.com/YuukanOO/streamy
meteor add yuukan:streamy
Send message to everybody:
Streamy.broadcast('ddpEvent', { data: 'something happened for all' });
Listen for message on client:
// Attach an handler for a specific message
Streamy.on('ddpEvent', function(d, s) {
console.log(d.data);
});
Send message to one user (by id)
var socket = Streamy.socketsForUsers(["nJyQvECmkBSXDZEN2"])._sockets[0]
Streamy.emit('ddpEvent', { data: 'something happened for you' }, socket);

Pusher App Client Events

I'm writing a multiplayer chess game, and using Pusher for the websocket server part.
Anyways, if I have a list of users, and I select any one of them and challenge them, how do I send challenge to just that one user? I know I would use the client event like:
channel.trigger("client-challenge_member1", {some : "data"});
But this event would have to have already been created I think. So do I create this event dynamically after each member subscribes? as possibly in:
channel.bind("pusher:subscribed_completed", function(member) // not sure of correct syntax but...
{
channel.bind("client-challenge_" + member.memberID, function(data)
{
alert(data.Name + " is challenging you.");
});
});
I would think there'd be a overloaded method for trigger, like:
channel.trigger(eventName, data, memberID)
But I cannot see anything like this. Any ideas? Thanks.
I ran into this problem on my application. At this time Pusher does not provide methods for sending events to a specific user. I think the approach that you mentioned would work for your situation. For my application I had each user subscribe to a channel with their user id as the channel id, then I could send messages to a single user through that channel.
client = new Pusher(PUSHER_API_KEY);
channel = client.subscribe(user_id);
channel.bind('my_event',function(data){
//Do stuff
});
I talked this approach over with the pusher team and they assured me there was no real overhead in having the extra channels. The new Pusher() command is the code that creates a new socket connection so you don't have to worry about extra sockets per channel or anything like that. Hope this helps.
I'm from Pusher. As Braden says, you can easily make a channel per user. This is more efficient than having the user id in the event name which means you spam everyone with useless messages.
This is an area we want to improve on further, so thanks for the feedback.
If you're able to consider another service, Beaconpush has the ability to send messages to a specific user.
From their site:
POST /1.0.0/[API key]/users/[user]

Resources