1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17/* sockperf.c 18 * This simple network client tries to connect to an echo daemon (echod) 19 * listening on a port it supplies, then time how long it takes to 20 * reply with packets of varying sizes. 21 * It prints results once completed. 22 * 23 * To run, 24 * 25 * ./echod & 26 * ./sockperf 27 */ 28 29#include <stdio.h> 30#include <stdlib.h> /* for atexit() */ 31 32#include "apr.h" 33#include "apr_network_io.h" 34#include "apr_strings.h" 35 36#define MAX_ITERS 10 37#define TEST_SIZE 1024 38 39struct testSet { 40 char c; 41 apr_size_t size; 42 int iters; 43} testRuns[] = { 44 { 'a', 1, 3 }, 45 { 'b', 4, 3 }, 46 { 'c', 16, 5 }, 47 { 'd', 64, 5 }, 48 { 'e', 256, 10 }, 49}; 50 51struct testResult { 52 int size; 53 int iters; 54 apr_time_t msecs[MAX_ITERS]; 55 apr_time_t avg; 56}; 57 58static apr_int16_t testPort = 4747; 59static apr_sockaddr_t *sockAddr = NULL; 60 61static void reportError(const char *msg, apr_status_t rv, 62 apr_pool_t *pool) 63{ 64 fprintf(stderr, "%s\n", msg); 65 if (rv != APR_SUCCESS) 66 fprintf(stderr, "Error: %d\n'%s'\n", rv, 67 apr_psprintf(pool, "%pm", &rv)); 68 69} 70 71static void closeConnection(apr_socket_t *sock) 72{ 73 apr_size_t len = 0; 74 apr_socket_send(sock, NULL, &len); 75} 76 77static apr_status_t sendRecvBuffer(apr_time_t *t, const char *buf, 78 apr_size_t size, apr_pool_t *pool) 79{ 80 apr_socket_t *sock; 81 apr_status_t rv; 82 apr_size_t len = size, thistime = size; 83 char *recvBuf; 84 apr_time_t testStart = apr_time_now(), testEnd; 85 int i; 86 87 if (! sockAddr) { 88 rv = apr_sockaddr_info_get(&sockAddr, "127.0.0.1", APR_UNSPEC, 89 testPort, 0, pool); 90 if (rv != APR_SUCCESS) { 91 reportError("Unable to get socket info", rv, pool); 92 return rv; 93 } 94 95 /* make sure we can connect to daemon before we try tests */ 96 97 rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, APR_PROTO_TCP, 98 pool); 99 if (rv != APR_SUCCESS) 100 return rv; 101 102 rv = apr_socket_connect(sock, sockAddr); 103 if (rv != APR_SUCCESS) { 104 reportError("Unable to connect to echod!", rv, pool); 105 apr_socket_close(sock); 106 return rv; 107 } 108 apr_socket_close(sock); 109 110 } 111 112 recvBuf = apr_palloc(pool, size); 113 if (! recvBuf) 114 return ENOMEM; 115 *t = 0; 116 117 /* START! */ 118 testStart = apr_time_now(); 119 rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, APR_PROTO_TCP, 120 pool); 121 if (rv != APR_SUCCESS) 122 return rv; 123 124 rv = apr_socket_connect(sock, sockAddr); 125 if (rv != APR_SUCCESS) { 126 reportError("Unable to connect to echod!", rv, pool); 127 apr_socket_close(sock); 128 return rv; 129 } 130 131 for (i = 0; i < 3; i++) { 132 133 len = size; 134 thistime = size; 135 136 rv = apr_socket_send(sock, buf, &len); 137 if (rv != APR_SUCCESS || len != size) { 138 reportError(apr_psprintf(pool, 139 "Unable to send data correctly (iteration %d of 3)", 140 i) , rv, pool); 141 closeConnection(sock); 142 apr_socket_close(sock); 143 return rv; 144 } 145 146 do { 147 len = thistime; 148 rv = apr_socket_recv(sock, &recvBuf[size - thistime], &len); 149 if (rv != APR_SUCCESS) 150 break; 151 thistime -= len; 152 } while (thistime); 153 } 154 155 closeConnection(sock); 156 apr_socket_close(sock); 157 testEnd = apr_time_now(); 158 /* STOP! */ 159 160 if (thistime) { 161 reportError("Received less than we sent :-(", rv, pool); 162 return rv; 163 } 164 if (strncmp(recvBuf, buf, size) != 0) { 165 reportError("Received corrupt data :-(", 0, pool); 166 printf("We sent:\n%s\nWe received:\n%s\n", buf, recvBuf); 167 return EINVAL; 168 } 169 *t = testEnd - testStart; 170 return APR_SUCCESS; 171} 172 173static apr_status_t runTest(struct testSet *ts, struct testResult *res, 174 apr_pool_t *pool) 175{ 176 char *buffer; 177 apr_status_t rv; 178 int i; 179 apr_size_t sz = ts->size * TEST_SIZE; 180 181 buffer = apr_palloc(pool, sz); 182 if (!buffer) { 183 reportError("Unable to allocate buffer", ENOMEM, pool); 184 return ENOMEM; 185 } 186 memset(buffer, ts->c, sz); 187 188 res->iters = ts->iters > MAX_ITERS ? MAX_ITERS : ts->iters; 189 190 for (i = 0; i < res->iters; i++) { 191 apr_time_t iterTime; 192 rv = sendRecvBuffer(&iterTime, buffer, sz, pool); 193 if (rv != APR_SUCCESS) { 194 res->iters = i; 195 break; 196 } 197 res->msecs[i] = iterTime; 198 } 199 200 return rv; 201} 202 203int main(int argc, char **argv) 204{ 205 apr_pool_t *pool; 206 apr_status_t rv; 207 int i; 208 int nTests = sizeof(testRuns) / sizeof(testRuns[0]); 209 struct testResult *results; 210 211 printf("APR Test Application: sockperf\n"); 212 213 apr_initialize(); 214 atexit(apr_terminate); 215 216 apr_pool_create(&pool, NULL); 217 218 results = (struct testResult *)apr_pcalloc(pool, 219 sizeof(*results) * nTests); 220 221 for(i = 0; i < nTests; i++) { 222 printf("Test -> %c\n", testRuns[i].c); 223 results[i].size = testRuns[i].size * (apr_size_t)TEST_SIZE; 224 rv = runTest(&testRuns[i], &results[i], pool); 225 } 226 227 printf("Tests Complete!\n"); 228 for(i = 0; i < nTests; i++) { 229 int j; 230 apr_time_t totTime = 0; 231 printf("%10d byte block:\n", results[i].size); 232 printf("\t%2d iterations : ", results[i].iters); 233 for (j = 0; j < results[i].iters; j++) { 234 printf("%6" APR_TIME_T_FMT, results[i].msecs[j]); 235 totTime += results[i].msecs[j]; 236 } 237 printf("<\n"); 238 printf("\t Average: %6" APR_TIME_T_FMT "\n", 239 totTime / results[i].iters); 240 } 241 242 return 0; 243} 244