#include #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include /* * Connection object. */ struct cx { int fd; int *counter; /* shared counter of the number of messages received. */ int total; /* total number of messages to send */ char *http_request; void (*read_fun)(int,short,void*); /* called when able to read fd */ void (*write_fun)(int,short,void*); /* called when able to write fd */ /* libevent data structures */ struct event evr, evw; struct event_base *base; }; int webdis_connect(const char *host, short port) { int ret; int fd; struct sockaddr_in addr; /* connect socket */ fd = socket(AF_INET, SOCK_STREAM, 0); addr.sin_family = AF_INET; addr.sin_port = htons(port); memset(&(addr.sin_addr), 0, sizeof(addr.sin_addr)); addr.sin_addr.s_addr = inet_addr(host); ret = connect(fd, (struct sockaddr*)&addr, sizeof(struct sockaddr)); if(ret != 0) { fprintf(stderr, "connect: ret=%d: %s\n", ret, strerror(errno)); return -1; } return fd; } /** * Send request and read until the delimiter string is reached. blocking. */ void reader_http_request(struct cx *c, const char* buffer, const char *limit) { char resp[2048]; int pos = 0; int r = write(c->fd, buffer, strlen(buffer)); (void)r; memset(resp, 0, sizeof(resp)); while(1) { int ret = read(c->fd, resp+pos, sizeof(resp)-pos); if(ret <= 0) { return; } pos += ret; if(strstr(resp, limit) != NULL) { break; } } } /** * (re)install connection in the event loop. */ void cx_install(struct cx *c) { if(c->read_fun) { /* attach callback for read. */ event_set(&c->evr, c->fd, EV_READ, c->read_fun, c); event_base_set(c->base, &c->evr); event_add(&c->evr, NULL); } if(c->write_fun) { /* attach callback for write. */ event_set(&c->evw, c->fd, EV_WRITE, c->write_fun, c); event_base_set(c->base, &c->evw); event_add(&c->evw, NULL); } } /** * Called when a reader has received data. */ void reader_can_read(int fd, short event, void *ptr) { char buffer[1024]; struct cx *c = ptr; const char *p; (void)event; int ret = read(fd, buffer, sizeof(buffer)); if(ret > 0) { /* count messages, each message starts with '{' */ p = buffer; do { /* look for the start of a message */ p = memchr(p, '{', buffer + ret - p); if(!p) break; /* none left. */ p++; (*c->counter)++; /* increment the global message counter. */ if(((*c->counter * 100) % c->total) == 0) { /* show progress. */ printf("\r%d %%", 100 * *c->counter / c->total); fflush(stdout); } if(*c->counter > c->total) { /* halt event loop. */ event_base_loopbreak(c->base); } } while(1); } cx_install(c); } /** * create a new reader object. */ void reader_new(struct event_base *base, const char *host, short port, int total, int *counter, int chan) { struct cx *c = calloc(1, sizeof(struct cx)); c->base = base; c->counter = counter; c->total = total; c->fd = webdis_connect(host, port); c->read_fun = reader_can_read; /* send subscription request. */ c->http_request = malloc(100); sprintf(c->http_request, "GET /SUBSCRIBE/chan:%d HTTP/1.1\r\n\r\n", chan); reader_http_request(c, c->http_request, "{\"SUBSCRIBE\":[\"subscribe\""); /* add to the event loop. */ cx_install(c); } /** * Called when a writer has received data back. read and ignore. */ void writer_can_read(int fd, short event, void *ptr) { char buffer[1024]; struct cx *c = ptr; int r; (void)event; r = read(fd, buffer, sizeof(buffer)); /* discard */ (void)r; /* re-install in the event loop. */ cx_install(c); } /* send request */ void writer_can_write(int fd, short event, void *ptr) { struct cx *c = ptr; (void)fd; (void)event; reader_http_request(c, c->http_request, "{\"PUBLISH\":"); cx_install(c); } void writer_new(struct event_base *base, const char *host, short port, int chan) { struct cx *c = malloc(sizeof(struct cx)); c->base = base; c->fd = webdis_connect(host, port); c->read_fun = writer_can_read; c->write_fun = writer_can_write; /* send request. */ c->http_request = malloc(100); sprintf(c->http_request, "GET /PUBLISH/chan:%d/hi HTTP/1.1\r\n\r\n", chan); reader_http_request(c, c->http_request, "{\"PUBLISH\":"); cx_install(c); } void usage(const char* argv0, char *host_default, short port_default, int r_default, int w_default, int n_default, int c_default) { printf("Usage: %s [options]\n" "Options are:\n" "\t-h host\t\t(default = \"%s\")\n" "\t-p port\t\t(default = %d)\n" "\t-r readers\t(default = %d)\n" "\t-w writers\t(default = %d)\n" "\t-c channels\t(default = %d)\n" "\t-n messages\t(number of messages to read in total, default = %d)\n", argv0, host_default, (int)port_default, r_default, w_default, c_default, n_default); } static struct event_base *__base; void on_sigint(int s) { (void)s; event_base_loopbreak(__base); } int main(int argc, char *argv[]) { /* Create R readers and W writers, send N messages in total. */ struct timespec t0, t1; struct event_base *base = event_base_new(); int i, count = 0; /* getopt vars */ int opt; char *colon; /* default values */ short port_default = 7379; char *host_default = "127.0.0.1"; int r_default = 450, w_default = 10, n_default = 100000, c_default = 1; /* real values */ int r = r_default, w = w_default, chans = c_default, n = n_default; char *host = host_default; short port = port_default; /* getopt */ while ((opt = getopt(argc, argv, "h:p:r:w:c:n:")) != -1) { switch (opt) { case 'h': colon = strchr(optarg, ':'); if(!colon) { size_t sz = strlen(optarg); host = calloc(1 + sz, 1); strncpy(host, optarg, sz); } else { host = calloc(1+colon-optarg, 1); strncpy(host, optarg, colon-optarg); port = (short)atol(colon+1); } break; case 'p': port = (short)atol(optarg); break; case 'r': r = atoi(optarg); break; case 'w': w = atoi(optarg); break; case 'c': chans = atoi(optarg); break; case 'n': n = atoi(optarg); break; default: usage(argv[0], host_default, port_default, r_default, w_default, n_default, c_default); exit(EXIT_FAILURE); } } for(i = 0; i < r; ++i) { reader_new(base, host, port, n, &count, i % chans); } for(i = 0; i < w; ++i) { writer_new(base, host, port, i % chans); } /* install signal handler */ __base = base; signal(SIGINT, on_sigint); /* save time now */ clock_gettime(CLOCK_MONOTONIC, &t0); /* run test */ event_base_dispatch(base); /* timing */ clock_gettime(CLOCK_MONOTONIC, &t1); float mili0 = t0.tv_sec * 1000 + t0.tv_nsec / 1000000; float mili1 = t1.tv_sec * 1000 + t1.tv_nsec / 1000000; printf("\rReceived %d messages from %d writers to %d readers through %d channels in %0.2f sec: received %0.2f msg/sec\n", count, w, r, chans, (mili1-mili0)/1000.0, 1000*count/(mili1-mili0)); return EXIT_SUCCESS; }