Sending data to specific client and not to the whole channel - javascript

I am currently playing with Faye.js. Upon subscription to a channel I want that specific client to receive an object that would be irrelevant to anybody already in the channel.
How can this be achieved?
More detail:
The object is an array of the last 20 chat comments in the room. Anybody already in the room would have received this object already or been a part of the chats and so it is not required for these to receive it.
Thank you in advance.

The only way I've come across is to generate a client-side GUID. When the client connects, it announces itself to the others through a 'public' channel, you can then use the GUID to send messages directly to this client.
For example, take the piece of code from the answer of this previous question to generate something that looks like a UUID.
You can then do something on the client-side like this:
var guid = guidGenerator();
client.subscribe('/privChannel_' + guid, onPrivateMessage);
client.subscribe('/pubChannel', onPublicMessage);
client.publish('/announce', { 'myId': guid });
function onPrivateMessage() {
// do something
};
function onPublicMessage() {
// do something
};
Your server should always subscribe to the '/announce' channel, and when any message is posted in that channel it should store that id so that it can identify that particular client. Then, the server can use this id to publish to a channel only this client should be subscribed to.
Note however that this is not a good idea for sensitive data. Other clients could also subscribe to '/announce' and farm the guids for malicious purposes.

Related

Send SSE to specific clients

I have a web app in which a teacher creates a class and is able to upload materials and add students to the class. Students can also join a particular teacher's class and download any uploaded material.
When a teacher uploads a material to a particular class, a Server Sent Event is sent to every connected client and from the client, checks are performed to find out if this event concerns the client. Here's a code sample:
let eventSource = new EventSource(url);
eventSource.onmessage = function(event){
if(joinedClasses.indexOf(event.data.class) !== -1){
//notify the user.
}
//else just discard the message.
}
I want to know if this is the proper way of doing this or should I just use (Long) Polling or some other method. Thanks.
Depending on your back-end structure you could try with 3 different approaches:
1) Sending the user ID as a GET parameter, this way you would have specific stream for each user.
2) The other way is to have different events on the server side and then the client would listen only to the events that are relevant for them.
3) If you don't have a database, sessions could also be used for some cases.
I've implemented using query strings as keys. You could see a minimal example here

What should I use as the document key to maintain idempotency?

What should I use as the document key to maintain idempotency?
I'm building a text messaging application that uses CouchDB (with PouchDB on the client) to store messages locally. Twilio (SMS provider) generates an ID for each message, and I use that as the CouchDB document ID. This way fetching messages from Twilio's API is idempotent -- if I come across the same message twice, it will only store one copy in my database.
// twilio API /messages
[
{smsid: 123, body: 'foo'},
{smsid: 456, body: 'bar'}
]
// transformed into couchdb docs
[
{id: 123, doc: {_id: 123, body: 'foo'}},
{id: 456, doc: {_id: 456, body: 'bar'}}
]
This is easy to do when fetching messages from twilio. But when the user sends an outbound message from the client application, there is no twilio ID yet because it hasn't been sent to twilio yet.
A traditional approach would involve POSTing the message to some endpoint on my server, and have the server send it to twilio, then add the record to the database once it has the smsid from twilio's response. The problem with this is (a) there's a noticeable delay from when the user presses "send" and when the message shows up in the UI, and (b) we can't take advantage of couchdb's auth system.
Instead, I have it setup so the client generates a random ID, and inserts it into the database (via pouchdb w/sync). The server then watches for new outbound records added and dispatches them to twilio.
This approach works fine, but if I GET /messages again, it's no longer idempotent -- it would create an additional record for the outbound message because I don't have a couchdb document with that message's smsid as its key (it didn't have an smsid when it was added to couchdb).
Is there a way around this or a better approach?
An idea to make this work is that you must rely on other data from each message, and ignore Twilio's smsid.
Perhaps hashing together the user id, the message body and an abrangent version of the timestamp (for example, int(UNIX-TIMESTAMP-IN-SECONDS/100) will tolerate a delay of 100 seconds between the time your server gets the message and Twilio acknowledges it).
Thanks for your replies. This was a tough one. #rnewson from #couchdb in freenode was kind enough to spend some time thinking about this one and proposed a solution that worked out great:
Message documents in couchdb use an arbitrary _id that can be generated by the server or the client
When the client sends a message, it generates an arbitrary _id and puts it into the database. The server observes this and dispatches it to twilio, then updates the database document by adding a twilio_id property to the document
I created a view to index the documents by twilio_id
When the server starts, it fetches the latest messages from twilio. In order to prevent adding duplicate records to the database, it queries the above view for each twilio id. For each match, it uses the match's _id and _rev to perform an update. For records with no matches, it generates a new arbitrary _id to perform an insert.
For anyone curious, here's the code.
Thanks again for your responses!

