WebRTC PeerJS Text Chat - Connect to multiple peerID at the same time - webrtc

Hi friends I have been working on to connect to multiple peer id for text chat when i connect to single peer alone it is working
but i am getting problem on connecting multiple peerid at the same time
For example for connecting to single peer we will be using this
var conn = peer.connect(peerId);
conn.on('open', function() {
connect(conn);
});
When i want to connect to multiple peer ID
For ex : var peerIDs = [ 'peerid 1', 'peerid 2', 'peerid 3']
I am using loop for this
for(var i=0 ; i < peerIDs.length ; i++){
conn = peer.connect(peerIDs[i]);
conn.on('open', function() {
connect(conn);
});
}
Here is the complete code:
var userId = new Date().getTime();
//Get the ID from the server
var peer = new Peer(userId, {host: 'localhost', port: 3031, path: '/',debug: true });
var conn;
var connections = [];
//to receive id from the server
peer.on('open', function(id){
console.log('the id is' +id);
});
//in case of error
peer.on('error', function(e){
alert(e.message);
})
//Awaits for the connection
peer.on('connection', connect);
function connect(c){
conn = c;
connections[c.peer].on('data', function(data){
var mess = document.createElement('div');
mess.innerHTML = '<span class="peer">' + c.peer + '</span>: ' + data;
angular.element( document.querySelector( '.messages' ) ).append(mess);
});
connections[c.peer].on('close', function(){
alert(c.peer + 'has left the chat');
});
}
//When user clicks the chat button
$scope.chat = function(){
alert('user clicked the connect button');
var peerIDs = [ 'peerid 1', 'peerid 2', 'peerid 3']
for(var i=0 ; i < peerIDs.length ; i++){
var conn = peer.connect(peerIDs[i]);
conn.on('open', function() {
connections.push(c);
connect(conn);
});
}
}
//send message when clicked
$scope.send = function(){
// For each active connection, send the message.
var msg = angular.element( document.querySelector( '#mess' ) ).val();
//Send message to all connected peers
for(var i in connections){
connections[i].send(msg);
}
angular.element( document.querySelector( '.messages' ) ).append('<div><span class="you">You: </span>' + msg
+ '</div>');
}
Can you please give the insight of how to achieve this.Your help will be Greatly appreciated.

To be able to have multi-connections at the same time, you just need to handle multi-connections at the same time.
// Array of remote peers ID and data channel
var remotePeerIds=[],// You need this to link with specific DOM element
connections=[]; // This is where you manage multi-connections
// create a Peer
var peer = new Peer({key: 'YOUR_KEY'}); // You can use your own peerID here
// Get your local peer id
peer.on('open', function(id) {
console.log('My peer ID is: ' + id);
});
// Start connection with other peer - and handle it
getConnect(remotePeerId){
var conn = peer.connect(remotePeerId);
handleConnection(conn);
}
// Ok now you need to handle the received connection too
peer.on('connection',function(conn){
handleConnection(conn);
});
// Handle connection - this is most important part
handleConnection(conn){
remotePeerIds.push(conn.peer); // Add remote peer to list
conn.on('open', function() {
console.log("Connected with peer: "+remotePeerId);
conn.on('data',function(data){
// You can do whatever you want with the data from this connection - this is also the main part
dataHandler(conn,data);
});
conn.on('error',function(){
// handle error
connectionError(conn);
});
conn.on('close',function(){
// Handle connection closed
connectionClose(conn);
});
connections.push(conn);
});
});
}
// So now you have multi-connections. If you want to send a message to all other peer, just using for loop with all the connections
function broadcastMessage(message){
for(var i=0;i<connections.length,i++){
connections[i].send(message);
}
}
// Or if you want to send a message to a specific peer - you need to know his peerid
function privateMessage(remotePeerId,message){
for(var i=0;i<connections.length,i++){
if(connections[i].peer==remotePeerId){
connections[i].send(message);
break;
}
}
}
This is the main part, you need to add some more code for connection handler in case of error and close.
That's it !

