I have 2 devices (desktop PCs), each running a browser tab that instantiates an IPFS node using js-ipfs.
//file index.html, served over HTTPS
const node = IpfsCore.create(); //ipfs browser node
Both nodes have peers (calling node.swarm.addrs() returns about 50 peers). They do not list each other as peers.
I want to connect those two nodes to each other, so if I call node.add( ... ) on the first, I can then call node.cat( ... ) on the second, and acquire a file from the first. (Or so that browser 1's pubsub broadcasts always reach browser 2; browser 1 can read browser 2's wantlist etc.)
How do I connect these 2 browser tabs as peers?
The example at https://github.com/ipfs/js-ipfs/blob/master/packages/interface-ipfs-core/src/swarm/connect.js uses this command:
ipfsA.swarm.connect( ipfsBId.addresses[0] )
But in my case, both my browser-tabs have no addresses.
console.log( ( await node.id() ).addresses ); //[] empty array
I don't know how the browser tabs manage connecting to other peers without their own addresses, and I don't know how to make them connect to each other.
There is a 4-year-old question about browser peers here IPFS - pubsub connect to peers from browser, but its fairly unrelated and seems to rely on the the deprecated / out-of-date webrtc-star https://github.com/libp2p/js-libp2p-webrtc-star
I know if I were setting up a WebRTC connection I would use fetch() or XHR or a websocket to a public-facing server (with a DNS record or IP address) to exchange negotiation info while querying a list of iceServers (also with DNS records / IP addresses).
I don't want to rely on a list of servers I own or configure, and I don't want to burden any public example TURN servers or anything. js-libp2p might use multicastDNS? I don't think browser tabs can broadcast signals though (I could be wrong? Maybe fetch() can do that somehow with some sneaky url stuff?)
What do I do? How can these two browser-tab IPFS peers discover each other, specifically?
I suspect this has a very straight-forward answer, but I have been researching for days now and read hundreds of pages, and none of the documentation I have read anywhere is relevant. Wherever the answer is on this vast Internet, I have not been able to find it.
Related
So ive been looking for a way to integrate webRTC into a site im making, but i want to do it on shared hosting. I came across this repo on GitHub by nielsbaloe and it has been a huge help in getting a basic connection.
This is the code i believe is responsible for adding the peer: ( index.html in the repo, line )
function icecandidate(localStream) {
pc = new RTCPeerConnection(configuration);
pc.onicecandidate = function (event) {
if (event.candidate) {
publish('client-candidate', event.candidate);
}
};
try {
pc.addStream(localStream);
}catch(e){
var tracks = localStream.getTracks();
for(var i=0;i<tracks.length;i++){
pc.addTrack(tracks[i], localStream);
}
}
pc.ontrack = function (e) {
document.getElementById('remoteVideo').style.display="block";
document.getElementById('localVideo').style.display="none";
remoteVideo.srcObject = e.streams[0];
};
}
Now the struggle im facing is adding room functionality, and maybe the ability to have more than two concurrent peers present at the same time. I did some experimenting, but in vain. I know that for room functionality id have to tinker around in the php, so at least id like to figure out how to make more than 1 peer possible.
As far as I know, there is no way to re-use the same RTCPeerConnection for multiple peers, so you'll have to do the same thing as 1-on-1 but between every single peerĀ in a group.
As far as signalling, it's pretty simple, goes kind of like this:
Client A -> [Offer] -> Server -> [Offer] -> Client B -> [Answer] -> Server -> [Answer] -> Client A
A nicer explanation at MDN
There isn't necessarily a need for NodeJS or WebSocket. The reason most people go for it it is because the last link in this chain (Server -> Client A) requires server-initiated connection. But that can be substituted with alternative techniques such as (long-)polling. Or, in case of PHP, you might use websocket implementations such as Bloatless or Aerys
To implement the room functionality, you'll have to implement the following:
Variant A (using polling):
An endpoint to throw offers at, e.g. POST /rooms/{id}
An endpoint to regularly check for new offers from, e.g. GET /rooms/{id}
Variant B (with websockets)
Create a broadcast rooms, for example, by dynamically creating HTTP endpoints and websocket server instances. Or by having a single websocket instance but sending whatever room you're intending to join right inside after establishing a connection. From there, it's only a matter of sending correct offers and answers to correct users.
In both cases, you might want to either create multiple offers in advance to pool from the server, or to dynamically create new ones, but, most importantly, make sure you're not connecting the same peers twice, otherwise you will end up with a loop. To prevent it, just provide each user with a randomly generated string to identify themself and send it among offers.
There are turnkey solutions available if you don't want to go this route, but be careful and check whether you can use your own TURN servers with them. A common trend I have noticed is that there are a lot of WebRTC solution providers out there that lure you with their simplicity but then lock you in with their own TURN servers for which you might have to pay a quite hefty bill later on.
Is it possible to establish a WebRTC connection between two browsers on a local area network without calling createOffer/Answer and instead by manually creating local and remote descriptions?
The browsers are not behind NAT with respect to each other and they have signaled their IP addresses somehow (say via a local HTTP server).
Would it be possible to do something in the spirit of:
const myIp = '192.168.0.1';
const peerIp = '192.168.0.2';
const c = new RTCPeerConnection();
c.setLocalDescription(MAGIC_createLocalDescriptionFor(myIp));
c.setRemoteDescription(MAGIC_createRemoteDescriptionFor(peerIp));
Yes! If you are using Chrome. Check out offline-browser-communication
You have three points of state you need to deal with.
IP/Port. You can setup your network in a way that this is stable. Or attempt to do some guessing?
ufrag/pwd. You can set this via SetLocalDescription so you can control these.
DTLS Certificate. Use GenerateCertificate this means you will only have to signal it once.
Not in the browser. The offer and answer contain properties such as ice-ufrag, ice-pwd, the DTLS fingerprints and the candidate ports etc that are not static.
I have two peers that want to connect to each other via WebRTC. Typically the first peer would create an offer and send it to the second via a signalling channel/server, the second peer would respond with an answer. This scenario works fine.
However, is it possible to support the case where both peers happen to try to connect to each other simultaneously both sending SDP offers to one another concurrently via the signalling server.
// Both peers do this simultaneously:
const conn = new RTCPeerConnection(null);
const sdpOffer = await conn.createOffer();
await conn.setLocalDescription(sdpOffer);
signalingService.send(peerId, sdpOffer);
// At some point in the future both peers also receive an SDP offer
// (rather than answer) from the other peer whom they sent an offer to
// via the signaling service. If this was an answer we'd call
// RTCPeerConnection.setRemoteDescription, however this doesn't work for an
// offer:
conn.setRemoteDescription(peerSDPOffer);
// In Chrome results in "DOMException: Failed to execute 'setRemoteDescription' on 'RTCPeerConnection': Failed to set remote offer sdp: Called in wrong state: kHaveLocalOffer"
I even tried to "convert" the received peer offers into answers by rewriting the SDP type from offer to answer and setup:actpass to setup:active but that doesn't seem to work, instead I just get a new exception.
So the question is, is this simultaneous connect/offer use case supported in some fashion - or should I close one side/peer RTCPeerConnection & instantiate a new one using RTCPeerConnection.createAnswer this time?
This situation is known as "signaling glare". The WebRTC API does not really define how to handle this (except for something called "rollback" but it is not implemented in any browser yet and nobody has missed it so far) so you have to avoid this situation yourself.
Simply replacing the a=setup won't work since the underlying DTLS mechanism still needs the concept of a client and a server.
The answer for how to avoid glare these days is to use the Perfect Negotiation Pattern: https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Perfect_negotiation
However, what OP described does work with the slight modification of setting setup:active on one peer and setup:passive on the other:
https://codepen.io/evan-brass/pen/mdpgWGG?editors=0010
It might not work for audio / video connections (because those may require negotiating codecs), but I've tested it on Chrome / Firefox / Safari for DataChannel only connections.
You could choose which peer is active and which is passive using whatever system you use to determine 'politeness' in perfect negotiation. One possibility would be to compare the DTLS fingerprints and make whichever one is larger the active peer.
I have a simple socket.io client and server program, running on node.js. The client and server exchange messages between them for a few minutes, before disconnecting (like chat).
If there any function/method I can use to get the total bytes transferred (read/write), after the socket is closed?
At present I am adding up the message size for each each message sent and received by the client. But, as per my understanding, in socket.io depending on which protocol is used (websocket, xhr-polling, etc.), the size of the final payload being sent will differ due to the header/wrapper size. Hence, just adding message bytes won't give me an accurate measure of bytes transferred.
I can use monitoring tools like Wireshark to get this value, but I would prefer using a javascript utility to get this value. Search online, didn't give me any reasonable answer.
For pure websocket connections, I am being able to get this value using the functions: socket._socket.bytesRead and socket._socket.bytesWritten
Any help is appreciated!
As of socket v2.2.0 i managed to get byte data like this. Only problem these are specified when client closes browser window and reason parameter is transport error. If client uses socket.close() or .disconnect() or server uses .disconnect() then bytes are 0.
socket.on('disconnect', (reason) => {
let symbs = Object.getOwnPropertySymbols(socket.conn.transport.socket._socket);
let bytesRead = socket.conn.transport.socket._socket[symbs[3]];
let bytesWritten = socket.conn.transport.socket._socket[symbs[4]];
});
If you wanted such a feature that would work no matter what the underlying transport was below a socket.io connection, then this would have to be a fundamental feature of socket.io because only it knows the details of what it's doing with each transport and protocol.
But, socket.io does not have this feature built in for the various transports that it could use. I would conclude that if you're going to use the socket.io interface to abstract out the specific protocol and implementation on top of that protocol, then you give up the ability to know exactly how many bytes socket.io chooses to use in order to implement the connection on its chosen transport.
There are likely debug APIs (probably only available to browser extensions, not standard web pages) that can give you access to some of the page-wide info you see in the Chrome debugger so that might be an option to investigate. See the info for chrome.devtools.network if you want more info.
I'm making a multiplayer (2 player) browser game in JavaScript. Every move a player makes will be sent to a server and validated before being transmitted to the opponent. Since WebSockets isn't ready for prime time yet, I'm looking at long polling as a method of transmitting the data and node.js looks quite interesting! I've gone through some example code (chat examples, standard long polling examples and suchlike) but all the examples I've seen seem to broadcast everything to every client, something I'm hoping to avoid. For general server messages this is fine but I want two players to be able to square off in a lobby or so and go into "private messaging" mode.
So I'm wondering if there's a way to implement private messaging between two clients using nodejs as a validating bridge? Something like this:
ClientA->nodejs: REQUEST
nodejs: VALIDATE REQUEST
nodejs->ClientA: VALID
nodejs->ClientB: VALID REQUEST FROM ClientA
You need some way to keep track of which clients are in a lobby together. You can do this with a simple global array like so process.lobby[1] = Array(ClientASocket, ClientBSocket) or something similar (possibly with some additional data, like nicknames and such), where the ClientXSocket is the socket object of each client that connects.
Now you can hook the lobby id (1 in this case) onto each client's socket object. A sort of session variable (without the hassle of session ids) if you will.
// i just made a hashtable to put all the data in,
// so that we don't clutter up the socket object too much.
socket.sessionData['lobby'] = 1;
What this allows you to do also, is add an event hook in the socket object, so that when the client disconnects, the socket can remove itself from the lobby array immediately, and message the remaining clients that this client has disconnected.
// see link in paragraph above for removeByValue
socket.on('close', function(err) {
process.lobby[socket.sessionData['lobby']].removeByValue(socket);
// then notify lobby that this client has disconnected.
});
I've used socket in place of the net.Stream or request.connection or whatever the thing is.
Remember in HTTP if you don't have keep-alive connections, this will make the TCP connection close, and so of course make the client unable to remain within a lobby. If you're using a plain TCP connection without HTTP on top (say within a Flash application or WebSockets), then you should be able to keep it open without having to worry about keep-alive. There are other ways to solve this problem than what I've shown here, but I hope I got you started at least. The key is keeping a persistent object for each client.
Disclaimer: I'm not a Node.js expert (I haven't even gotten around to installing it yet) but I have been reading up on it and I'm very familiar with browser js, so I'm hoping this is helpful somehow.