<?xml version = "1.0" encoding="utf-8" ?> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" | |
"DTD/xhtml1-strict.dtd"> | |
<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.js"></script> | |
<script type="text/javascript"> | |
hostip = "localhost"; | |
var ndncon = new NDN({port:9696,host:hostip}); | |
//var ndncon = new NDN({port:9696,host:hostip,verify:false}); | |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
/* | |
* Closure for calling expressInterest to fetch big file. | |
*/ | |
var ContentClosure = function ContentClosure(ndn, T0) { | |
// Inherit from Closure. | |
Closure.call(this); | |
this.T0 = T0; // start time | |
this.ndn = ndn; | |
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 content object cannot be ooo data | |
//this.firstReceivedSegmentNumber = null; | |
//this.firstReceivedContentObject = null; | |
// statistic counters | |
this.ooo_count = 0; // out-of-order content object counter; | |
// when this counter reaches 3, apply fast retransmission alg. | |
this.pkt_recved = 0; // total # of content object received | |
this.timed_out = 0; // totle # of timed out interest | |
this.interest_sent = 1; // there is an initial interest before calling the closure upcall | |
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; | |
}; | |
ContentClosure.prototype.upcall = function(kind, upcallInfo) { | |
this.pkt_recved++; | |
if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) { | |
if (this.terminated == false) { | |
this.timed_out++; | |
// Reduce window size to 1 | |
this.snd_wnd = 1; | |
// Retransmit interest for this segment | |
this.ndn.expressInterest(upcallInfo.interest.name, this); | |
//console.log("Resend interest sent for " + upcallInfo.interest.name.getName()); | |
document.getElementById('content').innerHTML += "<p>Resend interest sent for " | |
+ upcallInfo.interest.name.getName() + "</p>"; | |
this.interest_sent++; | |
document.getElementById('content').innerHTML += "<p>Interest " + upcallInfo.interest.name.getName() + " 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>"; | |
} | |
return Closure.RESULT_OK; | |
} | |
if (kind == Closure.UPCALL_CONTENT_BAD) { | |
console.log("NdnProtocol.ContentClosure: signature verification failed"); | |
console.log(upcallInfo.contentObject.name.getName()); | |
console.log(DataUtils.toHex(upcallInfo.contentObject.signature.Witness).toLowerCase()); | |
return Closure.RESULT_OK; | |
} | |
if (!(kind == Closure.UPCALL_CONTENT || | |
kind == Closure.UPCALL_CONTENT_UNVERIFIED)) | |
// The upcall is not for us. | |
return Closure.RESULT_ERR; | |
var contentObject = upcallInfo.contentObject; | |
if (contentObject.content == null) { | |
console.log("NdnProtocol.ContentClosure: contentObject.content is null"); | |
return Closure.RESULT_ERR; | |
} | |
// Use the segmentNumber to load multiple segments. | |
var segmentNumber = DataUtils.bigEndianToUnsignedInt | |
(contentObject.name.components[contentObject.name.components.length - 1]); | |
//console.log("Seg # " + segmentNumber + " received"); | |
//console.log("Seg name " + contentObject.name.getName()); | |
/* | |
if (this.firstReceivedSegmentNumber == null) { | |
// This is the first call. | |
this.firstReceivedSegmentNumber = segmentNumber; | |
if (segmentNumber != 0) { | |
// Special case: Save this content object for later and request segment zero. | |
// SWT: this should not happen in normal case; | |
// ccnd should always return the first segment if no seg# is specified | |
this.firstReceivedContentObject = contentObject; | |
var componentsForZero = contentObject.name.components.slice | |
(0, contentObject.name.components.length - 1); | |
componentsForZero.push([0]); | |
this.ndn.expressInterest(new Name(componentsForZero), this); | |
return Closure.RESULT_OK; | |
} | |
} | |
*/ | |
// Process received data here... | |
// Count content length | |
//nameStr = escape(contentObject.name.getName()); | |
//document.getElementById('content').innerHTML += "<p>Name string: " + nameStr + "</p>"; | |
//document.getElementById('content').innerHTML += "<p>Content buffer length: " + contentObject.content.length + "</p>"; | |
/* | |
// Check for the special case if the saved content is for the next segment that we need. | |
if (this.firstReceivedContentObject != null && | |
this.firstReceivedSegmentNumber == segmentNumber + 1) { | |
// Substitute the saved contentObject send its content and keep going. | |
contentObject = this.firstReceivedContentObject; | |
segmentNumber = segmentNumber + 1; | |
// Clear firstReceivedContentObject to save memory. | |
this.firstReceivedContentObject = null; | |
// Process received data here... | |
// Count content length | |
//nameStr = escape(contentObject.name.getName()); | |
//document.getElementById('content').innerHTML += "<p>Name string: " + nameStr + "</p>"; | |
//document.getElementById('content').innerHTML += "<p>Content buffer length: " + contentObject.content.length + "</p>"; | |
this.totalBlocks++; | |
} | |
*/ | |
// Record final seg# if present | |
var finalSegmentNumber = null; | |
if (contentObject.signedInfo != null && contentObject.signedInfo.finalBlockID != null) | |
finalSegmentNumber = DataUtils.bigEndianToUnsignedInt(contentObject.signedInfo.finalBlockID); | |
// 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 Closure.RESULT_OK; | |
} | |
// 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 Closure.RESULT_OK; | |
} | |
// 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 Closure.RESULT_OK; | |
} | |
// 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 = contentObject.name.components.slice(0, contentObject.name.components.length - 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.ndn.expressInterest(nextName, this); | |
//console.log("Interest sent for seg # " + this.snd_nxt + " name " + nextName.getName()); | |
this.interest_sent++; | |
this.snd_nxt++; | |
nextNameComponents.pop(); // Remove segment number from components | |
} | |
return Closure.RESULT_OK; | |
}; | |
/* | |
* 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; | |
} | |
/////////////////////////////////////////////////////////////////////////////////////////////////////////// | |
/* | |
var AsyncGetClosure = function AsyncGetClosure(T0) { | |
this.T0 = T0; // Start time | |
// Inherit from Closure. | |
Closure.call(this); | |
}; | |
AsyncGetClosure.prototype.upcall = function(kind, upcallInfo) { | |
//console.log("Closure.upcall() executed."); | |
if (kind == Closure.UPCALL_FINAL) { | |
// Do nothing. | |
} else if (kind == Closure.UPCALL_CONTENT) { | |
var T1 = new Date(); | |
var content = upcallInfo.contentObject; | |
nameStr = escape(content.name.getName()); | |
document.getElementById('content').innerHTML += "<p>Time elapsed: " + (T1 - this.T0) + " ms</p>"; | |
document.getElementById('content').innerHTML += "<p>Name string: " + nameStr + "</p>"; | |
document.getElementById('content').innerHTML += "<p>Content buffer length: " + content.content.length + "</p>"; | |
//console.log("In callback, nameStr: " + nameStr); | |
//console.log("In callback, content: "); | |
//console.log(content.content.length); | |
//document.getElementById('content').innerHTML += contentObjectToHtml(content); | |
} else if (kind == Closure.UPCALL_INTEREST_TIMED_OUT) { | |
console.log("Closure.upcall called with interest time out."); | |
} | |
return Closure.RESULT_OK; | |
}; | |
*/ | |
function run() { | |
document.getElementById('content').innerHTML += "<p>Started...</p>"; | |
//ndn.expressInterest(new Name(document.getElementById('interest').value), new AsyncGetClosure( new Date() )); | |
var name = new Name(document.getElementById('interest').value); | |
ndncon.expressInterest( name, | |
new ContentClosure( ndncon, new Date() )); | |
} | |
</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> |