ccncatchunks2.c

Go to the documentation of this file.
00001 /**
00002  * @file ccncatchunks2.c
00003  * Reads stuff written by ccnsendchunks, writes to stdout.
00004  *
00005  * A CCNx command-line utility.
00006  *
00007  * Copyright (C) 2008-2010 Palo Alto Research Center, Inc.
00008  *
00009  * This work is free software; you can redistribute it and/or modify it under
00010  * the terms of the GNU General Public License version 2 as published by the
00011  * Free Software Foundation.
00012  * This work is distributed in the hope that it will be useful, but WITHOUT ANY
00013  * WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
00015  * for more details. You should have received a copy of the GNU General Public
00016  * License along with this program; if not, write to the
00017  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  * Boston, MA 02110-1301, USA.
00019  */
00020 #include <sys/time.h>
00021 #include <assert.h>
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <time.h>
00026 #include <unistd.h>
00027 
00028 #include <ccn/ccn.h>
00029 #include <ccn/charbuf.h>
00030 #include <ccn/schedule.h>
00031 #include <ccn/uri.h>
00032 
00033 #define PIPELIMIT (1U << 7)
00034 //#define GOT_HERE() fprintf(stderr, "LINE %d\n", __LINE__)
00035 #define GOT_HERE() ((void)(__LINE__))
00036 
00037 struct excludestuff;
00038 
00039 struct ooodata {
00040     struct ccn_closure closure;     /* closure per slot */
00041     unsigned char *raw_data;        /* content that has arrived out-of-order */
00042     size_t raw_data_size;           /* its size (plus 1) in bytes */
00043 };
00044 
00045 struct mydata {
00046     struct ccn *h;
00047     int allow_stale;
00048     int use_decimal;
00049     unsigned ooo_base;
00050     unsigned ooo_count;
00051     unsigned curwindow;
00052     unsigned maxwindow;
00053     unsigned sendtime;
00054     unsigned sendtime_slot;
00055     unsigned rtt;
00056     unsigned rtte;
00057     unsigned backoff;
00058     unsigned finalslot;
00059     struct ccn_charbuf *name;
00060     struct ccn_charbuf *templ;
00061     struct excludestuff *excl;
00062     struct ccn_schedule *sched;
00063     struct ccn_scheduled_event *report;
00064     struct ccn_scheduled_event *holefiller;
00065     intmax_t interests_sent;
00066     intmax_t pkts_recvd;
00067     intmax_t co_bytes_recvd;
00068     intmax_t delivered;
00069     intmax_t delivered_bytes;
00070     intmax_t junk;
00071     intmax_t holes;
00072     intmax_t timeouts;
00073     intmax_t dups;
00074     intmax_t lastcheck;
00075     intmax_t unverified;
00076     struct timeval start_tv;
00077     struct timeval stop_tv;
00078     struct ooodata ooo[PIPELIMIT];
00079 };
00080 
00081 static int fill_holes(struct ccn_schedule *sched, void *clienth, 
00082                       struct ccn_scheduled_event *ev, int flags);
00083 
00084 static FILE* logstream = NULL;
00085 
00086 static void
00087 usage(const char *progname)
00088 {
00089     fprintf(stderr,
00090             "%s [-a] [-p n] ccnx:/a/b\n"
00091             "   Reads stuff written by ccnsendchunks under"
00092             " the given uri and writes to stdout\n"
00093             "   -a - allow stale data\n"
00094             "   -p n - use up to n pipeline slots\n"
00095             "   -s - use new-style segmentation markers\n",
00096             progname);
00097     exit(1);
00098 }
00099 
00100 static void
00101 mygettime(const struct ccn_gettime *self, struct ccn_timeval *result)
00102 {
00103     struct timeval now = {0};
00104     gettimeofday(&now, 0);
00105     result->s = now.tv_sec;
00106     result->micros = now.tv_usec;
00107 }
00108 
00109 static struct ccn_gettime myticker = {
00110     "timer",
00111     &mygettime,
00112     1000000,
00113     NULL
00114 };
00115 
00116 static void
00117 update_rtt(struct mydata *md, int incoming, unsigned slot)
00118 {
00119     struct timeval now = {0};
00120     unsigned t, delta, rtte;
00121     
00122     if (!incoming && md->sendtime_slot == ~0)
00123         md->sendtime_slot = slot;
00124     if (slot != md->sendtime_slot)
00125         return;
00126     gettimeofday(&now, 0);
00127     t = ((unsigned)(now.tv_sec) * 1000000) + (unsigned)(now.tv_usec);
00128     if (incoming) {
00129         delta = t - md->sendtime;
00130         md->rtt = delta;
00131         if (delta <= 30000000) {
00132             rtte = md->rtte;
00133             if (delta > rtte)
00134                 rtte = rtte + (rtte >> 3);
00135             else
00136                 rtte = rtte - (rtte >> 7);
00137             if (rtte < 127)
00138                 rtte = delta;
00139             md->rtte = rtte;
00140         }
00141         if (md->holefiller == NULL)
00142             md->holefiller = ccn_schedule_event(md->sched, 10000, &fill_holes, NULL, 0);
00143         md->sendtime_slot = ~0;
00144         if (logstream)
00145             fprintf(logstream,
00146                     "%ld.%06u ccncatchunks2: "
00147                     "%jd isent, %jd recvd, %jd junk, %jd holes, %jd t/o, %jd unvrf, "
00148                     "%u curwin, %u rtt, %u rtte\n",
00149                     (long)now.tv_sec,
00150                     (unsigned)now.tv_usec,
00151                     md->interests_sent,
00152                     md->pkts_recvd,
00153                     md->junk,
00154                     md->holes,
00155                     md->timeouts,
00156                     md->unverified,
00157                     md->curwindow,
00158                     md->rtt,
00159                     md->rtte
00160                     );
00161     }
00162     else
00163         md->sendtime = t;
00164 }
00165 
00166 static int
00167 reporter(struct ccn_schedule *sched, void *clienth, 
00168          struct ccn_scheduled_event *ev, int flags)
00169 {
00170     struct timeval now = {0};
00171     struct mydata *md = clienth;
00172     gettimeofday(&now, 0);
00173     fflush(stdout);
00174     fprintf(stderr,
00175             "%ld.%06u ccncatchunks2[%d]: "
00176             "%jd isent, %jd recvd, %jd junk, %jd holes, %jd t/o, %jd unvrf, "
00177             "%u curwin, %u rtt, %u rtte\n",
00178             (long)now.tv_sec,
00179             (unsigned)now.tv_usec,
00180             (int)getpid(),
00181             md->interests_sent,
00182             md->pkts_recvd,
00183             md->junk,
00184             md->holes,
00185             md->timeouts,
00186             md->unverified,
00187             md->curwindow,
00188             md->rtt,
00189             md->rtte
00190             );
00191     if ((flags & CCN_SCHEDULE_CANCEL) != 0) {
00192         md->report = NULL;
00193         return(0);
00194     }
00195     return(3000000);
00196 }
00197 
00198 void
00199 print_summary(struct mydata *md)
00200 {
00201     const char *expid;
00202     const char *dlm = " ";
00203     double elapsed = 0.0;
00204     intmax_t delivered_bytes;
00205     double rate = 0.0;
00206     
00207     expid = getenv("CCN_EXPERIMENT_ID");
00208     if (expid == NULL)
00209         expid = dlm = "";
00210     gettimeofday(&md->stop_tv, 0);
00211     elapsed = (double)(long)(md->stop_tv.tv_sec - md->start_tv.tv_sec);
00212     elapsed += ((int)md->stop_tv.tv_usec - (int)md->start_tv.tv_usec)/1000000.0;
00213     delivered_bytes = md->delivered_bytes;
00214     if (elapsed > 0.00001)
00215         rate = delivered_bytes/elapsed;
00216     fprintf(stderr,
00217             "%ld.%06u ccncatchunks2[%d]: %s%s"
00218             "%jd bytes transferred in %.6f seconds (%.0f bytes/sec)"
00219             "\n",
00220             (long)md->stop_tv.tv_sec,
00221             (unsigned)md->stop_tv.tv_usec,
00222             (int)getpid(),
00223             expid,
00224             dlm,
00225             delivered_bytes,
00226             elapsed,
00227             rate
00228             );
00229 }
00230 
00231 struct ccn_charbuf *
00232 make_template(struct mydata *md)
00233 {
00234     struct ccn_charbuf *templ = ccn_charbuf_create();
00235     ccn_charbuf_append_tt(templ, CCN_DTAG_Interest, CCN_DTAG);
00236     ccn_charbuf_append_tt(templ, CCN_DTAG_Name, CCN_DTAG);
00237     ccn_charbuf_append_closer(templ); /* </Name> */
00238     // XXX - use pubid if possible
00239     ccn_charbuf_append_tt(templ, CCN_DTAG_MaxSuffixComponents, CCN_DTAG);
00240     ccnb_append_number(templ, 1);
00241     ccn_charbuf_append_closer(templ); /* </MaxSuffixComponents> */
00242     if (md->allow_stale) {
00243         ccn_charbuf_append_tt(templ, CCN_DTAG_AnswerOriginKind, CCN_DTAG);
00244         ccnb_append_number(templ, CCN_AOK_DEFAULT | CCN_AOK_STALE);
00245         ccn_charbuf_append_closer(templ); /* </AnswerOriginKind> */
00246     }
00247     ccn_charbuf_append_closer(templ); /* </Interest> */
00248     return(templ);
00249 }
00250 
00251 static struct ccn_charbuf *
00252 sequenced_name(struct mydata *md, uintmax_t seq)
00253 {
00254     struct ccn_charbuf *name = NULL;
00255     struct ccn_charbuf *temp = NULL;
00256     
00257     name = ccn_charbuf_create();
00258     ccn_charbuf_append(name, md->name->buf, md->name->length);
00259     if (md->use_decimal) {
00260         temp = ccn_charbuf_create();
00261         ccn_charbuf_putf(temp, "%ju", seq);
00262         ccn_name_append(name, temp->buf, temp->length);
00263         ccn_charbuf_destroy(&temp);
00264     }
00265     else
00266         ccn_name_append_numeric(name, CCN_MARKER_SEQNUM, seq);
00267     return(name);
00268 }
00269 
00270 static void
00271 ask_more(struct mydata *md, uintmax_t seq)
00272 {
00273     struct ccn_charbuf *name = NULL;
00274     struct ccn_charbuf *templ = NULL;
00275     int res;
00276     unsigned slot;
00277     struct ccn_closure *cl = NULL;
00278     
00279     slot = seq % PIPELIMIT;
00280     cl = &md->ooo[slot].closure;
00281     if (cl->intdata == -1)
00282         cl->intdata = seq;
00283     assert(cl->intdata == seq);
00284     assert(md->ooo[slot].raw_data_size == 0);
00285     name = sequenced_name(md, seq);
00286     templ = make_template(md);
00287     update_rtt(md, 0, slot);
00288     res = ccn_express_interest(md->h, name, cl, templ);
00289     if (res < 0) abort();
00290     md->interests_sent++;
00291     ccn_charbuf_destroy(&templ);
00292     ccn_charbuf_destroy(&name);
00293     if (seq == md->delivered + md->ooo_count)
00294         md->ooo_count++;
00295     assert(seq >= md->delivered);
00296     assert(seq < md->delivered + md->ooo_count);
00297     assert(md->ooo_count < PIPELIMIT);
00298 }
00299 
00300 static enum ccn_upcall_res
00301 hole_filled(struct ccn_closure *selfp,
00302     enum ccn_upcall_kind kind,
00303     struct ccn_upcall_info *info)
00304 {
00305     if (kind == CCN_UPCALL_FINAL)
00306         free(selfp);
00307     return(CCN_UPCALL_RESULT_OK);
00308 }
00309 
00310 static int
00311 fill_holes(struct ccn_schedule *sched, void *clienth, 
00312          struct ccn_scheduled_event *ev, int flags)
00313 {
00314     struct mydata *md = clienth;
00315     struct ccn_charbuf *name = NULL;
00316     struct ccn_charbuf *templ = NULL;
00317     struct ccn_closure *cl = NULL;
00318     unsigned backoff;
00319     int delay;
00320     
00321     if ((flags & CCN_SCHEDULE_CANCEL) != 0) {
00322         md->holefiller = NULL;
00323         return(0);
00324     }
00325     backoff = md->backoff;
00326     if (md->delivered == md->lastcheck && md->ooo_count > 0) {
00327         if (backoff == 0) {
00328             md->holes++;
00329             fprintf(stderr, "*** Hole at %jd\n", md->delivered);
00330             reporter(sched, md, NULL, 0);
00331             md->curwindow = 1;
00332             cl = calloc(1, sizeof(*cl));
00333             cl->p = &hole_filled;
00334             name = sequenced_name(md, md->delivered);
00335             templ = make_template(md);
00336             ccn_express_interest(md->h, name, cl, templ);
00337             md->interests_sent++;
00338             ccn_charbuf_destroy(&templ);
00339             ccn_charbuf_destroy(&name);
00340         }
00341         if ((6000000 >> backoff) > md->rtte)
00342             backoff++;
00343     }
00344     else {
00345         md->lastcheck = md->delivered;
00346         backoff = 0;
00347     }
00348     md->backoff = backoff;
00349     delay = (md->rtte << backoff);
00350     if (delay < 10000)
00351         delay = 10000;
00352     return(delay);
00353 }
00354 
00355 static int
00356 is_final(struct ccn_upcall_info *info)
00357 {
00358         // XXX The test below should get refactored into the library
00359     const unsigned char *ccnb;
00360     size_t ccnb_size;
00361     ccnb = info->content_ccnb;
00362     if (ccnb == NULL || info->pco == NULL)
00363         return(0);
00364     ccnb_size = info->pco->offset[CCN_PCO_E];
00365     if (info->pco->offset[CCN_PCO_B_FinalBlockID] !=
00366         info->pco->offset[CCN_PCO_E_FinalBlockID]) {
00367         const unsigned char *finalid = NULL;
00368         size_t finalid_size = 0;
00369         const unsigned char *nameid = NULL;
00370         size_t nameid_size = 0;
00371         struct ccn_indexbuf *cc = info->content_comps;
00372         ccn_ref_tagged_BLOB(CCN_DTAG_FinalBlockID, ccnb,
00373                             info->pco->offset[CCN_PCO_B_FinalBlockID],
00374                             info->pco->offset[CCN_PCO_E_FinalBlockID],
00375                             &finalid,
00376                             &finalid_size);
00377         if (cc->n < 2) abort();
00378         ccn_ref_tagged_BLOB(CCN_DTAG_Component, ccnb,
00379                             cc->buf[cc->n - 2],
00380                             cc->buf[cc->n - 1],
00381                             &nameid,
00382                             &nameid_size);
00383         if (finalid_size == nameid_size &&
00384               0 == memcmp(finalid, nameid, nameid_size))
00385             return(1);
00386     }
00387     return(0);
00388 }
00389 
00390 enum ccn_upcall_res
00391 incoming_content(struct ccn_closure *selfp,
00392                  enum ccn_upcall_kind kind,
00393                  struct ccn_upcall_info *info)
00394 {
00395     const unsigned char *ccnb = NULL;
00396     size_t ccnb_size = 0;
00397     const unsigned char *data = NULL;
00398     size_t data_size = 0;
00399     size_t written;
00400     int res;
00401     struct mydata *md = selfp->data;
00402     unsigned slot;
00403     
00404     if (kind == CCN_UPCALL_FINAL) {
00405         selfp->intdata = -1;
00406         return(CCN_UPCALL_RESULT_OK);
00407     }
00408 GOT_HERE();
00409     if (kind == CCN_UPCALL_INTEREST_TIMED_OUT) {
00410         md->timeouts++;
00411         if (selfp->refcount > 1 || selfp->intdata == -1)
00412             return(CCN_UPCALL_RESULT_OK);
00413         md->interests_sent++;
00414         md->curwindow = 1;
00415         // XXX - may need to reseed bloom filter
00416         return(CCN_UPCALL_RESULT_REEXPRESS);
00417     }
00418 GOT_HERE();
00419     if (kind != CCN_UPCALL_CONTENT && kind != CCN_UPCALL_CONTENT_UNVERIFIED)
00420         return(CCN_UPCALL_RESULT_ERR);
00421     assert(md != NULL);
00422     if (kind == CCN_UPCALL_CONTENT_UNVERIFIED) {
00423         if (md->pkts_recvd == 0)
00424             return(CCN_UPCALL_RESULT_VERIFY);
00425         md->unverified++;
00426     }
00427     md->pkts_recvd++;
00428     if (selfp->intdata == -1) {
00429         /* Outside the window we care about. Toss it. */
00430         md->dups++;
00431         return(CCN_UPCALL_RESULT_OK);
00432     }
00433     ccnb = info->content_ccnb;
00434     ccnb_size = info->pco->offset[CCN_PCO_E];
00435     res = ccn_content_get_value(ccnb, ccnb_size, info->pco, &data, &data_size);
00436     if (res < 0) abort();
00437 GOT_HERE();
00438     /* OK, we will accept this block. */
00439     md->co_bytes_recvd += data_size;
00440     slot = ((uintptr_t)selfp->intdata) % PIPELIMIT;
00441     assert(selfp == &md->ooo[slot].closure);
00442     if (is_final(info)) {
00443         GOT_HERE();
00444         md->finalslot = slot;
00445     }
00446     if (slot != md->ooo_base || md->ooo_count == 0) {
00447         /* out-of-order data, save for later */
00448         struct ooodata *ooo = &md->ooo[slot];
00449         if (ooo->raw_data_size == 0) {
00450 GOT_HERE();
00451             update_rtt(md, 1, slot);
00452             ooo->raw_data = malloc(data_size);
00453             memcpy(ooo->raw_data, data, data_size);
00454             ooo->raw_data_size = data_size + 1;
00455         }
00456         else
00457             md->dups++;
00458         if (md->curwindow > 1)
00459             md->curwindow--;
00460     }
00461     else {
00462         assert(md->ooo[slot].raw_data_size == 0);
00463         update_rtt(md, 1, slot);
00464         md->ooo[slot].closure.intdata = -1;
00465         md->delivered++;
00466         md->delivered_bytes += data_size;
00467         written = fwrite(data, data_size, 1, stdout);
00468         if (written != 1)
00469             exit(1);
00470         /* Check for EOF */
00471         if (slot == md->finalslot) {
00472             GOT_HERE();
00473             ccn_schedule_destroy(&md->sched);
00474             print_summary(md);
00475             exit(0);
00476         }
00477         md->ooo_count--;
00478         slot = (slot + 1) % PIPELIMIT;
00479         if (md->curwindow < md->maxwindow)
00480             md->curwindow++;
00481         while (md->ooo_count > 0 && md->ooo[slot].raw_data_size != 0) {
00482             struct ooodata *ooo = &md->ooo[slot];
00483             md->delivered++;
00484             md->delivered_bytes += (ooo->raw_data_size - 1);
00485             written = fwrite(ooo->raw_data, ooo->raw_data_size - 1, 1, stdout);
00486             if (written != 1)
00487                 exit(1);
00488             /* Check for EOF */
00489             if (slot == md->finalslot) {
00490                 GOT_HERE();
00491                 ccn_schedule_destroy(&md->sched);
00492                 exit(0);
00493             }
00494             free(ooo->raw_data);
00495             ooo->raw_data = NULL;
00496             ooo->raw_data_size = 0;
00497             slot = (slot + 1) % PIPELIMIT;
00498             md->ooo_count--;
00499         }
00500         md->ooo_base = slot;
00501     }
00502     
00503     /* Ask for the next one or two */
00504     if (md->ooo_count < md->curwindow)
00505         ask_more(md, md->delivered + md->ooo_count);
00506     if (md->ooo_count < md->curwindow)
00507         ask_more(md, md->delivered + md->ooo_count);
00508     
00509     return(CCN_UPCALL_RESULT_OK);
00510 }
00511 
00512 int
00513 main(int argc, char **argv)
00514 {
00515     struct ccn *ccn = NULL;
00516     struct ccn_charbuf *name = NULL;
00517     struct ccn_closure *incoming = NULL;
00518     const char *arg = NULL;
00519     int res;
00520     int micros;
00521     int opt;
00522     struct mydata *mydata;
00523     int allow_stale = 0;
00524     int use_decimal = 1;
00525     int i;
00526     unsigned maxwindow = PIPELIMIT-1;
00527     
00528     if (maxwindow > 31)
00529         maxwindow = 31;
00530     
00531     while ((opt = getopt(argc, argv, "hap:s")) != -1) {
00532         switch (opt) {
00533             case 'a':
00534                 allow_stale = 1;
00535                 break;
00536             case 'p':
00537                 res = atoi(optarg);
00538                 if (1 <= res && res < PIPELIMIT)
00539                     maxwindow = res;
00540                 else
00541                     usage(argv[0]);
00542                 break;
00543             case 's':
00544                 use_decimal = 0;
00545                 break;
00546             case 'h':
00547             default:
00548                 usage(argv[0]);
00549         }
00550     }
00551     arg = argv[optind];
00552     if (arg == NULL)
00553         usage(argv[0]);
00554     name = ccn_charbuf_create();
00555     res = ccn_name_from_uri(name, arg);
00556     if (res < 0) {
00557         fprintf(stderr, "%s: bad ccn URI: %s\n", argv[0], arg);
00558         exit(1);
00559     }
00560     if (argv[optind + 1] != NULL)
00561         fprintf(stderr, "%s warning: extra arguments ignored\n", argv[0]);
00562     ccn = ccn_create();
00563     if (ccn_connect(ccn, NULL) == -1) {
00564         perror("Could not connect to ccnd");
00565         exit(1);
00566     }
00567     
00568     mydata = calloc(1, sizeof(*mydata));
00569     mydata->h = ccn;
00570     mydata->name = name;
00571     mydata->allow_stale = allow_stale;
00572     mydata->use_decimal = use_decimal;
00573     mydata->excl = NULL;
00574     mydata->sched = ccn_schedule_create(mydata, &myticker);
00575     mydata->report = ccn_schedule_event(mydata->sched, 0, &reporter, NULL, 0);
00576     mydata->holefiller = NULL;
00577     mydata->maxwindow = maxwindow;
00578     mydata->finalslot = ~0;
00579     for (i = 0; i < PIPELIMIT; i++) {
00580         incoming = &mydata->ooo[i].closure;
00581         incoming->p = &incoming_content;
00582         incoming->data = mydata;
00583         incoming->intdata = -1;
00584     }
00585     mydata->ooo_base = 0;
00586     mydata->ooo_count = 0;
00587     mydata->curwindow = 1;
00588     gettimeofday(&mydata->start_tv, 0);
00589     logstream = NULL;
00590     // logstream = fopen("xxxxxxxxxxxxxxlogstream" + (unsigned)getpid()%10, "wb"); 
00591     ask_more(mydata, 0);
00592     /* Run a little while to see if there is anything there */
00593     res = ccn_run(ccn, 500);
00594     if (mydata->delivered == 0) {
00595         fprintf(stderr, "%s: not found: %s\n", argv[0], arg);
00596         exit(1);
00597     }
00598     /* We got something, run until end of data or somebody kills us */
00599     while (res >= 0) {
00600         micros = ccn_schedule_run(mydata->sched);
00601         if (micros < 0)
00602             micros = 10000000;
00603         res = ccn_run(ccn, micros / 1000);
00604     }
00605     ccn_schedule_destroy(&mydata->sched);
00606     print_summary(mydata);
00607     ccn_destroy(&ccn);
00608     exit(res < 0);
00609 }
Generated on Fri May 13 16:27:01 2011 for Content-Centric Networking in C by  doxygen 1.6.3