How to send old messages with Websockets

I've got a working Websockets example, where clients receive messages from the server.
I'm not sure how I should send old messages to clients when they connect.
Example:
Each client supplies their name when they connect
The server responds with "[name] just connected" (to all clients)
Any new clients would NOT get these messages
I'm wondering if there's any way clients can receive old messages (either all of them, or messages in the last 5 minutes would be acceptable).
I suspect I may have to capture this information myself, store it somewhere (like a database) and send the messages to new clients myself. Is that right, or am I missing something?
If anyone has pseudo code, or a link to an example of how others have implemented this, that would be handy.
You could do something like this:
Each message should have an id -> muid (Message Unique ID)
Each time a client send s a message, it gets an ACK from the server along with the muid for the sent message.
Each time a new message is received in the server side, a muid is assigned, sent with the ACK and also sent with the message to every connected user. This way the view will be able to present, for every user, the same sequence at some point in the time.
Each time a new user connects it sends the last muid it has received so the server knows where this user stopped receiving messages. The server could then send as many old messages as you want, depending on the kind of storage you implement:
Full history: I would recommend a database storage with proper indexing
Last N messages: Depending on the size of N you could simply store the last N messages in a fixed size Array and send them, all or the needed chunk, on each reconnection. Keep in mind that this will consume memory so, storing last 1024 messages for 1024 different chats would eat quite a bit of memory, specially if messages are of unlimited size.
Hope it helps
You will have to capture it by your own and store it on server... once user connects you will have to name that data to all connected clients and the messages which you have stored back to the user who has connected. So, you will have to code to broadcast the data to users
By the way what are you using server side? (Node, Erlang , etc)
You can check following link if you are using node.js
http://martinsikora.com/nodejs-and-websocket-simple-chat-tutorial

How much data is loaded onto the client after a Meteor.subscribe?

Inside of Meteor.publish's callback function we return cursors. So does it mean that when we do a subscribe on the client, only a minimum amount of data is transferred to the client?
It's only when we do something on the client like cursor.fetch or cursor.forEach that the collection data is actually being pushed onto the client?
Thanks!
Everything that is defined in your Meteor.publish will be sent down to the client:
e.g if you have
Meteor.publish("rooms", function() {
return Rooms.find();
});
The entire result of the query Rooms.find() is sent down to the client. The .fetch() only fetches all the data from the cursor which helps stop looping through all of the unnecessary data (in memory). The cursor's fetch/forEach won't influence the subscription and data availed from the server.
To control what comes down to the client you need to explicitly tell the subscription/publish what to publish to the client. A good example on how to do this is the Chat & Rooms example at the meteor docs under subscribe

Sending data only to chosen users using Socket.io-node

Is it possible to send data using socket.io-node just to chosen group of users? For example, how could I implement chat with different rooms? I dont want .broadcast() to send data to all logged in users.
Normally you should have for each room a list of connected user and those user all have a client object that you should have stored somewhere. So when you want to send a message to a specific room, you just have to iterate over the connected user of that room and access their client object and send the data.
In short, it is possible you just have to send your data to each of the users in the group one-by-one.
socket.io has a grouping functionality built in
On the socket object for a single connection, like you get passed when a new user connects, you can call .join('roomName') where roomName is any string you want to use to identify the "room", you could use a room name like "profile/14" to create a channel for updates to user #14's profile.
Then on the main io object do something like:
io.sockets.in('profile/14').emit('newComment', {message:'hello'});
The message will go out to all connections that have .join()'d the given room.
Typically I'll have my client emit a "hello" event onConnect that identifies what content the client is interested in subscribing to, and then on the server side my handler for the "hello" event handles .join()'ing the client into whatever rooms are needed

Categories