ndn-js
Version:
A JavaScript client library for Named Data Networking
290 lines (243 loc) • 12.8 kB
HTML
<?xml version = "1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
<!--
* Copyright (C) 2014-2019 Regents of the University of California.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* A copy of the GNU Lesser General Public License is in the file COPYING.
-->
<html xmlns = "http://www.w3.org/1999/xhtml">
<meta charset="UTF-8">
<head>
<title>NDN Get File via WebSocket</title>
<script type="text/javascript" src="../../build/ndn.js"></script>
<script type="text/javascript">
hostip = "localhost";
var face = new Face({port:9696,host:hostip});
///////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* Context for calling expressInterest to fetch big file.
*/
var ContentContext = function ContentContext(face, T0) {
this.T0 = T0; // start time
this.face = face;
this.totalBlocks = 0; // total # of segments delivered to usr;
// TODO: in this test script we simply discard the content after it is received
// should consider some way to buffer the whole data in future refactor --- SWT
// We should always start with the first element so the first data packet object cannot be ooo data
//this.firstReceivedSegmentNumber = null;
//this.firstReceivedData = null;
// statistic counters
this.ooo_count = 0; // out-of-order data packet object counter;
// when this counter reaches 3, apply fast retransmission alg.
this.pkt_recved = 0; // total # of data packet object received
this.timed_out = 0; // totle # of timed out interest
this.interest_sent = 1; // there is an initial interest before calling the callback
this.dups = 0; // total # of dup content segments
this.max_window = 32; // max window size
this.max_retrans = 5; // max # of trial before give up; if we fail on one segment, the entire process is aborted
this.snd_una = 0; // pointer to unacked segments
this.snd_wnd = 1; // current window size
this.snd_nxt = 1; // pointer to next interest to be sent
this.ooo_table_size = 128;
this.ooo_table = []; // hash table to mark ooo segments
this.terminated = false; // Set this flag after we receive all the segments;
};
ContentContext.prototype.expressInterest = function(name) {
var thisContext = this;
this.face.expressInterest
(name,
function(interest, data) { thisContext.onData(interest, data); },
function(interest) { thisContext.onTimeout(interest); });
};
ContentContext.prototype.onTimeout = function(interest) {
this.pkt_recved++;
if (this.terminated == false) {
this.timed_out++;
// Reduce window size to 1
this.snd_wnd = 1;
// Retransmit interest for this segment
this.expressInterest(interest.getName());
//console.log("Resend interest sent for " + interest.getName().toUri());
document.getElementById('content').innerHTML += "<p>Resend interest sent for "
+ interest.getName().toUri() + "</p>";
this.interest_sent++;
document.getElementById('content').innerHTML += "<p>Interest " + interest.getName().toUri() + " time out.</p>";
document.getElementById('content').innerHTML += "<p>Total number of blocks: " + this.totalBlocks + "</p>";
document.getElementById('content').innerHTML += "<p>Total number of packets: " + this.pkt_recved + "</p>";
document.getElementById('content').innerHTML += "<p>Total number of dup segments: " + this.dups + "</p>";
document.getElementById('content').innerHTML += "<p>Total number of interest sent: " + this.interest_sent + "</p>";
document.getElementById('content').innerHTML += "<p>Total number of time-out interest: " + this.timed_out + "</p>";
document.getElementById('content').innerHTML += "<p>SND_UNA: " + this.snd_una + "</p>";
document.getElementById('content').innerHTML += "<p>SND_WND: " + this.snd_wnd + "</p>";
document.getElementById('content').innerHTML += "<p>SND_NXT: " + this.snd_nxt + "</p>";
}
};
ContentContext.prototype.onData = function(interest, data) {
this.pkt_recved++;
if (data.getContent().isNull()) {
console.log("data content is null");
return;
}
// Use the segmentNumber to load multiple segments.
var segmentNumber = data.getName().get(-1).toSegment();
// Process received data here...
// Count content length
//nameStr = escape(data.getName().toUri());
//document.getElementById('content').innerHTML += "<p>Name string: " + nameStr + "</p>";
//document.getElementById('content').innerHTML += "<p>Content buffer length: " + data.getContent().size() + "</p>";
/*
// Check for the special case if the saved content is for the next segment that we need.
if (this.firstReceivedData != null &&
this.firstReceivedSegmentNumber == segmentNumber + 1) {
// Substitute the saved data send its content and keep going.
data = this.firstReceivedData;
segmentNumber = segmentNumber + 1;
// Clear firstReceivedData to save memory.
this.firstReceivedData = null;
// Process received data here...
// Count content length
//nameStr = escape(data.getName().toUri());
//document.getElementById('content').innerHTML += "<p>Name string: " + nameStr + "</p>";
//document.getElementById('content').innerHTML += "<p>Content buffer length: " + data.getContent().size() + "</p>";
this.totalBlocks++;
}
*/
// Record final seg# if present
var finalSegmentNumber = null;
if (data.getMetaInfo() != null && data.getMetaInfo().getFinalBlockId().getValue().size() > 0)
finalSegmentNumber = data.getMetaInfo().getFinalBlockId().toSegment();
// Check for out-of-order segment
if (segmentNumber != this.snd_una) {
//console.log("Out-of-order segment #" + segmentNumber + " received.");
document.getElementById('content').innerHTML += "<p>Out-of-order segment #" + segmentNumber + " received.</p>";
this.ooo_count++;
if (segmentNumber >= this.snd_nxt || segmentNumber < this.snd_una) {
// Discard segment that is out of window
//console.log("Out-of-window segment #" + segmentNumber);
document.getElementById('content').innerHTML += "<p>Out-of-window segment #" + segmentNumber + "</p>";
return;
}
// Mark this segment in hash table
var slot = segmentNumber % this.ooo_table_size;
this.ooo_table[slot] = segmentNumber;
if (this.ooo_count == 3) {
// Fast retransmit
// TODO: expressInterest for seg# = this.snd_una
//this.snd_wnd = Math.floor(this.snd_wnd / 2) + 3;
} else if (this.ooo_count > 3) {
//this.snd_wnd++;
// TODO: send a new interest if allowed by snd_wnd
// SWT: what if we never receive the first unacked segment???
}
return;
}
// In-order segment; slide window forward
this.snd_una++
this.totalBlocks++;
var slot = this.snd_una % this.ooo_table_size;
while (this.ooo_table[slot] != undefined) {
// continue to move forward until we reach a hole in the seg# sequence
this.ooo_table[slot] = undefined;
this.snd_una++;
this.totalBlocks++;
slot = this.snd_una % this.ooo_table_size;
}
if (finalSegmentNumber != null && this.snd_una == finalSegmentNumber + 1) {
// All blocks before final block, including final block, is received. Mission complete.
// Record stop time and print statistic result
this.terminated = true;
var T1 = new Date();
document.getElementById('content').innerHTML += "<p>Final block received.</p>";
document.getElementById('content').innerHTML += "<p>Time elapsed: " + (T1 - this.T0) + " ms</p>";
document.getElementById('content').innerHTML += "<p>Total number of blocks: " + this.totalBlocks + "</p>";
document.getElementById('content').innerHTML += "<p>Total number of packets: " + this.pkt_recved + "</p>";
document.getElementById('content').innerHTML += "<p>Total number of dup segments: " + this.dups + "</p>";
document.getElementById('content').innerHTML += "<p>Total number of interest sent: " + this.interest_sent + "</p>";
document.getElementById('content').innerHTML += "<p>Total number of time-out interest: " + this.timed_out + "</p>";
document.getElementById('content').innerHTML += "<p>SND_UNA: " + this.snd_una + "</p>";
document.getElementById('content').innerHTML += "<p>SND_WND: " + this.snd_wnd + "</p>";
document.getElementById('content').innerHTML += "<p>SND_NXT: " + this.snd_nxt + "</p>";
return;
}
// Adjust window size
if (this.snd_wnd < this.max_window) {
this.snd_wnd++;
//console.log("Window size after adjust: " + this.snd_wnd);
}
// Send the next interest if allowed by snd_wnd
var nextNameComponents = data.getName().components.slice(0, data.getName().size() - 1);
//console.log("SND_UNA: " + this.snd_una);
//console.log("SND_NXT: " + this.snd_nxt);
while (this.snd_nxt - this.snd_una < this.snd_wnd) {
// Make a name for the next segment and get it.
var segmentNumberPlus1 = DataUtils.nonNegativeIntToBigEndian(this.snd_nxt);
// Put a 0 byte in front.
var nextSegmentNumber = new Uint8Array(segmentNumberPlus1.length + 1);
nextSegmentNumber.set(segmentNumberPlus1, 1);
nextNameComponents.push(nextSegmentNumber);
var nextName = new Name(nextNameComponents);
this.expressInterest(nextName);
//console.log("Interest sent for seg # " + this.snd_nxt + " name " + nextName.toUri());
this.interest_sent++;
this.snd_nxt++;
nextNameComponents.pop(); // Remove segment number from components
}
};
/*
* Convert the big endian Uint8Array to an unsigned int.
* Don't check for overflow.
*/
function ArrayToNum(bytes) {
var result = 0;
for (var i = 0; i < bytes.length; ++i) {
result = result * 10;
result += (bytes[i] - 48);
}
return result;
}
/*
* Convert the int value to a new big endian Uint8Array and return.
* If value is 0 or negative, return Uint8Array(0).
*/
function NumToArray(value) {
value = Math.round(value);
if (value <= 0)
return new Uint8Array(0);
numString = value.toString();
var size = numString.length;
var result = new Uint8Array(size);
for (i = 0; i < size; i++) {
result[i] = numString.charCodeAt(i);
}
return result;
}
function run() {
document.getElementById('content').innerHTML += "<p>Started...</p>";
var name = new Name(document.getElementById('interest').value);
var context = new ContentContext(face, new Date());
context.expressInterest(name);
}
</script>
</head>
<body >
<form>
Please Enter an Interest:<br />
<input id="interest" type="text" name="INTEREST" size="50" value="/wentao.shang/mars.jpg/%00" />
</form>
<button onclick="run()">Fetch Content</button>
<p id="content">Result: <br/></p>
</body>
</html>