| <?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="../tools/build/ndn-js.js"></script> | |
| <script type="text/javascript"> | |
| hostip = "131.179.196.232"; | |
| //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> |