#luongnv89 Thanks for your response.
But i am getting problem when i try to connect multiple peerID
For Ex :
// Start connection with other peer - and handle it
function getConnect(remotePeerId){
var conn = peer.connect(remotePeerId);
handleConnection(conn);
}
var peerIDS = ['cttgmy43jy30udi0', 'mhzqhpn8rj4f5hfr'];
for(var i=0 ; i < peerIDS.length ; i++){
getConnect(peerIDS[i]);
}
When i ran the above loop i can able to connect with only the last peerid which i pass in the array in this case it is 'mhzqhpn8rj4f5hfr'
Here i post the console thing
PeerJS: Creating RTCPeerConnection.
peer.min.js:1 PeerJS: Listening for ICE candidates.
peer.min.js:1 PeerJS: Listening for `negotiationneeded`
peer.min.js:1 PeerJS: Listening for data channel
peer.min.js:1 PeerJS: Listening for remote stream
peer.min.js:1 PeerJS: Creating RTCPeerConnection.
peer.min.js:1 PeerJS: Listening for ICE candidates.
peer.min.js:1 PeerJS: Listening for `negotiationneeded`
peer.min.js:1 PeerJS: Listening for data channel
peer.min.js:1 PeerJS: Listening for remote stream
2peer.min.js:1 PeerJS: `negotiationneeded` triggered
peer.min.js:1 PeerJS: Created offer.
peer.min.js:1 PeerJS: Set localDescription: offer for: cttgmy43jy30udi0
peer.min.js:1 PeerJS: Created offer.
peer.min.js:1 PeerJS: Received ICE candidates for: cttgmy43jy30udi0
peer.min.js:1 PeerJS: Set localDescription: offer for: mhzqhpn8rj4f5hfr
peer.min.js:1 PeerJS: Received ICE candidates for: mhzqhpn8rj4f5hfr
peer.min.js:1 PeerJS: Setting remote description RTCSessionDescription {sdp: "v=0
↵o=- 8190108536299128797 2 IN IP4 127.0.0.1
↵s…id:data
↵a=sctpmap:5000 webrtc-datachannel 1024
↵", type: "answer"}
peer.min.js:1 PeerJS: Added ICE candidate for: cttgmy43jy30udi0
peer.min.js:1 PeerJS: Set remoteDescription: ANSWER for: cttgmy43jy30udi0
2peer.min.js:1 PeerJS: Added ICE candidate for: cttgmy43jy30udi0
2peer.min.js:1 PeerJS: Received ICE candidates for: mhzqhpn8rj4f5hfr
peer.min.js:1 PeerJS: Data channel connection success
peer.min.js:1 PeerJS: Setting remote description RTCSessionDescription {sdp: "v=0
↵o=Mozilla-SIPUA-35.0.1 15417 0 IN IP4 0.0.0.0…ap:5000 webrtc-datachannel 1024
↵a=setup:active
↵", type: "answer"}
2peer.min.js:1 PeerJS: Added ICE candidate for: mhzqhpn8rj4f5hfr
peer.min.js:1 PeerJS: Set remoteDescription: ANSWER for: mhzqhpn8rj4f5hfr
peer.min.js:1 PeerJS: Added ICE candidate for: mhzqhpn8rj4f5hfr
peer.min.js:1 PeerJS: Data channel connection success
peer.min.js:1 PeerJS: Added ICE candidate for: mhzqhpn8rj4f5hfr
So what did is to make it work i dont know whether it is the right approach or not...
I just set the delay between connections
var peerIDS = ['cttgmy43jy30udi0', 'mhzqhpn8rj4f5hfr'];
var arrLength = peerIDS.length;
var count = 0;
(function processConnection(){
if(arrLength <= count) return;
getConnect(peerIDS[count]);
count++;
setTimeout(function(){
processConnection()
}, 5000);
})();
And now it is working properly..Can you please tell me whether i am going in a right path or is there anyother better way to accomplish this

Related

stomp.js cannot receive message sent from pika (RabbitMQ version: 3.11.7)

I have a web page that should receive messages from RabbitMQ using STOMP:
<body>
<script src="stomp.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client#1.1/dist/sockjs.min.js"></script>
<script>
var client = Stomp.client('ws://localhost:15674/ws');
client.debug = null;
var sub = function(d) {
// print_first(d.body);
console.log("got the message! ", d.body)
}
var on_connect = function(x) {
id = client.subscribe("/topic/test", sub);
console.log("connected")
};
var on_error = function() {
console.log('error');
};
client.connect('guest', 'guest', on_connect, on_error, '/');
</script>
</body>
when I run this code, it shows connected in the console (so far so good)
I also have a python backend, which should send messages to the queue (send.py):
import pika
connection = pika.BlockingConnection(
pika.ConnectionParameters(host='localhost'))
channel = connection.channel()
channel.queue_declare("/topic/test")
properties = pika.BasicProperties(
content_type='application/json',
content_encoding='utf-8',
delivery_mode=2,
)
channel.basic_publish(exchange='',
routing_key='/topic/test',
body='Hello World!',
properties=properties)
The messages are sent (I ran the script with py -m send; the messages appear in the RabbitMQ Management) :
However the console.log in sub isn't running. Any idea how I can fix this?
Thanks!
Ended up using stomp.py instead of pika:
import stomp
PORT = 61613
LOCALHOST = '0.0.0.0'
conn = stomp.Connection11([(LOCALHOST, PORT)])
conn.connect('guest','guest')
conn.send(body="start",destination='/queue/test')
conn.send(body="end",destination='/queue/test')
conn.disconnect()

Timeout error while connecting to SQL Server: Connection Error: Failed to connect to servername\instancename in 15000ms

The error code is code ETIMEOUT. I am using SQL Server 2014, NodeJs version v12.16.2 and I am running this code in Visual Studio Code.
I have created the database and the table is also created with some records. For the server name, I have also tried giving the FQDN, but it didn't work.
This is the code snippet:
const express = require('express');
const app = express();
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
app.get('/', function(req, res) {
res.send('<hHii</h');
});
const sql = require('mssql');
var config = {
user: 'domain\username',
password: 'mypwd',
server: 'servername',
host: 'hostname',
port: '1433',
driver: 'tedious',
database: 'DBname',
options: {
instanceName: 'instancename'
}
};
sql.connect(config, function(err) {
if (err)
console.log(err);
let sqlRequest = new sql.Request();
//var sqlRequest = new sql.Request(connection)
let sqlQuery = 'SELECT * from TestTable';
sqlRequest.query(sqlQuery, function(err, data) {
if (err) console.log(err);
console.log(data);
//console.table(data.recordset);
// console.log(data.rowAffected);
//console.log(data.recordset[0]);
sql.close()
});
});
const webserver = app.listen(5000, function() {
console.log('server is up and running....');
});
Error:
tedious deprecated The default value for `config.options.enableArithAbort` will change from `false` to `true` in the next major version of `tedious`. Set the value to `true` or`false` explicitly to silence this message.
node_modules\mssql\lib\tedious\connection-pool.js:61:23
server is up and running....
ConnectionError: Failed to connect to servername\instantname in 15000ms
at Connection.<anonymous(..\SQL\Sample\sample\node_modules\mssql\lib\tedious\connection-pool.js:68:17)
at Object.onceWrapper (events.js:417:26)
at Connection.emit (events.js:310:20)
at Connection.connectTimeout (..\SQL\Sample\sample\node_modules\tedious\lib\connection.js:1195:10)
at Timeout._onTimeout (..\SQL\Sample\sample\node_modules\tedious\lib\connection.js:1157:12)
at listOnTimeout (internal/timers.js:549:17)
at processTimers (internal/timers.js:492:7) {
code: 'ETIMEOUT',
originalError: ConnectionError: Failed to connect to INPUNPSURWADE\DA in 15000ms
at ConnectionError (..\SQL\Sample\sample\node_modules\tedious\lib\errors.js:13:12)
at Connection.connectTimeout (..\SQL\Sample\sample\node_modules\tedious\lib\connection.js:1195:54)
at Timeout._onTimeout (..\SQL\Sample\sample\node_modules\tedious\lib\connection.js:1157:12)
at listOnTimeout (internal/timers.js:549:17)
at processTimers (internal/timers.js:492:7) {
message: 'Failed to connect to servername\instantname in 15000ms',
code: 'ETIMEOUT'
},
name: 'ConnectionError'
}
ConnectionError: Connection is closed.
at Request._query (..\SQL\Sample\sample\node_modules\mssql\lib\base\request.js:462:37)
at Request._query (..\SQL\Sample\sample\node_modules\mssql\lib\tedious\request.js:346:11)
at Request.query (..\SQL\Sample\sample\node_modules\mssql\lib\base\request.js:398:12)
at Immediate.<anonymous(..\SQL\Sample\sample\index.js:43:12)
at processImmediate (internal/timers.js:458:21) {
code: 'ECONNCLOSED',
name: 'ConnectionError'
}
Go to Start menu in Windows, search for "Services" and open it.
Look for "SQL Server Browser"
Right click on it and go to Properties.
Switch "Start up type" to "Automatic"
Click Ok
Right click on it again and Start the service.
It should work after that!
You might make a typo in host/port, or the server doesn't listen external interfaces (it might be configured to liten 127.0.0.1 only)
to check that the server is listening to incoming connections, run in terminal the following:
telnet <hostname> <port>
(mac/linux only)
If it says "Unable to connect to remote host: Connection refused" it means the host is there but it doesn't listen the port.
If it says "Unable to connect to remote host: Connection timed out" then the host might not be there, ot doesn't listen to the port.
You may also want to check if your firewall allows connection to that server.

WebRTC. Does server or another peer see my internal IP address?

Is it possible to get user internal IP address by using WebRTC in browser.
Here browserleaks webrtc I can see my Local IP address but this address was extracted on client side via JS script.
Here is minimal JS example how to achieve this
window.RTCPeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection; //compatibility for firefox and chrome
var pc = new RTCPeerConnection({iceServers:[]}), noop = function(){};
pc.createDataChannel(""); //create a bogus data channel
pc.createOffer(pc.setLocalDescription.bind(pc), noop); // create offer and set local description
pc.onicecandidate = function(ice){ //listen for candidate events
if(!ice || !ice.candidate || !ice.candidate.candidate) return;
var myIP = /([0-9]{1,3}(\.[0-9]{1,3}){3}|[a-f0-9]{1,4}(:[a-f0-9]{1,4}){7})/.exec(ice.candidate.candidate)[1];
document.querySelector('#output').innerHTML=myIP
//console.log('my IP: ', myIP);
pc.onicecandidate = noop;
};
<div id="output">
</div>
As we can see in code we extract ip from onicecandidate event.
If we will look at WebRTC connection flow we can see that ICE candidates exchanges between peers via signal Channel
So is this possible for STUN/TURN server to get information of internal IP address? (I doubt this is possible, but just to check)
Does specially crafted peer client able to get internal IP address of another peer during connection phase or when it exchange ICE candidates with another peer ?
The question is mostly about security concern
Your flow looks right and secure under certain assumptions:
The browser supports Trickle ICE mechanism (all modern browsers do support)
You don't wait for ice gathering to be complete before sending the SDP
You send the SDP from createOffer/createAnswer method, instead of RTCPeerConnection.localDescription.
If the browser does not support ICE trickle, it's disabled by the code or if you wait for ice gathering to be completed. The candidates will be added also to the local SDP which might still expose your internal IP.
Here is the working snippet for current chrome (v73):
const canvas = document.createElement('canvas');
const track = canvas.captureStream(1).getTracks()[0];
const pc = new RTCPeerConnection();
pc.addTrack(track);
pc.createOffer().then(offer => pc.setLocalDescription(offer))
pc.onicecandidate = () => {};
pc.onicegatheringstatechange = () => {
if(pc.iceGatheringState === 'complete') {
const sdp = pc.localDescription.sdp;
const sdpRows = sdp.split('\n')
const candidates = sdpRows.filter(row => row.indexOf('a=candidate') === 0);
console.log(candidates)
}
}

WebRTC: How do I stream Client A's video to Client B?

I am looking into WebRTC but I feel like I'm not understanding the full picture. I'm looking at this demo project in particular: https://github.com/oney/RCTWebRTCDemo/blob/master/main.js
I'm having trouble understanding how I can match 2 clients so that Client A can see Client B's video stream and vice versa.
This is in the demo:
function getLocalStream(isFront, callback) {
MediaStreamTrack.getSources(sourceInfos => {
console.log(sourceInfos);
let videoSourceId;
for (const i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if(sourceInfo.kind == "video" && sourceInfo.facing == (isFront ? "front" : "back")) {
videoSourceId = sourceInfo.id;
}
}
getUserMedia({
audio: true,
video: {
mandatory: {
minWidth: 500, // Provide your own width, height and frame rate here
minHeight: 300,
minFrameRate: 30
},
facingMode: (isFront ? "user" : "environment"),
optional: [{ sourceId: sourceInfos.id }]
}
}, function (stream) {
console.log('dddd', stream);
callback(stream);
}, logError);
});
}
and then it's used like this:
socket.on('connect', function(data) {
console.log('connect');
getLocalStream(true, function(stream) {
localStream = stream;
container.setState({selfViewSrc: stream.toURL()});
container.setState({status: 'ready', info: 'Please enter or create room ID'});
});
});
Questions:
What exactly is MediaStreamTrack.getSources doing? Is this because devices can have multiple video sources (e.g. 3 webcams)?
Doesn't getUserMedia just turn on the client's camera? In the code above isn't the client just viewing a video of himself?
I'd like to know how I can pass client A's URL of some sort to client B so that client B streams the video coming from client A. How do I do this? I'm imagining something like this:
Client A enters, joins room "abc123". Waits for another client to join
Client B enters, also joins room "abc123".
Client A is signaled that Client B has entered the room, so he makes a connection with Client B
Client A and Client B start streaming from their webcam. Client A can see Client B, and Client B can see Client A.
How would I do it using the WebRTC library (you can just assume that the backend server for room matching is created)
The process you are looking for is called JSEP (JavaScript Session Establishment Protocol) and it can be divided in the 3 steps I describe below. These steps start once both clients are in the room and can comunicate through WebSockets, I will use ws as an imaginary WebSocket API for communication between the client and the server and other clients:
1. Invite
During this step, one desinged caller creates and offer and sends it through the server to the other client (callee):
// This is only in Chrome
var pc = new webkitRTCPeerConnection({iceServers:[{url:"stun:stun.l.google.com:19302"}]}, {optional: [{RtpDataChannels: true}]});
// Someone must be chosen to be the caller
// (it can be either latest person who joins the room or the people in it)
ws.on('joined', function() {
var offer = pc.createOffer(function (offer) {
pc.setLocalDescription(offer);
ws.send('offer', offer);
});
});
// The callee receives offer and returns an answer
ws.on('offer', function (offer) {
pc.setRemoteDescription(new RTCSessionDescription(offer));
pc.createAnswer(function(answer) {
pc.setLocalDescription(answer);
ws.send('answer', answer);
}, err => console.log('error'), {});
});
// The caller receives the answer
ws.on('answer', function (answer) {
pc.setRemoteDescription(new RTCSessionDescription(answer));
});
Now both sides are have exchanged SDP packets and are ready to connect to each other.
2. Negotiation (ICE)
ICE candidates are created by each side to find a way to connect to each other, they are pretty much IP addresses where they can be found: localhost, local area network address (192.168.x.x) and external public IP Address (ISP). They are generated automatically by the PC object.
// Both processing them on each end:
ws.on('ICE', candidate => pc.addIceCandidate(new RTCIceCandidate(data)));
// Both sending them:
pc.onicecandidate = candidate => ws.send('ICE', candidate);
After the ICE negotiation, the conexion gets estabished unless you try to connect clients through firewalls on both sides of the connection, p2p communications are NAT traversal but won't work on some scenarios.
3. Data streaming
// Once the connection is established we can start to transfer video,
// audio or data
navigator.getUserMedia(function (stream) {
pc.addStream(stream);
}, err => console.log('Error getting User Media'));
It is a good option to have the stream before making the call and adding it at earlier steps, before creating the offer for the caller and right after receiving the call for the callee, so you don't have to deal with renegotiations. This was a pain a few years ago but it may be better implemented now in WebRTC.
Feel free to check my WebRTC project in GitHub where I create p2p connections in rooms for many participants, it is in GitHub and has a live demo.
MediaStreamTrack.getSources is used to get video devices connected. It seems to be deprecated now. See this stack-overflow question and documentation. Also refer MediaStreamTrack.getSources demo and code.
Yes. getUserMedia is just turning on camera. You can see the demo and code here.
Please refer to this peer connection sample & code here to stream audio and video between users.
Also look at this on Real time communication with WebRTC.

Socket.io-objc 400 (Handshake Error) Remote Server vs. localhost

When I connect to Socket.io on the server I get a 400 error, but I don't see any errors connecting to localhost. (same code and connecting via socket.io-objc)
I'm using Azure to host the node.js project.
(I also have websockets on in the azure config if that makes a difference)
ERROR: handshake failed ... The request timed out.
onError() Error Domain=SocketIOError Code=-6 "The operation couldn’t be completed.
(SocketIOError error -6.)" UserInfo=0x1874cc00 {NSUnderlyingError=0x1870cad0 "The request timed out."}
Server Code (On Azure)
var fs = require('fs');
var app = require('express')(),
server = require('http').createServer(app),
redis = require("redis"),
Primus = require('primus'),
kue = require("kue");
var haversine = require('haversine')
var finish = require("finish");
var client = redis.createClient(12276, "redis url");
client.auth('password');
var io = require('socket.io').listen(server);
io.sockets.on('connection', function (socket) {
socket.emit('join', { status: 'connected' });
});
var port = process.env.port || 1337;
server.listen(port);
SOCKET.IO-OBJC CODE
- (void) reconnect
{
[socketIO disconnectForced];
socketIO = [[SocketIO alloc] initWithDelegate:self];
socketIO.useSecure = NO;
[socketIO connectToHost:#"siteurl" onPort:80];
}