/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* sockperf.c * This simple network client tries to connect to an echo daemon (echod) * listening on a port it supplies, then time how long it takes to * reply with packets of varying sizes. * It prints results once completed. * * To run, * * ./echod & * ./sockperf */ #include #include /* for atexit() */ #include "apr.h" #include "apr_network_io.h" #include "apr_strings.h" #define MAX_ITERS 10 #define TEST_SIZE 1024 struct testSet { char c; apr_size_t size; int iters; } testRuns[] = { { 'a', 1, 3 }, { 'b', 4, 3 }, { 'c', 16, 5 }, { 'd', 64, 5 }, { 'e', 256, 10 }, }; struct testResult { apr_size_t size; int iters; apr_time_t msecs[MAX_ITERS]; apr_time_t avg; }; static apr_int16_t testPort = 4747; static apr_sockaddr_t *sockAddr = NULL; static void reportError(const char *msg, apr_status_t rv, apr_pool_t *pool) { fprintf(stderr, "%s\n", msg); if (rv != APR_SUCCESS) fprintf(stderr, "Error: %d\n'%s'\n", rv, apr_psprintf(pool, "%pm", &rv)); } static void closeConnection(apr_socket_t *sock) { apr_size_t len = 0; apr_socket_send(sock, NULL, &len); } static apr_status_t sendRecvBuffer(apr_time_t *t, const char *buf, apr_size_t size, apr_pool_t *pool) { apr_socket_t *sock; apr_status_t rv; apr_size_t len = size, thistime = size; char *recvBuf; apr_time_t testStart = apr_time_now(), testEnd; int i; if (! sockAddr) { rv = apr_sockaddr_info_get(&sockAddr, "127.0.0.1", APR_UNSPEC, testPort, 0, pool); if (rv != APR_SUCCESS) { reportError("Unable to get socket info", rv, pool); return rv; } /* make sure we can connect to daemon before we try tests */ rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, APR_PROTO_TCP, pool); if (rv != APR_SUCCESS) { reportError("Unable to create IPv4 stream socket", rv, pool); return rv; } rv = apr_socket_connect(sock, sockAddr); if (rv != APR_SUCCESS) { reportError("Unable to connect to echod!", rv, pool); apr_socket_close(sock); return rv; } apr_socket_close(sock); } recvBuf = apr_palloc(pool, size); if (! recvBuf) { reportError("Unable to allocate buffer", ENOMEM, pool); return ENOMEM; } *t = 0; /* START! */ testStart = apr_time_now(); rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, APR_PROTO_TCP, pool); if (rv != APR_SUCCESS) { reportError("Unable to create IPv4 stream socket", rv, pool); return rv; } rv = apr_socket_connect(sock, sockAddr); if (rv != APR_SUCCESS) { reportError("Unable to connect to echod!", rv, pool); apr_socket_close(sock); return rv; } for (i = 0; i < 3; i++) { len = size; thistime = size; rv = apr_socket_send(sock, buf, &len); if (rv != APR_SUCCESS || len != size) { reportError(apr_psprintf(pool, "Unable to send data correctly (iteration %d of 3)", i) , rv, pool); closeConnection(sock); apr_socket_close(sock); return rv; } do { len = thistime; rv = apr_socket_recv(sock, &recvBuf[size - thistime], &len); if (rv != APR_SUCCESS) { reportError("Error receiving from socket", rv, pool); break; } thistime -= len; } while (thistime); } closeConnection(sock); apr_socket_close(sock); testEnd = apr_time_now(); /* STOP! */ if (thistime) { reportError("Received less than we sent :-(", rv, pool); return rv; } if (strncmp(recvBuf, buf, size) != 0) { reportError("Received corrupt data :-(", 0, pool); printf("We sent:\n%s\nWe received:\n%s\n", buf, recvBuf); return EINVAL; } *t = testEnd - testStart; return APR_SUCCESS; } static apr_status_t runTest(struct testSet *ts, struct testResult *res, apr_pool_t *pool) { char *buffer; apr_status_t rv = APR_SUCCESS; int i; apr_size_t sz = ts->size * TEST_SIZE; buffer = apr_palloc(pool, sz); if (!buffer) { reportError("Unable to allocate buffer", ENOMEM, pool); return ENOMEM; } memset(buffer, ts->c, sz); res->iters = ts->iters > MAX_ITERS ? MAX_ITERS : ts->iters; for (i = 0; i < res->iters; i++) { apr_time_t iterTime; rv = sendRecvBuffer(&iterTime, buffer, sz, pool); if (rv != APR_SUCCESS) { res->iters = i; break; } res->msecs[i] = iterTime; } return rv; } int main(int argc, char **argv) { apr_pool_t *pool; apr_status_t rv; int i; int nTests = sizeof(testRuns) / sizeof(testRuns[0]); struct testResult *results; printf("APR Test Application: sockperf\n"); apr_initialize(); atexit(apr_terminate); apr_pool_create(&pool, NULL); results = (struct testResult *)apr_pcalloc(pool, sizeof(*results) * nTests); for (i = 0; i < nTests; i++) { printf("Test -> %c\n", testRuns[i].c); results[i].size = testRuns[i].size * (apr_size_t)TEST_SIZE; rv = runTest(&testRuns[i], &results[i], pool); if (rv != APR_SUCCESS) { /* error already reported */ exit(1); } } printf("Tests Complete!\n"); for (i = 0; i < nTests; i++) { int j; apr_time_t totTime = 0; printf("%" APR_SIZE_T_FMT " byte block:\n", results[i].size); printf("\t%2d iterations : ", results[i].iters); for (j = 0; j < results[i].iters; j++) { printf("%6" APR_TIME_T_FMT, results[i].msecs[j]); totTime += results[i].msecs[j]; } printf("<\n"); printf("\t Average: %6" APR_TIME_T_FMT "\n", totTime / results[i].iters); } return 0; }