Integrate XMPP instead of WebSync in IceLink Library - p2p

I am using IceLink library for peer to peer communication. We need to deploy two servers IceLink and WebSync for that as listed here
http://docs.frozenmountain.com/icelink2/index.html#class=icelink-getting-started-creating-a-conference-10_ios-macosx
But I want to use XMPP instead of WebSync.
Following code is used for WebSync now I just want to replace it so that I can use XMPP instead if this WebSync.
[client addOnStreamFailureWithValueBlock:^(FMWebSyncStreamFailureArgs *e)
{
[conference unlinkAll];
}];
// Add a couple event handlers to the conference to send
// generated offers/answers and candidates to a peer.
// The peer ID is something we define later. In this case,
// it represents the remote WebSync client ID. WebSync's
// "notify" method is used to send data to a specific client.
[conference addOnLinkOfferAnswerWithValueBlock:^(FMIceLinkLinkOfferAnswerArgs *e)
{
[client notifyWithNotifyArgs:[FMWebSyncNotifyArgs notifyArgsWithClientId:[FMGuid guidWithG:e.peerId]
dataJson:[e.offerAnswer toJson]
tag:#"offeranswer"]];
}];
[conference addOnLinkCandidateWithValueBlock:^(FMIceLinkLinkCandidateArgs *e)
{
[client notifyWithNotifyArgs:[FMWebSyncNotifyArgs notifyArgsWithClientId:[FMGuid guidWithG:e.peerId]
dataJson:[e.candidate toJson]
tag:#"candidate"]];
}];
// Add an event handler to the WebSync client to receive
// incoming offers/answers and candidates from a peer.
// Call the "receiveOfferAnswer" or "receiveCandidate"
// method to pass the information to the conference.
[client addOnNotifyWithValueBlock:^(FMWebSyncNotifyReceiveArgs *e)
{
NSString *peerId = [e.notifyingClient.clientId toString];
NSObject *peerState = e.notifyingClient.boundRecords;
if ([e.tag isEqualToString:#"offeranswer"])
{
[conference receiveOfferAnswerWithOfferAnswer:[FMIceLinkOfferAnswer fromJsonWithOfferAnswerJson:e.dataJson]
peerId:peerId
peerState:peerState];
}
else if ([e.tag isEqualToString:#"candidate"])
{
[conference receiveCandidateWithCandidate:[FMIceLinkCandidate fromJsonWithCandidateJson:e.dataJson]
peerId:peerId];
}
}];
// Subscribe to a WebSync channel. When another client joins the same
// channel, create a P2P link. When a client leaves, destroy it.
FMWebSyncSubscribeArgs *subscribeArgs = [FMWebSyncSubscribeArgs subscribeArgsWithChannel:#"/mychat"];
[subscribeArgs setOnSuccessBlock:^(FMWebSyncSubscribeSuccessArgs *e)
{
[self writeLine:#"-- Subscribed to %#.", e.channel];
}];
[subscribeArgs setOnFailureBlock:^(FMWebSyncSubscribeFailureArgs *e)
{
[self writeLine:#"-- Could not subscribe to %#. %#", e.channel, e.exception.message];
}];
[subscribeArgs setOnReceiveBlock:^(FMWebSyncSubscribeReceiveArgs *e) { }];
[subscribeArgs setOnClientSubscribeWithOnClientSubscribeBlock:^(FMWebSyncSubscribersClientSubscribeArgs *e)
{
NSString *peerId = [e.subscribedClient.clientId toString];
NSObject *peerState = e.subscribedClient.boundRecords;
[conference linkWithPeerId:peerId peerState:peerState];
}];
[subscribeArgs setOnClientUnsubscribeWithOnClientUnsubscribeBlock:^(FMWebSyncSubscribersClientUnsubscribeArgs *e)
{
NSString *peerId = [e.unsubscribedClient.clientId toString];
[conference unlinkWithPeerId:peerId];
}];
[client subscribeWithSubscribeArgs:subscribeArgs];

There is no question here. At any rate, as a general answer to future readers, you can use literally any method of communication between the clients. You could even email and copy/paste the information that's sent over the signalling channel (something I actually did for my earliest WebRTC stuff, to avoid the complexity of having a signalling server).
You simply must use your signalling channel to exchange the following information (I'm using the C# naming, but other languages should have similar names, but following the language conventions):
One peer's offer (generated via callback OnLinkOfferAnswer once you call Conference.Link) is sent to the other peer. To call Conference.Link, you need to know the peer ID of who you're connecting to, so may need your signalling server to send this data to us (in my usage, I actually connected to an intermediate server for recording, and thus could use a hardcoded "server" peer ID).
That peer's answer (generated via callback OnLinkOfferAnswer once you register the offer with Conference.ReceiveOfferAnswer) is sent to the first peer (and registered the same way).
Both peers will generate ICE candidates with the OnLinkCandidate callback (multiple times), which must be sent to the other peer and registered with Conference.ReceiveCandidate.
Note that this all occurs asynchronously, so the ICE candidates could be generated before the answer is received. That is fine, as IceLink will internally manage them.
You must keep track of which peer this is meant for (the offer/answer and candidates are peer-specific). It's entirely up to your signalling library (XMPP here) to generate some kind of unique peer ID so that you can identify users (you could do it yourself if your signalling library does not).
The data that gets exchanged is JSON, but you shouldn't have to modify it in the typical usage. The OfferAnswer and Candidate objects have FromJson and ToJson methods to handle the conversion.

Related

How to share dynamic objects across workers?

I'm trying to make a game, which works on rooms, lobby and such (imagine the chat app, except with additional checks/information storing).
Let's say, I have a module room.js
var EventEmitter = require('events');
class Room extends EventEmitter {
constructor (id, name) {
super();
this.id = id;
this.name = name;
this.users = [];
}
}
Room.prototype.addUser = function (user) {
if(this.users.indexOf(user) === -1) {
this.users.push(user);
this.emit('user_joined', user);
} else {
/* error handling */
}
};
module.exports = {
Room: Room,
byId: function (id) {
// where should I look up?
}
};
How can I get exactly this object (with events)? How can I access events emitted by this object?
In a single instance of node, I would do something like:
var rooms = [];
var room = new Room(1234, 'test room');
room.on('user_joined', console.log);
rooms.push(room);
Also, I don't quite understood how Redis is actually helping (is it replacement of EventEmitter?)
Regards.
EDIT: Would accept PM2 solutions too.
Instead of handling rooms in Node, you can replace them with channels in Redis).
When a new client wants to join in a room, the NodeJS app returns it the ID of this given room (that is to say the name of the channel), then the client suscribes to the selected room (your client is directly connected to Redis.
You can use a Redis Set to manage the list of rooms.
In this scenario, you don't need any event emitter, and your node servers are stateless.
Otherwise, it would mean Redis would be exposed on the Internet (assuming your game is public), so you must activate Redis authentication. A major problem with this solution is that you have to give the server password to all clients, so it's definitely unsecure.
Moreover, Redis' performances allow brute force attacks so exposing it on Internet is not recommended. That's why I think all communications should go through a Node instance, even if Redis is used as a backend.
To solve this, you can use socket.io to open sockets between Node and your clients, and make the Node instances (not the client) subscribe to the Redis channel. When a message is published by Redis, send it to the client through the socket. And add a layer of authentication to ensure only valid clients connect to a given channel.
Event emitter is not required. It's the Redis client which will be an event emitter (like in this example based on ioRedis)

how can I make private chat rooms with sockjs?

I am trying to make a chat system where only two users are able to talk to each other at a time ( much like facebook's chat )
I've tried multiplexing, using mongoDB's _id as the name so every channel is unique.
The problem I'm facing is that I cannot direct a message to a single client connection.
this is the client side code that first sends the message
$scope.sendMessage = function() {
specificChannel.send(message)
$scope.messageText = '';
};
this is the server side receiving the message
specificChannel.on('connection', function (conn) {
conn.on('data', function(message){
conn.write('message')
}
}
When I send a message, to any channel, every channel still receives the message.
How can I make it so that each client only listens to the messages sent to a specific channel?
It appeared that SockJS doesn't support "private" channels. I used the following solution for a similar issue:
var channel_id = 'my-very-private-channel'
var connection = new SockJS('/pubsub', '')
connection.onopen = function(){
connection.send({'method': 'set-channel', 'data': {'channel': channel_id}})
}
Backend solution is specific for every technology stack so I can't give a universal solution here. General idea is the following:
1) Parse the message in "on_message" function to find the requested "method name"
2) If the method is "set-channel" -> set the "self.channel" to this value
3) Broadcast further messages to subscribers with the same channel (I'm using Redis for that, but it also depends on your platform)
Hope it helps!

redis pub/sub model for notification system with selective pushing

I'm looking to build a notification system like facebook where my application logic (publisher) will push notifications generated by all users to the redis system.
User 1 --------------> Redis (Channel : notifications)
User 2 --------------> Redis (Channel : notifications)
User 3 --------------> Redis (Channel : notifications)
As you can see above, all the activity generated by users 1,2,3 are sent to the same channel notifications.
I have a node.js /socket.io server listening to redis as a subscriber for these notifications. (subscribed to channel notifications)
Now, How do I selectively push only certain notifications to certain subscribers ? Like facebook notifications, I will get notifications only for private messages sent to me, not for messages sent to others.
Thanks for your help in advance.
Create personal channel per user, e.g., notifications.User1, notifications.User2, ...,
and make each user subscribe his/her channel.
(You don't need to worry of the channels' size.)
If the users are sharing one redis connection,
You may need to identify the receiver user from the channel name whenever the connection receives any subscription message.
UPDATE:
I assume this scenario:
Your nodejs app may know the user's id when the user logs in your app.
Then, your app subscribes the channel for the user only, e.g., like this:
(this is kind of pseudo-code, I'm not sure of nodejs.)
onUserLoggedIn(string userId) {
...
string userChannel = "notifications.user." + userId;
// If userId == "yash",
// then userChannel == "notifications.user.yash"
redisConnection.command("subscribe", userChannel);
...
}
And when your connection is received a published message from your redis server:
onMessagePublished(string channel, string message) {
...
// You can get userId from channel id.
vector<string> tokens = parseTokensFromChannel(channel);
// If channel == "notifications.user.yash",
// tokens == {"notifications", "user", "yash"};
if (tokens[0] == "notifications") {
if (tokens[1] == "user") {
...
string userId = tokens[2];
onMessagePublishedForUser(userId, message);
...
} else {
...
}
...
} else {
...
}
...
}
onMessagePublishedForUser(string userId, string message) {
// You can handle the message for each user.
// I don't think your user may need it's own handling code per user.
...
}
In this scenario, you don't need any hard-coding at all.
Your redis connection can subscribe any redis channels by simply sending command 'subscribe'.
I assume your user will send custom identifiable user information(at least the user's id) to the nodejs server so that your nodejs app can make the channel name dynamic.
(If your user will not send the user's id, how do you identify each user?)

How to do an Idle status for a Pusher user?

Typical chat app. Using the presence channel to tell who is online, but looking for an elegant way to mark a User in the presence channel with an idle flag.
The full solution to this is probably reasonably complicated and it also depends on the runtime - I'm going to assume web web client.
Ultimately you need a way of doing two things:
to detect a user as being "idle"
to signal all other users about that user being idel
To detect a user is idle
window.onblur so you know your app window is no longer focused
mousemove tracking to see if the user is actually doing anything within your application.
In order to achieve this you probably just want a timeout and only if that timeout triggers do you send an event to indicate the user is idle:
var timeout = null;
function sendUserIdle() {
// see part 2
}
function resetIdleTracking() {
if( timeout !== null ) {
// reset
clearTimeout( timeout );
}
timeout = setTimeout( sendUserIdle, 3*60*1000 ); // 3 minutes
}
window.addEventListener( 'mousemove', resetIdleTracking );
Signal other users about idle users
A missing feature of Pusher presence channels IMO is the ability to update presence information. So, you need another way of achieving this. I think you have two solutions:
Enable client events on the presence channel and trigger an event from the idle user when your code detects the user becoming idle.
Send a message to the server from the idle client. The server then triggers a message telling the users that the user is idle.
See: accessing channel members.
1. Using client events
function sendUserIdle() {
var channel = pusher.channel( 'presence-<your-channel>' );
// client events have to have a 'client-' prefix
channel.trigger( 'client-user-idle', channel.members.me );
}
2. Sending to the server
function sendUserIdle() {
makeAjaxRequest( '/idle-notification-endpoint', channel.members.me );
}
Note: you can serialise channel.members.me using JSON.stringify( channel.members.me )
On the server (in pseudo code):
userInfo = getUserInfoFromRequest();
pusher.trigger( 'presence-<your-channel>', 'user-idle', userInfo );
Showing a client is idle
Upon receipt of the event you would update the list of users UI accordingly (mark that user as idle).
channel.bind( 'user-idle', function( user ) {
var uniqueUserId = user.id;
// update UI
}

What is the difference between Gateway and Service Activator?

What is the difference between Gateway and Service Activator as Message Endpoints (in terms of Enterprise Integration Patterns)?
http://eaipatterns.com/
Typically, a service activator is used to invoke a local service, in such a manner that the service doesn't know it's being invoked from a messaging system.
A gateway s typically an entry or exit point for the messaging system.
The service activator calls a method on an object where the application developer provides the implementation. Spring Integration takes care of calling the method with messages from the input channel and shunting the results off to some output channel. The application-provided code can do some arbitrary work.
For the gateway the application developer provides only an interface, its implementation is provided by Spring.
An appendix to the Spring Integration documentation includes a Cafe example where Barista is a service called through a service activator, and Cafe is a gateway.
The application's main method looks up a Cafe object from the Spring application context and calls placeOrder, on it, passing an Order in as an argument:
public static void main(String[] args) {
AbstractApplicationContext context = null;
if (args.length > 0) {
context = new FileSystemXmlApplicationContext(args);
}
else {
context = new ClassPathXmlApplicationContext(
"cafeDemo.xml", CafeDemo.class);
}
Cafe cafe = (Cafe) context.getBean("cafe");
for (int i = 1; i <= 100; i++) {
Order order = new Order(i);
order.addItem(DrinkType.LATTE, 2, false);
order.addItem(DrinkType.MOCHA, 3, true);
cafe.placeOrder(order);
}
}
The Cafe is an interface that the application does not provide an implementation for. Spring generates an implementation that sends the Orders passed into it down the input channel called "orders".
Further down the pipeline, there are two service-activators that have a reference to the Barista. The Barista is a POJO that has code for creating a Drink like this:
public Drink prepareHotDrink(OrderItem orderItem) {
try {
Thread.sleep(this.hotDrinkDelay);
System.out.println(Thread.currentThread().getName()
+ " prepared hot drink #" + hotDrinkCounter.incrementAndGet()
+ " for order #" + orderItem.getOrder().getNumber()
+ ": " + orderItem);
return new Drink(orderItem.getOrder().getNumber(),
orderItem.getDrinkType(),
orderItem.isIced(), orderItem.getShots());
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
}
The Barista receives drink orders from the service-activator's input channel and has a method called on it that returns a Drink, which gets sent down the service-activator's output channel, "preparedDrinks".
For me the gateway is used for making an abstraction and provide a normalised API for one or more back-end services.
E.g You have 5 providers which are using different ways to interface with you (SOAP, REST, XML/http, whatever), but your client want only one way to get the data (let says json/REST).
The gateway will convert the json request form your client and convert them to the right backend with its own protocol, after it will convert the backend response to json to give the response to your client.
The service activator acts more as a trigger on an incoming message. Let say your activator poll a database for incoming message and then when the condition meet the "activation" it calls the underlying service.
Info for gateway here.
Info for Activator here.

Resources