Two user getting control access in socket.io - race condition - javascript

Two or more users are notified for an available room.
How I am trying to prevent it, and make it so that only one user will be notified available at a time.
socket.on('Check', function (room) {
io.in(room).clients((error, clients) => {
var hasPush = false;
clients.forEach(function (clientId) {
var client = io.sockets.connected[clientId];
if (client.hasPush) {
hasPush = true;
socket.emit('busy', room);
return;
}
}, this);
if (!hasPush) {
socket.hasPush = true;
socket.emit('available', room);
}
});
});
I have tried to prevent using a shared variable but that didn't work.
rooms = {};
socket.on('check', function (room) {
if (!rooms[room]) {
rooms[room] = true;
}
else {
socket.emit('busy', room);
return;
}
.......
rooms[room] = false;
}

Now I have added two servers with nginx and redis, redis-lock work for me.

Related

WEBRTC, establish connection if user A to user B. With different Video and Audio constraints

This code, is handling then new User connected.
User Class
class User{
constructor(friendID){
this.friendID = friendID;
this.userName = null;
this.myPeer = new RTCPeerConnection(servers);
this.videoManager = new VideoManager();
this.setup_events();
}
get name(){
return this.userName;
}
get id(){
return this.friendID;
}
setup_events(){
let this_instance = this;
this.myPeer.onicecandidate = function(event){
if (event.candidate !== null){
LOG("ICE CANDIDATE SEND TO " + this_instance.friendID);
LOG_obj(event.candidate);
socket.emit("ice-candidate", event.candidate, this_instance.friendID);
}else{
LOG("EMPTY CANDIDATE");
}
}
this.myPeer.addEventListener('track', async(event) => {
const [remoteStream] = event.streams;
this_instance.videoManager.createVideo();
this_instance.videoManager.setStream(remoteStream);
LOG("ADDED stream to VideoObject");
});
}
add_candidate(candidate){
this.myPeer.addIceCandidate( new RTCIceCandidate(candidate) );
LOG("CANDIDATE ADDED TO PEER");
}
accept_offer(userOffer){
this.myPeer.setRemoteDescription(new RTCSessionDescription(userOffer));
LOG("ACCEPTED OFFER");
}
async create_offer(){
MediaRules.get_MediaRules()
.then( (mediaRules) => {
navigator.mediaDevices.getUserMedia(mediaRules).then( (mediaStream) =>{
let tracks = mediaStream.getTracks();
tracks.forEach( track => { this.myPeer.addTrack(track, mediaStream); });
LOG("ADDED ALL TRACKS");
}).then( () => {
this.myPeer.createOffer(mediaRules).then( (offerObj) => {
this.myPeer.setLocalDescription(offerObj);
socket.emit("user-offer", offerObj, this.friendID);
});
});
});
}
accept_answer(userAnswer){
this.myPeer.setRemoteDescription(new RTCSessionDescription(userAnswer));
LOG("ACCEPTED ANSWER");
}
async create_answer(){
MediaRules.get_MediaRules().then( (mediaRules) => {
navigator.mediaDevices.getUserMedia(mediaRules).then( (mediaStream) => {
let tracks = mediaStream.getTracks();
tracks.forEach( track => { this.myPeer.addTrack(track, mediaStream); });
LOG("ADDED ALL TRACKS");
}).then( () => {
this.myPeer.createAnswer(mediaRules).then( (answerObj) => {
this.myPeer.setLocalDescription(answerObj);
socket.emit("user-answer", answerObj, this.friendID);
});
});
});
}
}
User Pool
class UsersPool{
constructor(){
this.UsersMap = {};
}
addUser(userObj){
this.UsersMap[userObj.id] = userObj;
}
accept_IceCandidate(candidateObject, user_id){
this.UsersMap[user_id].add_candidate(candidateObject);
}
accept_Offer(offerObject, user_id){
LOG("ACCEPT OFFER FROM " + user_id);
this.UsersMap[user_id].accept_offer(offerObject);
}
accept_Answer(answerObject, user_id){
this.UsersMap[user_id].accept_answer(answerObject);
}
async CreateSendOffer(user_id){
await this.UsersMap[user_id].create_offer();
}
async CreateSendAnswer(user_id){
await this.UsersMap[user_id].create_answer();
}
}
Media Constraints
class MediaConstraints{
async get_MediaRules(){
let mediaRules = { video: false, audio: false };
let devicesEnum = await navigator.mediaDevices.enumerateDevices();
devicesEnum.forEach( device => {
if ( device.kind == "audioinput" ){
mediaRules["audio"] = true;
}
else if ( device.kind == "videoinput"){
mediaRules["video"] = true;
}
});
return mediaRules;
}
}
Video Manager (creates video element by user)
class VideoManager {
constructor(){
this.videoObject = null;
}
createVideo(){
let videoObject = document.createElement("video");
let divVideo = document.createElement("div");
videoObject.setAttribute('width', "600");
videoObject.setAttribute('height', "800");
divVideo.appendChild(videoObject);
document.body.appendChild(divVideo);
this.videoObject = videoObject;
}
setStream(stream){
this.videoObject.srcObject = stream;
this.videoObject.play();
}
}
Well, the problem is here. icecandidate is working nice, signaling server is working too.
TURN/STUN server works fine.
My main question is how to create constraints and setup correctly Offer and Answer if User A don't have webcamera but User B has.
At the moment i get error that STUN server is broken, but this is because peers can't finish establishing connection between each other.
How to make it, if i have only microphone on my Laptop, but on other Laptop i have video and microphone.
EDIT 0: Well, looks like WebRTC doesn't like if constraints are different, if User A create offer with {video: false, audio: true}, and send it to User B, and User B creates answer with {video: true, audio: true} then it fails to connect because constraints are different.
Still don't understand why this is a problem.
EDIT 1: Looks like the only way is to use addTransceiver and to control manually media.
Actually, the problem was not in Linux Firefox version.
The Problem was in Offer-Answer exchange and using AddTrack function.
Problem was that, if User A doesn't have WebCamera but User B have, it can't finish normally connection, and as a result ice-candidate is not triggered.
So, i solved it using AddTransceiver().
An example how to make connection if someone doesn't have webacamera or microphone.
Create RTCPeerConnection.
let myPeer = new RTCPeerConnection();
Make a function that is looking for active devices. so in result you need to get
real_devices = { video: false, audio: true } for example if there is not webcamera
async get_MediaRules(){
let devicesEnum = await navigator.mediaDevices.enumerateDevices();
devicesEnum.forEach( device => {
if ( device.kind == "audioinput" ){
this.physicalRule["audio"] = true;
}
else if ( device.kind == "videoinput"){
this.physicalRule["video"] = true;
}
});
return this.physicalRule;
}
Get mediaStream
let myStream = navigator.mediaDevices.getUserMedia(real_devices);
Create Transceiver and add it to connection ( do this on Caller Side ).
async transceiverSetup(){
let videoTracks = myStream.getVideoTracks();
let audioTracks = myStream.getAudioTracks();
if (videoTracks.length > 0){
this.clientPeer.addTransceiver(videoTracks[0]);
}
else{
let video = this.myStream.addTransceiver("video");
video.direction = "recvonly";
}
if (audioTracks.length > 0){
this.myStream.addTransceiver(audioTracks[0]);
}
else{
let audio = this.myStream.addTransceiver("audio");
audio.direction = "recvonly";
}
}
After that you call
myPeer.createOffer()...
And on other side after you receiver Offer, you call remoteTrackSetup(). function and you setup on your side transceivers.
async configure_transceiver(mediaTracks, transceivers){
transceivers.forEach( async(any) => {
if (any.receiver.track){
if (mediaTracks.length > 0){
any.direction = "sendrecv";
await any.sender.replaceTrack(mediaTracks[0]);
}
else{
any.direction = "recvonly";
await any.sender.replaceTrack(null);
}
}else{
if (mediaTracks.length > 0){
any.direction = "sendonly";
await any.sender.replaceTrack(mediaTracks[0]);
}else{
any.direction = "inactive";
await mediaTracks.sender.replaceTrack(null);
}
}
});
}
async remoteTrackSetup(){
let mediaStream = GlobalState.Media;
let audioTracks = mediaStream.getAudioTracks();
let videoTracks = mediaStream.getVideoTracks();
let transceivers = this.clientPeer.getTransceivers();
let audioTransceivers = transceivers.filter(function(tr){
return tr.receiver.track.kind == "audio";
});
let videoTransceivers = transceivers.filter(function(tr){
return tr.receiver.track.kind == "video";
});
await this.configure_transceiver(audioTracks, audioTransceivers);
await this.configure_transceiver(videoTracks, videoTransceivers);
}
After those functions you call
myPeer.createAnswer()...
And connection is fully established.
Here is code for ontrack event.
setTransceiver(transceiver){
if(!this.videoObject.srcObject || this.videoObject.srcObject.getTracks().length == 2){
this.videoObject.srcObject = new MediaStream([transceiver.receiver.track]);
}else{
this.videoObject.srcObject.addTrack(transceiver.receiver.track);
}
}

WebRTC-Problem: Cannot create answer in stable (no Chrome but AJAX signalizing involved)

can somebody help me out a little? I am a little stuck.
I am trying to write a signaling process with ajax and a database involved (this is just for learning the basics of WebRTC for now).
I am receiving the SDP fine from the JSON-object as it seems, but then I always get an error "Cannot create answer in stable" when I try to create an answer in get_remote_offer() for pc_partner.
I am pretty sure it is something obvious, but I am pretty new to WebRTC and just can't see what.
I am using Firefox here and just trying to connect two instances of it (one in private mode, one in "normal" mode, but I am trying to make it work for remote users.
This is my code:
var opt;
var video_el_partner;
var video_el_local;
var pc_partner;
var pc_local;
var interval_gro;
var remote_offer_available = false;
var service_url = "https://xyz.de/webrtc";
var pwd = "xxx";
var signaling_url = "https://xyz.de/webrtc/sdp_transfer.php";
function init_stream(video_partner_id, video_local_id, allow_video, allow_audio){
if (location.protocol === 'https:') { // only possible for https!
pc_local = new RTCPeerConnection();
pc_partner = new RTCPeerConnection();
if(document.getElementById(video_partner_id) != null){
video_el_partner = document.getElementById(video_partner_id);
video_el_local = document.getElementById(video_local_id);
if(allow_video == null){
allow_video = true;
}
if(allow_audio == null){
allow_audio = true;
}
opt = { audio: allow_audio, video: allow_video };
if(typeof navigator != 'undefined' && typeof navigator.mediaDevices != 'undefined' && navigator.mediaDevices.getUserMedia != null){
navigator.mediaDevices.getUserMedia(opt).then (
function (this_stream){
// local video directly into video element:
video_el_local.srcObject = this_stream;
// remote one is more insteresting:
pc_local.addStream(this_stream);
pc_local.createOffer().then(
function (this_sdp) {
// sdp (session dependend protocol object) is now available... this would need to go to a server somehow now.
// they use socket.io for that... maybe I can use my own thing to do that?
pc_local.setLocalDescription(this_sdp);
var this_sdp_json = JSON.stringify(this_sdp)
var params_ins = "mode=insert_offer&sdp_con=" + this_sdp_json + "&pass=" + pwd + "&service_url=" + service_url;
ajax_request_simple (
signaling_url,
params_ins,
function (res_ins) {
// insert done. Lets read for another candidate.
console.log('Set Interval!');
interval_gro = window.setInterval('get_remote_offer();', 5000);
}
);
}
);
}
).catch(
function (error) {
console.log('Problem: ');
console.log(error);
}
);
} else {
console.log("navgiator or navigator.mediaDevices is not defined.");
}
}
} else {
console.log('init_stream(): We can only do anything like that on https-connections! Http is not supported by the browser!');
}
}
window.onload = function () {
document.getElementById('button_start_stream').onclick = function () {
init_stream('video_partner', 'video_local', true, false);
}
}
function is_json_str(str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
function get_remote_offer() {
var params_read = "mode=get_offer&pass=" + pwd + "&service_url=" + service_url;
ajax_request_simple (
signaling_url,
params_read,
function (res_read) {
// done.
if(is_json_str(res_read)){
// seems like we get one now.
// lets use that to connect and stream the video to the remote view.
var partner_offer = res_read;
partner_offer = JSON.parse(partner_offer);
// clear interval if found.
window.clearInterval(interval_gro);
console.log('Cleared Interval. Found!');
pc_local.setRemoteDescription(
new RTCSessionDescription(partner_offer), function(){
// video_el_partner.srcObject = event.stream;
pc_local.onicecandidate = function (e) {
if ( e.candidate != null ) {
pc_partner.addIceCandidate( new RTCIceCandidate(e.candidate) );
}
};
pc_partner.onicecandidate = function (e) {
if ( e.candidate != null ) {
pc_local.addIceCandidate( new RTCIceCandidate(e.candidate) );
}
};
pc_partner.createAnswer(
function (offer) {
pc_local.setRemoteDescription(offer);
pc_partner.setLocalDescription(offer);
}
);
// pc_local.ontrack = function (evt) {
// video_el_local.srcObject = evt.stream;
// };
pc_partner.ontrack = function (evt) {
video_el_partner.srcObject = evt.stream;
};
},
function(e) {
console.log("Problem while doing client-answer: ", e);
}
);
} else {
console.log("Can not parse: ");
console.log(res_read);
}
}
);
}
Sorry for the mix of promises and callbacks... I tried a couple of things out just in case... when it is working I will rewrite the callback parts.
Thank you very much in advance for any hint you can give me :).
Best regards and thanks for reading till now ;).
Fuchur

how to call a function continously

I want to be able to run this code continuously, so that the function I called is always checking whether entered is true or false
index.js
io.on('connection', function(socket) {
var entered = require('./main.js').onProximityBoolean; /// i want this to continuously be checking itself
if (entered == true) {
socket.emit('enterProximity');
}
}
main.js
var enter = false;
function onProximityBoolean(enter) {
if (enter === true) {
console.log(enter);
return true;
} else {
return false;
}
}
function isWithinBounds() {
//inside here is sensor code that says if the object comes close
//then enter will be true else enter will be false
//this part accurately redefines the onProximityBoolean
}
module.exports = {
onProximityBoolean: onProximityBoolean(enter)
};
You may want to implement an observer with a setter / getter:
"main.js";
var state=false;
var handlers=[];
module.exports={
get proximityBoolean(){
return state;
},
set proximityBoolean(value){
if(value!==state){
state=value;
handlers.forEach(func=>func(value));
},
observe(func){
handlers.push(func);
}
}
So you can do:
var main = require('./main.js');
io.on('connection', function(socket) {
main.observe(function(entered){
if (entered == true) {
socket.emit('enterProximity');
}
});
}
//trigger it somewhen
main.proximityBoolean=true;
Alternatively use setInterval to check regularily:
var main=require('./main.js');
io.on('connection', function(socket){
setInterval(function() {
var entered = main.onProximityBoolean;
if (entered == true) {
socket.emit('enterProximity');
}
},1000);
});
i think you should use cron jobs . Here the npm repo and example of use
Try to use setInterval() method. You can use a while(true) loop but that slow the code a lot, so what I suggest you to do is:
io.on('connection', function(socket){
setInterval(function(socket) {
var entered = require('./main.js').onProximityBoolean;
if (entered == true) {
socket.emit('enterProximity');
}
},1000);}); //after 1 second the function will be called [1000ms]

Getting True/False values from Firebase if a tree/value exists. Cannot get this to work

Been at this all day. I am building a social network.
It checks to see if two users have connected to each other, if they have a structure like this is created.
ROOT->Groups->RequestingUser->UserTheyWant
When two people have each requested each other, I want two variables to compare so if both are true I can continue. I can not get this to work.
I have tried so many iterations of this but I cannot get true/false values from this, I don't want to post all my attempts... Hopefully this gets it across.
I just need the two functions didYouRequest and didTheyRequest to return true/false OR didTheVisitorConnect / checkIfOtherProfilePersonConnected actually set outside the damn function so i can compare them.
$scope.connect = () => {
// my userid -> and set key in there for who they want to visit
console.log('added ' + $scope.profile.name + ' as friend.');
return fbGroupsDb.child(userLoggedIn).child(userUID).set(
{
connected: true
});
};
var didTheVisitorConnect = false;
$scope.didTheVisitorConnect = false;
var checkIfOtherProfilePersonConnected = false;
$scope.checkIfOtherProfileConnected = false;
var didYouRequest = fbGroupsDb.child(userLoggedIn).child(userUID).once('value').then( function (snapshot) {
if (snapshot.val() !== null) {
$scope.didTheVisitorConnect = true;
didTheVisitorConnect = true;
console.log('Did You Request? : ', didTheVisitorConnect);
return true;
} else {
$scope.didTheVisitorConnect = false;
didTheVisitorConnect = false;
console.log('Did You Request? : ', didTheVisitorConnect);
return false;
}
});
var didTheyRequest = fbGroupsDb.child(userUID).child(userLoggedIn).once('value').then( function (snapshot) {
if (snapshot.val() !== null) {
$scope.checkIfOtherProfileConnected = true;
checkIfOtherProfilePersonConnected = true;
console.log('Have they requested You? : ', checkIfOtherProfilePersonConnected);
return true;
} else {
$scope.checkIfOtherProfileConnected = false;
checkIfOtherProfilePersonConnected = false;
console.log('Have they requested You? : ', checkIfOtherProfilePersonConnected);
return false;
}
});
You seem to be looking for
Promise.all([didYouRequest, didTheyRequest]).then(function([you, they]) {
if (you && they) {
…
} else {
…
}
});

Kaazing, AngularJS & BasicChallengeHandler

I'm trying to use the Kaazing Library in our HTML5 client. I already implemented it in a java client and it worked. It seems that there is a problem with the LoginHandler. When I debug the code it ends in a endless loop in the LoginHandler. The line callback(new PasswordAuthentication(usr, pwd)) is called over and over again:
// Configure a Basic Challenge Handler
var basicHandler = new BasicChallengeHandler();
basicHandler.loginHandler = function(callback) {
callback(new PasswordAuthentication(usr, pwd));
}
JmsConnectionProperties jmsProps = new JmsConnectionProperties();
jmsProps.connectionTimeout = 1000000;
jmsProps.reconnectAttemptsMax = -1;
jmsProps.reconnectDelay = 3000;
jmsProps.shutdownDelay = 5000;
console.log("Connect to: " + url);
// Create Connection Factory
jmsConnectionFactory = new JmsConnectionFactory(url, jmsProps);
websocketFactory = jmsConnectionFactory.getWebSocketFactory();
websocketFactory.setChallengeHandler(basicHandler);
// reate Connection future handler for the result
try {
if (connection == null) {
var connectionFuture = jmsConnectionFactory.createConnection( function() {
try {
// never comes to this line!!!
connection = connectionFuture.getValue();
// DO SOME STUFF
} catch (e) {
console.dir(e);
// alert(e.message);
}
});
} else {
try {
connection.close(function() { /* Closed */
});
} finally {
connection = null;
}
}
} catch (ex) {
console.dir(ex);
}
Any help would be very appreciated!
Regards
Angela
Is it possible that the username and password combination is incorrect, so that the client is being re-challenged?

Categories

Resources