In reality we are quite centralized due to the fact:
(actually it's not that simple)
Tasks:
var channel_confs = {
TCPLIKE_CHANNEL:{}, //reliable, ordered
UNORDERED_RELIABLE:{ ordered: false },
ORDERED_PARTIAL_3ATTEMPS: { ordered: true, maxRetransmits: 3},
UNORDERED_PARTIAL_3ATTEMPS: { ordered: false, maxRetransmits: 3},
ORDERED_PARTIAL_1S: { ordered: true, maxRetransmitTime: 1000},
UNORDERED_PARTIAL_1S: { ordered: false, maxRetransmitTime: 1000},
UNORDERED_UNRELIABLE: { ordered: false, maxRetransmits: 0 }
}
var pc = new RTCPeerConnection(servers
) // don't do {RtpDataChannels:true}
var dc = pc.createDataChannel("dc_label", channel_confs.UNORDERED_PARTIAL_3ATTEMPS);
/**
*
* @param swarmId
* @param seeder true if seeder
* @param complete true if availabilityMap, false if update
* @param blockIds in list of chunks, or availabilityMap
* @constructor
*/
function Have(swarmId, seeder, availabilityMap, blockIds) {
this.tag = exports.P2P_HAVE;
this.swarmId = swarmId;
this.seeder = seeder;
this.blockIds = [];
if (!seeder) {
if (availabilityMap) {
this.complete = true
this.availabilityMap = availabilityMap;
}
else {
this.complete = false
this.blockIds = blockIds;
}
}
} // swarmId (4bytes)
// chunkIds - optional - array of ids each 4bytes
// Example of 2 chunks encoding: 0x010203040A0B0C0D. 0x01-0x04 are the first encoded chunk, 0x0A-0x0D are the second encoded chunk)
function Request(swarmId, chunkIds) {
this.tag = exports.P2P_REQUEST
this.swarmId = swarmId;
if (!chunkIds) chunkIds = [];
this.chunkIds = chunkIds;
}
// attributes (ordered)
// swarmId - 4bytes - only the intial 4 bytes will be encoded
// length: chunk size + swarmId length under 1200 bytes constraint
// constraints:
// peer will send only hash varified chunks
function Data(swarmId, chunkId, payload) {
this.tag = exports.P2P_DATA;
this.swarmId = swarmId;
this.chunkId = chunkId;
this.payload = payload;
}
var p2p_have_encode = function (message) {
var buffers = [];
var swarmId = ascii2ab(BinaryProtocol.packSwarmId(message.swarmId));
buffers.push(swarmId);
var seeder = BoolToUInt8Array(message.seeder);
buffers.push(seeder);
var complete = BoolToUInt8Array(message.complete);
buffers.push(complete);
if (message.complete) {
buffers.push(message.availabilityMap);
} else {
var blockIdsEncoded = UInt32ToUInt8Array(message.blockIds);
buffers.push(blockIdsEncoded);
}
return BinaryProtocol.concat(buffers);
};
var p2p_have_decode = function (buffer, index, length) {
var blockIds;
var availabilityMap;
var swarmId = ab2ascii(buffer.slice(index, index + BinaryProtocol.transferedSwarmIdSize));
index += BinaryProtocol.transferedSwarmIdSize;
length -= BinaryProtocol.transferedSwarmIdSize;
var seeder = UInt8ArrayToBool(buffer, index);
index++;
length--;
if (!seeder) {
var complete = UInt8ArrayToBool(buffer, index);
index++;
length--;
if (complete) {
availabilityMap = buffer.slice(index, index + length);
} else { //update
blockIds = [];
while (length > 0) {
blockIds.push(UInt8ArrayToInt32(buffer, index));
index += 4;
length -= 4;
}
}
}
return new protocol.Have(swarmId, seeder, availabilityMap, blockIds);
};
Last Conclusion from Sharefest:
- Data channels is hard:
- learn a new javascript api
- create a signaling protocol
- STUN/TURN
- Big messages
- Really big messages
- Multi-party p2p
- P2PXHR - Empower your xhr's
peer5.Request = function(peer5_options){
peer5.Request.prototype = {
/* -- Attributes -- */
this.readyState;
this.response;
this.status;
/* -- Methods -- */
open: function(method, id) {},
send: function() {},
abort: function(options) {},
/* -- EVENTS --
onreadystatechange: function(e) {},
onloadstart: function(e) {},
onprogress: function(e) {},
onerror:function(e) {},
onload:function(e) {},
}
}
var xhr = new XMLHttpRequest(); // (taken from html5rocks.com) xhr.open('GET', '/path/to/image.png', true); xhr.responseType = 'blob'; xhr.onload = function(e) { if (this.status == 200) { // Note: .response instead of .responseText var blob = new Blob([this.response], {type: 'image/png'}); ... } }; xhr.send();
//ctor takes optional parameters to control hybrid downloading var request = new peer5.Request();
request.open('GET', 'http://path/to/a/big/file.json');
request.onload = function(e){
console.log(this.response);
... }; request.send();
var request = new peer5.Request();
request.open("GET",url);
request.onprogress = function(e){
console.log(e.loadedHTTP);
console.log(e.loadedP2P);
$("#amount-http").text(e.loadedHTTP);
$("#amount-p2p").text(e.loadedP2P);
$("#progress").attr("value",((e.loadedHTTP+e.loadedP2P)/e.total)*100);
};
request.onload = function(e){
$("#progress").hide();
var saveB = document.getElementById("saveBtn");
saveB.hidden=false;
saveB.onclick = function(){save(e.currentTarget.response);};
};
request.onerror = function(e){
console.log(e.error);
}
request.send();
var request = new peer5.Request();
request.open("GET",url);
request.onprogress = function(e){
console.log(e.loadedHTTP);
console.log(e.loadedP2P);
};
request.onload = function(e){
console.log(e.currentTarget.response);
$("#cat").attr("src",e.currentTarget.response);
};
request.onerror = function(e){
console.log(e.error);
}
request.send();
downloader
peer5Request = new peer5.Request({downloadMode:"p2p"});
peer5Request.onload = function(e){
console.log("Finished downloading the file");
finishProgressUI(e.loaded, e.total);
}
peer5Request.onloadstart = function(e){
initiateProgressUI();
}
peer5Request.onprogress = function(e){
if(this.readyState == 1) return;
updateProgressUI(e.loaded,e.total);
}
peer5Request.onreadystatechange = function(e){
if(this.readyState == 1){
document.getElementById('box-text').textContent = "Initiating...";
}else if(this.readyState == 2){
document.getElementById('box-text').textContent = "";
}
}
peer5Request.onswarmstatechange = function(e){
if(e.numOfPeers){
$('#numOfPeers').html('Connected to ' + e.numOfPeers + ' peers');
$('#numOfPeers').show();
}
}
peer5Request.onerror = function(e){
switch (this.status) {
case peer5.Request.SWARMID_NOT_FOUND_ERR:
roomNotFoundUI();
break;
case peer5.Request.CHROME_ONLY_SWARM_ERR:
chromeOnlyRoomUI();
break;
case peer5.Request.FIREFOX_ONLY_SWARM_ERR:
firefoxOnlyRoomUI();
break;
case peer5.Request.FILE_SIZE_ERR:
fileSizeTooBigUI();
break;
}
}
peer5Request.open("GET",document.location.pathname.substring(1));
peer5Request.send();
Uploader
var peer5Request = new peer5.Request({downloadMode:"p2p"});
var file = files[0];
peer5Request.onprogress = function(e){
updateShareProgressUI(e.loaded, e.total);
}
peer5Request.onload = function(e){
userState = {isSeeder:true,origin:true};
var url = this.getFileInfo().swarmId;
history.pushState({}, url, url);
ga('send', 'pageview'); // should notify the current URL
updateList(file.name);
$('#box-text').append(' is ready to be shared!' + '<br>');
showLink();
updateShareButtons();
}
peer5Request.open('POST');
peer5Request.onerror = function(e){
switch (this.status) {
case peer5.Request.FILE_SIZE_ERR:
fileSizeTooBigUI();
break;
}
}
peer5Request.send(file);
- Encrypted send
- Clipboard share
- above/below the fold loading
- and more
hadar@peer5.com // sharefest.me