peers / peerjs

Simple peer-to-peer with WebRTC.
https://peerjs.com
MIT License
12.02k stars 1.41k forks source link

Connection drops immediately after connecting #347

Closed domschiener closed 4 years ago

domschiener commented 7 years ago

Currently using PeerJS with Meteor (here is the project: https://github.com/domschiener/peered). Everything works fine locally on the same connection, but as I deploy it to my server and try to connect with a friend, the connection is very unstable and oftentimes drops immediately after connecting.

Anyone have an idea as to why that could be?

PeerJS:  Socket open
Success! Your Peer ID is: Ajrgmd6A3KwDLnuoB
PeerJS:  Creating RTCPeerConnection.
PeerJS:  Listening for ICE candidates.
PeerJS:  Listening for `negotiationneeded`
PeerJS:  Listening for data channel
PeerJS:  Listening for remote stream
PeerJS:  Setting remote description RTCSessionDescription {type: "offer", sdp: "v=0
↵o=- 6918372162384193671 2 IN IP4 127.0.0.1
↵s…id:data
↵a=sctpmap:5000 webrtc-datachannel 1024
↵"}
Received new connection ERQFHRAHkFDRMLnJf
PeerJS:  Set remoteDescription: OFFER for: ERQFHRAHkFDRMLnJf
PeerJS:  Created answer.
PeerJS:  Set localDescription: answer for: ERQFHRAHkFDRMLnJf
PeerJS:  Received ICE candidates for: ERQFHRAHkFDRMLnJf
PeerJS:  Added ICE candidate for: ERQFHRAHkFDRMLnJf
PeerJS:  Received data channel
PeerJS:  Data channel connection success
PeerJS:  DataChannel closed for: ERQFHRAHkFDRMLnJf
PeerJS:  Cleaning up PeerConnection to ERQFHRAHkFDRMLnJf
Connection closed```
mikkelking commented 7 years ago

I have had similar problems. I am considering, well no, actually starting to fold the peerjs server into Meteor. Basically because both peerjs and meteor use sockets for communications, and I see little value in maintaining 2 socket connections. I can provide my source code if I am successful and you are interested - or did you abandon the project?

abardik commented 6 years ago

For me it was an absent sdpMid in RTCIceCandidate (negotiator.js:298), which drops a webrtc data connection between browser and nodejs imediatelly after establishment, but only if clients in different network segments or via internet (in the same local network it works without sdpMid). This is according to the official MDN spec:

https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/addIceCandidate

When an error occurs while attempting to add the ICE candidate, the Promise returned by this method is rejected, returning one of the errors below as the name attribute in the specified DOMException object passed to the rejection handler.

TypeError The specified candidate doesn't have values for both sdpMid and sdpMLineIndex.

[negotiator.js:298] /* Handle a candidate. / Negotiator.handleCandidate = function(connection, ice) { var candidate = ice.candidate; var sdpMid = ice.sdpMid; // add sdpMid which is absent in original peerjs var sdpMLineIndex = ice.sdpMLineIndex; connection.pc.addIceCandidate(new RTCIceCandidate({ sdpMid: sdpMid, // add sdpMid which is absent in original peerjs sdpMLineIndex: sdpMLineIndex, candidate: candidate })); util.log('Added ICE candidate for:', connection.peer); }

RamyaAshika commented 5 years ago

What is the solution for this? I'm also using peerjs @abardik @domschiener @mikkelking

abardik commented 5 years ago

In lib/negotiator.js add to lines as shown above with comment // add sdpMid which is absent in original peerjs

RamyaAshika commented 5 years ago

OOH ok fine but I'm using this library https://cdnjs.cloudflare.com/ajax/libs/peerjs/0.3.18/peer.min.js so what I need to do now? @abardik

abardik commented 5 years ago

Well, I forked from 0.3.13 two years ago and changed a lot. But you can fix this by yourself in new version as well. You have to modify the source code, so:

  1. Grab the non-minified version from here: https://cdnjs.cloudflare.com/ajax/libs/peerjs/0.3.18/peer.js
  2. Go to the line 722 (Negotiator.handleCandidate)
  3. Add two lines shown in previous comment or change the whole function to the following:
    Negotiator.handleCandidate = function(connection, ice) {
    var candidate = ice.candidate;
    var sdpMid = ice.sdpMid; // !!! THIS LINE
    var sdpMLineIndex = ice.sdpMLineIndex;
    connection.pc.addIceCandidate(
    new RTCIceCandidate({
      sdpMid: sdpMid, // !!! AND THIS LINE
      sdpMLineIndex: sdpMLineIndex,
      candidate: candidate
    })
    );
    util.log("Added ICE candidate for:", connection.peer);
    };
  4. In your project include your modified library instead of from CDN.

Good luck!

RamyaAshika commented 5 years ago

Let me try, Many many thanks

On Wed, Nov 28, 2018 at 12:31 PM Andy Bard notifications@github.com wrote:

Well, I forked from 0.3.13 two years ago and changed a lot. But you can fix this by yourself in new version as well. You have to modify the source code, so:

  1. Grab the non-minified version from here: https://cdnjs.cloudflare.com/ajax/libs/peerjs/0.3.18/peer.js
  2. Go to the line 722 (Negotiator.handleCandidate)
  3. Add two lines shown in previous comment or change the whole function to the following:

Negotiator.handleCandidate = function(connection, ice) { var candidate = ice.candidate; var sdpMid = ice.sdpMid; // !!! THIS LINE var sdpMLineIndex = ice.sdpMLineIndex; connection.pc.addIceCandidate( new RTCIceCandidate({ sdpMid: sdpMid, // !!! AND THIS LINE sdpMLineIndex: sdpMLineIndex, candidate: candidate }) ); util.log("Added ICE candidate for:", connection.peer); };

  1. In your project include your modified library instead of from CDN.

Good luck!

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/peers/peerjs/issues/347#issuecomment-442341314, or mute the thread https://github.com/notifications/unsubscribe-auth/AkxCQgtIvZDVrFLfdI-Q2l7conFJ00ldks5uzjTdgaJpZM4IkV8R .

RamyaAshika commented 5 years ago

I keep on getting this error now @abardik ICE failed, add a TURN server and see about:webrtc for more details

RamyaAshika commented 5 years ago

TypeError: connection.pc is undefined[Learn More] this error also @abardik

abardik commented 5 years ago

TURN is required if your peers cannot establish a direct connection. For example, this is possible, if one of them behind the NAT. I have to use TURN often when one of the peers is mobile client (LTE). So, try to install your own TURN or find a public one (which is hard to find). Actually, did you configured ICE servers settings? Maybe the problem is just ICE (STUN), not TURN. STUN is required for any peers that not in your network. So, if you did not configure STUN (ICE) settings, your remote peers will be unavailable.

abardik commented 5 years ago

You can use Google's STUN:

var config = { 'iceServers': [{ 'urls': ['stun:stun.l.google.com:19302'] }] };

and then:

var peer = new Peer(id, config);

RamyaAshika commented 5 years ago

After googling a lot I found these things only but I don't think so this is also working. peer = new Peer(id,{ debug: 2 config: {'iceServers': [ { url: 'stun:stun.l.google.com:19302' }, // Pass in optional STUN and TURN server for maximum network compatibility { url: 'turn:numb.viagenie.ca:3478', credential: 'muazkh', username:'webrtc@live.com' }, { url: 'turn:numb.viagenie.ca', credential: 'muazkh', username:'webrtc@live.com' }, { url: 'turn:numb.viagenie.ca:3478', credential: 'peerjsdemo', username:'p.srikanta@gmail.com' }, { url: 'turn:192.158.29.39:3478?transport=udp', credential: 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
username:'28224511:1379330808' } { url: 'turn:192.158.29.39:3478?transport=tcp', credential: 'JZEOEt2V3Qb0y27GRntt2u2PAYA=', --> username:'28224511:1379330808' } ]} });

and yes I'm trying to connect from the Mobile(LTE) and how to install TURN server whether its a paid one? Please let me know the possible solution. I'm struggling from last week. @abardik

abardik commented 5 years ago

About TypeError... I need more info from your browser console (at least file and line of the error, or trace or something). Try to include the original 0.3.18 (not minified) and then just add those two lines.

RamyaAshika commented 5 years ago

Sure I will let you know @abardik

abardik commented 5 years ago

I don't know about those TURNs in your example (it seems like old test servers from peerjs developers), but STUN is good. Remove all TURNs and try again. You can try my example above with just one Google's server ('urls' is a new standard instead of old 'url' from your example). P.S. From my experience, iPhone LTE requires TURN anyway. At least, with my mobile carrier. So, if you connect from iPhone, try to install your own TURN.

RamyaAshika commented 5 years ago

/* Handle a candidate. / Negotiator.handleCandidate = function (connection, ice) { var candidate = ice.candidate; var sdpMid = ice.sdpMid; // !!! THIS LINE var sdpMLineIndex = ice.sdpMLineIndex; connection.pc.addIceCandidate(new RTCIceCandidate({ //TypeError: connection.pc is undefined sdpMid: sdpMid, // !!! AND THIS LINE sdpMLineIndex: sdpMLineIndex
, candidate: candidate })); util.log("Added ICE candidate for:", connection.peer); }; module.exports = Negotiator;

connection.pc.addIceCandidate(new RTCIceCandidate({ //TypeError: connection.pc is undefined

This is the line I'm getting that error

and after adding that Google STUN server,this is what I got ICE failed, add a TURN server and see about:webrtc for more details screenshot 680 @abardik

RamyaAshika commented 5 years ago

and now this is my code

 var config = { 'iceServers': 
    [{ 'urls': ['stun:stun.l.google.com:19302'] 
    }] 
    };
 peer = new Peer(id, config);
abardik commented 5 years ago

Config is good, but the browser says you need TURN, because ICE failed. This is the same behavior I have with iPhone. If any of those TURNs from your example did not work, you should to install your own TURN or maybe try to create account here: http://numb.viagenie.ca/ (I never tried them). You have to create that account because their TURN requires an authentication, and you should put your login/password to your config (like in your example config above).

abardik commented 5 years ago

For TypeError. I see that error in line 650 (peer.js). But line 650 (at least in my copy of peer.js that I just downloaded) contains: util.log("Failed to createOffer, ", err); As you see, there is no connection.pc in this line. Maybe you use the wrong peer.js? Anyway, try to use the original peer.js after you install TURN. When the TURN problem is gone - come back to sdpMid problem.

RamyaAshika commented 5 years ago

Many many thanks abardik.Let me try and I will let you know @abardik

RamyaAshika commented 5 years ago

After creating an account, this is how I configured in my code. Whether it's correct or not? var config = { 'iceServers': [{ 'urls':'stun:stun.l.google.com:19302'}, { 'urls': 'turn:numb.viagenie.ca', credential: 'mypassword', username:'myusername' } ] }; @abardik

kidandcat commented 5 years ago

Just for the code modification, you can clone the repository, make the changes, and compile, you only need to launch grunt (don't need to mess with minified code). And ofc any PR is welcome.

abardik commented 5 years ago

By spec it should be:

{
    "iceServers": [
        { "urls": ["stun:stun.l.google.com:19302"] },
        { "urls": ["turn:numb.viagenie.ca:3478"], "username": "###", "credential": "###" }
    ]
}
RamyaAshika commented 5 years ago

I tried but often I'm getting the same issue if I I tried to connect 5times,in that only 3 times works. and media streams are getting strucked its not at all moving more than 2 minutes - 3minutes @abardik
screenshot 684

abardik commented 5 years ago

If you successfully established LTE connection and transfered some data and it has been live for several minutes, than I believe the reason of your initial issue is gone. Now, I think you have another problem - unstable connection. It can be anything. For example, bad LTE signal or bug in your code. You should try to write as simple test as possible to exclude your bugs. But from the other side, your serverreflexive candidate is failed on your screenshot. Without srvflx candidate you can not rich remote peer if your device does not have a public IP (it's true for many home routers and LTE devices). If you have established LTE connection, than: a) you have a public IP and your connection is established with host candidate; or b) your screenshot is outdated because it's not possible to connect to remote peer without public IP and with failed srvflx (STUN) and/or relay (TURN) candidates. Anyway, it's hard to say something more usefull without being on your computer, sorry.

RamyaAshika commented 5 years ago

Can I share my code? @abardik

abardik commented 5 years ago

Sure.

RamyaAshika commented 5 years ago

Here I have attached my file as a txt. Please change it as HTML file. Sender file is the one who is creating connection and receiver is the one who is receiving the connetion. Sender.txt receiver.txt

and here after getting the stream in video tag,I'm drawing it into the canvas using drawImage. Please let me know and to make the connection url it will be look like http://ipaddress:8080/Sender.html and for receiver it should be like, http://ipaddress:8080/Receiver.html?roomId= //In this only we will pass our id as params.For eg,roomId=xxxxxx @abardik

`

abardik commented 5 years ago

Sorry, I don't have https server to upload and try your code. It's not possible to establish a non-secured (http) connection for webrtc (only localhost, but you need LTE). I suggest to start from very simple webrtc/peerjs example to avoid your own bugs. Then, when you successfully establish LTE connection in that simple example, you can start to add your own logic.

RamyaAshika commented 5 years ago

But it works great in Firefox and one more doubt I'm having in firefox documentation,they said webrtc won't work in safari. Whether it will work on safari or not? @abardik

abardik commented 5 years ago

It will. MediaConnection will work as is. But for DataConnection you have to make a little fix to send binary data (this is not required if you plan to send strings only). To do this in DataConnection.prototype._trySend find this._dc.send and replace it with the following:

if ( ios ) this._dc.send(new Uint8Array(msg));
else this._dc.send(msg);

Or you can handle this in your main code where you calling DataConnection.send(). Maybe it has been fixed already, but in v. 0.3.13 this bug exists.

abardik commented 5 years ago

And you have to define ios somewhere, of course.

RamyaAshika commented 5 years ago

Ok, I will ask this when I will work on that issue. Right now Let me try to get the connection clearly @abardik

Bobisback commented 5 years ago

@abardik Is there any other bug fixes needed to get peerjs to work on ios?

I am having an issue where only on iOS, if a peer connects to iOS it does not work. But if iOS connects to anything else it works fine. Any ideas?

abardik commented 5 years ago

Just those two: sdpMid and Uint8Array. But in my case iOS doesn't work without TURN. Maybe it's because of my LTE provider. Wi-Fi is ok without TURN, as I remember. Didn't try on Android with LTE. Maybe it's the same.

Bobisback commented 5 years ago

Well I was just planning to only send strings, and my application will be wifi only. So technically it should work then?

Maybe I am just missing something. In my case it says it is connected and I get the connection event being fired off of the peer object that gives a DataConnection object, but then any interaction I have with the DataConnection object, does not work. No console logs, no errors, just nothing at all. Not sure what is going on. Super annoying though.

Bobisback commented 5 years ago

Can you confirm that you got peerjs to work correctly on iOS 11 in a MKWebView application? I am guessing that Meteor is some sort of cross platform web based framework like cordova?

There is tons of things saying that iOS 11 is not supported for webRTC, based on my research this is not entirely true, and in fact it works fine but you cannot get the data stream through the browser. Since we can get the data stream natively I am trying to find out if WebRTC and peerJS actually do work in iOS and Android minus the data stream issue in iOS 11. Any thoughts?

abardik commented 5 years ago

Yes, it works totally correctly on iOS, starting from iOS 9 or 10 when WebRTC has been announced by Apple (pure Safari and Phonegap app with WKWebView and iosrtc plugin, because WKWebView for Cordova still doesn't support WebRTC). No issues with DataConnection and MediaConnection. It's hard to deal with iosrtc plugin, but it works finally. Android the same - everything works, starting from Android 5.

abardik commented 5 years ago

Meteor is not lika Cordova. Meteor it's just a full-stack web framework. Cordova is a mobile framework. Cordova gives you an access to device's features. Meteor just gives you an exellent way to deal with data and UI.

Bobisback commented 5 years ago

Ok so you never actually created a native mobile app then? Meteor just allows you to code a website for mobile, but it is using prue safari not WKWebView. Based no ym research WKWebView is the issue.

Bobisback commented 5 years ago

@abardik So how do you define iOS or not in the Uint8Array bug?

Also does this refer to the person you are sending it to is on iOS or does it mean that client who is sending it is on iOS?

abardik commented 5 years ago

Something like this:

var isCordovaIOS = /Apple/.test(navigator.vendor) && /YourAppUA/.test(navigator.userAgent);
var isSafari = /Apple/.test(navigator.vendor) && /Safari/.test(navigator.userAgent) && ! isCordovaIOS;
abardik commented 5 years ago

Phonegap (Cordova) is a native app. You just use JS to code instead of Objective C or Java. Meteor is a web framework. You can not build a native app with meteor only. You have to wrap meteor app into cordova app. Then you have WKWebView in your cordova app, in which meteor web app is running. But from the WebRTC point of view it doesn't meter what web framework you're using. You have cordova implementation of WKWebView, which is on iOS has no WebRTC yet. So, you have to add iosrtc plugin in your cordova app. For Android app WebRTC just works, as well as Safari on your iPhone. But for iOS cordova app you need iosrtc plugin.

abardik commented 5 years ago

And yes, there are no issues with WebRTC and peerjs on all of those platforms. Only restrictions I have with WebRTC are in IE and Edge. Chrome, Firefox, Safari, UIWebView, WKWebView - all of them just work.

Bobisback commented 5 years ago

@abardik Cool that is good to here. Thanks for the input. At this point I just need to figure out how to piece all this together.

Right now I am doing a test with data connection and i am having issues with it in both safari and iOS MKWebView. Maybe you can help me out with it?

When setting breakpoints it would appear the issue is related to RTCPeerConnection and the event onicecandidate. With full logging, getting no errors, this only happens when another device tries to connect to safari or iOS. So the device connection works correctly and even fires the open connection event, but when i send stuff nothing happens. On iOS and Safari for some reason the onicecandidate never fires which seems to be causing the issues.

I have implemented both of your fixes and still having issues. Any ideas?

abardik commented 5 years ago

Uint8Array is for already established connection and just for binary data transfer. For strings only you don't need it. And, actually, I see now, that I've commented sdpMid fix almost a year ago. It works now without that fix for me. Sorry for that. Comment it and try again. Your problem is ICE. If you have no onicecandidate, you have no peer to connect to. I had the same issue on iOS on LTE without TURN. But, I think, this is just my provider's LTE network configuration. If your provider provides a direct IP for LTE, you don't need TURN. If both of your peers are in the same wi-fi network, you even dont' need STUN, just host candidates. But i'm not sure WebRTC will work on iOS without STUN config (if I remember, it didn't work for me without google STUN in peerjs config, even in the same wi-fi). So, for the first, make sure you've configured STUN (see comments above) and connect both peers to the same wi-fi network. Then look at the 'host' and 'srvflx' candidates in webrtc logs. Then configure TURN, connect through LTE and look for 'relay' candidates. 'srvflx' and/or 'relay' are required for LTE connections.

RamyaAshika commented 5 years ago

I have tried with COTURN configuration with my local system using my local IP address. It worked. But now, I'm trying to configure this using my public IP. Is it possible? and I'm doing this using Cygwin, can I able to configure this in my system with my public IP address? @abardik

abardik commented 5 years ago

Theoretically, yes. But I never configured TURN locally, so, practically, I don't know.

Bobisback commented 5 years ago

Hey @abardik,

Thanks a ton for the great input on this subject, you are a huge help. Currently I am only sending strings, so guessing the fix does not matter. Also I have tried with and without the stun servers, I basically copied the above suggestions to the letter. There is no change on behavior for my app. The requirement for this app is it only needs to work over wifi, nothing else. So I do not have to worry about turn servers.

You mention if you have no onicecandidate then you have no peer, but here is the log of the bnroken one.

[Log] PeerJS:  – "Socket open" (vendor.js, line 88282)
[Log] GET "http://192.168.102.146:8080/api/connected-users/" (main.js, line 2166)
             succeeded in 33 ms, with body: ["ax8p10z501g00000 (You)"]
[Log] PeerJS:  – "Creating RTCPeerConnection." (vendor.js, line 88282)
[Log] PeerJS:  – "Listening for ICE candidates." (vendor.js, line 88282)
[Log] PeerJS:  – "Listening for data channel" (vendor.js, line 88282)
[Log] PeerJS:  – "Listening for remote stream" (vendor.js, line 88282)
[Log] PeerJS:  – "Setting remote description" – RTCSessionDescription {type: "offer", sdp: "v=0
↵o=- 817605983474189040 2 IN IP4 127.0.0.1
↵s=…id:data
↵a=sctpmap:5000 webrtc-datachannel 1024
↵", toJSON: function} (vendor.js, line 88282)
RTCSessionDescription {type: "offer", sdp: "v=0
↵o=- 817605983474189040 2 IN IP4 127.0.0.1
↵s=…id:data
↵a=sctpmap:5000 webrtc-datachannel 1024
↵", toJSON: function}RTCSessionDescription
[Log] Got connection event in page: fmri5l4xjz000000 (main.js, line 2166)
[Log] Data connection opened. (main.js, line 2166)
[Log] Data connection setup with fmri5l4xjz000000 (main.js, line 2166)
[Log] PeerJS:  – "Added ICE candidate for:" – "fmri5l4xjz000000" (vendor.js, line 88282)
[Log] PeerJS:  – "Set remoteDescription:" – "OFFER" – "for:" – "fmri5l4xjz000000" (vendor.js, line 88282)
[Log] PeerJS:  – "Added ICE candidate for:" – "fmri5l4xjz000000" (vendor.js, line 88282)
[Log] PeerJS:  – "Created answer." (vendor.js, line 88282)
[Log] PeerJS:  – "Set localDescription: answer" – "for:" – "fmri5l4xjz000000" (vendor.js, line 88282)
[Log] PeerJS:  – "Added ICE candidate for:" – "fmri5l4xjz000000" (vendor.js, line 88282)

Now here is the log for the one that works.

logging.service.ts:11 Connected to server as 7tvukmruwhq00000
logging.service.ts:11 GET "http://192.168.102.146:8080/api/connected-users/"
             succeeded in 29 ms, with body: ["ax8p10z501g00000","tvjfqg34iuo00000","7tvukmruwhq00000 (You)"]
util.js:65 PeerJS:  Creating RTCPeerConnection.
util.js:65 PeerJS:  Listening for ICE candidates.
util.js:65 PeerJS:  Listening for data channel
util.js:65 PeerJS:  Listening for remote stream
util.js:65 PeerJS:  Setting remote description RTCSessionDescription {type: "offer", sdp: "v=0
↵o=mozilla...THIS_IS_SDPARTA-63.0.3 1691102812…a=sctp-port:5000
↵a=max-message-size:1073741823
↵"}
logging.service.ts:11 Got connection event in page: tvjfqg34iuo00000
logging.service.ts:11 Data connection opened.
logging.service.ts:11 Data connection setup with tvjfqg34iuo00000
3util.js:65 PeerJS:  Added ICE candidate for: tvjfqg34iuo00000
util.js:65 PeerJS:  Set remoteDescription: OFFER for: tvjfqg34iuo00000
util.js:65 PeerJS:  Created answer.
util.js:65 PeerJS:  Set localDescription: answer for: tvjfqg34iuo00000
2util.js:65 PeerJS:  Received ICE candidates for: tvjfqg34iuo00000
util.js:65 PeerJS:  Received data channel
util.js:65 PeerJS:  Data channel connection success
logging.service.ts:11 Data connection open according to peerjs.

As you can see, it is getting the handshake, just after it creates the offer nothing happens, but on the one that works it gets a Received ICE candidates message. Also sometimes if I wait long enough I get a this on the client that is broken. The client that is connecting to the broken ones appears the same as a normal one, aka it connects and a open event shows.

[Log] PeerJS:  – "iceConnectionState is disconnected, closing connections to fmri5l4xjz000000" (vendor.js, line 88282)
[Log] Error on Receive listening: There was an error Error: Negotiation of connection to fmri5l4xjz000000 failed. (pages-dashboard-dashboard-module.js, line 238)

Any thoughts?

Thanks, Bob

abardik commented 5 years ago

I have to see your candidates to say something. If you have just one candidate 127.0.0.1 (localhost), no other peer can connect to you, except others on the same device. Go to Negotiator.handleCandidate and change the last line to this: util.log('Added ICE candidate for:', connection.peer, candidate);