From 0f3351ee16bc8b5f9d69e09551e73768d4a15d2e Mon Sep 17 00:00:00 2001 From: Moritz Langenstein <ml5717@ic.ac.uk> Date: Tue, 29 Oct 2019 23:53:50 +0000 Subject: [PATCH] (ml5717) Added cross-platform heartbeat and reconnection backoff --- package-lock.json | 6 +-- src/liowebrtc | 2 +- src/room.js | 16 ++++++- src/y-webrtc/index.js | 102 +++++++++++++++++++++++++++++++++--------- 4 files changed, 100 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7bde0ea..7401d2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6870,9 +6870,9 @@ } }, "source-map-support": { - "version": "0.5.15", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.15.tgz", - "integrity": "sha512-wYF5aX1J0+V51BDT3Om7uXNn0ct2FWiV4bvwiGVefxkm+1S1o5jsecE5lb2U28DDblzxzxeIDbTVpXHI9D/9hA==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", "dev": true, "requires": { "buffer-from": "^1.0.0", diff --git a/src/liowebrtc b/src/liowebrtc index 7e94603..ce4a2eb 160000 --- a/src/liowebrtc +++ b/src/liowebrtc @@ -1 +1 @@ -Subproject commit 7e94603dca2fbffe844fe5ee4f3361e9c361e7cc +Subproject commit ce4a2ebe160804ed84f7b6fc3bd10c91e766bdcd diff --git a/src/room.js b/src/room.js index ae28294..e92d017 100644 --- a/src/room.js +++ b/src/room.js @@ -81,8 +81,22 @@ class Room extends EventTarget { name: "webrtc", url: "/", room: this.name, + handshake: { + initial: 100, + interval: 500, + }, + heartbeat: { + interval: 500, + minimum: 1000, + timeout: 10000, + }, onUserEvent: (event) => { - if (event.action == "userID") { + if (event.action == "userConnection") { + const { quality } = event + this.dispatchEvent( + new CustomEvent("userConnection", { detail: quality }), + ) + } else if (event.action == "userID") { const { id } = event this.ownID = id this.dispatchEvent(new CustomEvent("allocateOwnID", { detail: id })) diff --git a/src/y-webrtc/index.js b/src/y-webrtc/index.js index dc2f730..97ff153 100644 --- a/src/y-webrtc/index.js +++ b/src/y-webrtc/index.js @@ -14,6 +14,20 @@ function extend(Y) { super(y, options) this.webrtcOptions = options + this.webrtcOptions.handshake = this.webrtcOptions.handshake || {} + this.webrtcOptions.handshake.initial = + this.webrtcOptions.handshake.initial || 100 + this.webrtcOptions.handshake.interval = + this.webrtcOptions.handshake.interval || 500 + + this.webrtcOptions.heartbeat = this.webrtcOptions.heartbeat || {} + this.webrtcOptions.heartbeat.interval = + this.webrtcOptions.heartbeat.interval || 500 + this.webrtcOptions.heartbeat.minimum = + this.webrtcOptions.heartbeat.minimum || 1000 + this.webrtcOptions.heartbeat.timeout = + this.webrtcOptions.heartbeat.timeout || 10000 + this.queue = new Worker("js/queue.js") this.queue.onmessage = (event) => { const method = event.data.method @@ -96,11 +110,13 @@ function extend(Y) { console.log("TODO: LEFT ROOM") }) - this.webrtc.on("channelError", (a, b, c, d) => console.log(a, b, c, d)) + this.webrtc.on("channelError", (a, b, c, d) => + console.log("TODO: CHANNEL ERROR", a, b, c, d), + ) this.webrtc.on("channelOpen", (dataChannel, peer) => { this.checkAndEnsureUser() - console.log(dataChannel) + // Start a handshake to ensure both sides are able to use the channel function handshake(peer) { const _peer = this.webrtc.getPeerById(peer.id) @@ -121,10 +137,16 @@ function extend(Y) { message: "tw", }) - setTimeout(handshake.bind(this, peer), 500) + setTimeout( + handshake.bind(this, peer), + this.webrtcOptions.handshake.interval, + ) } - setTimeout(handshake.bind(this, peer), 100) + setTimeout( + handshake.bind(this, peer), + this.webrtcOptions.handshake.initial, + ) }) this.webrtc.on("receivedPeerData", (type, message, peer) => { @@ -166,17 +188,14 @@ function extend(Y) { return } - console.log( - this.webrtc - .getPeerById(uid) - .getStats(null) - .then((stats) => stats.forEach((report) => console.log(report))), - ) - - const health = {} + const health = { + lastStatsResolved: true, + lastReceivedBytes: 0, + lastReceivedTimestamp: Date.now(), + } health.cb = setInterval( this.heartbeat.bind(this, this.webrtc.getPeerById(uid), health), - 500, + this.webrtcOptions.heartbeat.interval, ) this.peers.set(uid, health) @@ -193,26 +212,67 @@ function extend(Y) { return } + if (!health.lastStatsResolved) { + return peer.end(true) + } + health.lastStatsResolved = false + const self = this - // TODO: Check which stats are supported by different browsers - // TODO: Check massive renegotiation on reconnect peer.getStats(null).then((stats) => { - stats.forEach((report) => { - if (report.type == "candidate-pair" && report.selected) { - if (Date.now() - report.lastPacketReceivedTimestamp > 10000) { - return peer.end(true) - } + health.lastStatsResolved = true - if (Date.now() - report.lastPacketReceivedTimestamp > 500) { + let disconnect = true + + stats.forEach((report) => { + if ( + report.type == "candidate-pair" && + report.bytesSent > 0 && + report.bytesReceived > 0 && + report.writable + ) { + const timeSinceLastReceived = + Date.now() - health.lastReceivedTimestamp + + if (report.bytesReceived != health.lastReceivedBytes) { + health.lastReceivedBytes = report.bytesReceived + health.lastReceivedTimestamp = Date.now() + } else if ( + timeSinceLastReceived > self.webrtcOptions.heartbeat.timeout + ) { + return + } else if ( + timeSinceLastReceived > self.webrtcOptions.heartbeat.interval + ) { self.queue.postMessage({ method: "send", uid: peer.id, channel: "heartbeat", }) } + + for (let f of this.userEventListeners) { + f({ + action: "userConnection", + quality: + 1.0 - + (self.webrtcOptions.heartbeat.timeout - + Math.max( + timeSinceLastReceived, + self.webrtcOptions.heartbeat.minimum, + )) / + (self.webrtcOptions.heartbeat.timeout - + self.webrtcOptions.heartbeat.minimum), + }) + } + + disconnect = false } }) + + if (disconnect) { + peer.end(true) + } }) } -- GitLab