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 reportError("Unable to create IPv4 stream socket", rv, pool); 101 return rv; 102 } 103 104 rv = apr_socket_connect(sock, sockAddr); 105 if (rv != APR_SUCCESS) { 106 reportError("Unable to connect to echod!", rv, pool); 107 apr_socket_close(sock); 108 return rv; 109 } 110 apr_socket_close(sock); 111 112 } 113 114 recvBuf = apr_palloc(pool, size); 115 if (! recvBuf) { 116 reportError("Unable to allocate buffer", ENOMEM, pool); 117 return ENOMEM; 118 } 119 120 *t = 0; 121 122 /* START! */ 123 testStart = apr_time_now(); 124 rv = apr_socket_create(&sock, APR_INET, SOCK_STREAM, APR_PROTO_TCP, 125 pool); 126 if (rv != APR_SUCCESS) { 127 reportError("Unable to create IPv4 stream socket", rv, pool); 128 return rv; 129 } 130 131 rv = apr_socket_connect(sock, sockAddr); 132 if (rv != APR_SUCCESS) { 133 reportError("Unable to connect to echod!", rv, pool); 134 apr_socket_close(sock); 135 return rv; 136 } 137 138 for (i = 0; i < 3; i++) { 139 140 len = size; 141 thistime = size; 142 143 rv = apr_socket_send(sock, buf, &len); 144 if (rv != APR_SUCCESS || len != size) { 145 reportError(apr_psprintf(pool, 146 "Unable to send data correctly (iteration %d of 3)", 147 i) , rv, pool); 148 closeConnection(sock); 149 apr_socket_close(sock); 150 return rv; 151 } 152 153 do { 154 len = thistime; 155 rv = apr_socket_recv(sock, &recvBuf[size - thistime], &len); 156 if (rv != APR_SUCCESS) { 157 reportError("Error receiving from socket", rv, pool); 158 break; 159 } 160 thistime -= len; 161 } while (thistime); 162 } 163 164 closeConnection(sock); 165 apr_socket_close(sock); 166 testEnd = apr_time_now(); 167 /* STOP! */ 168 169 if (thistime) { 170 reportError("Received less than we sent :-(", rv, pool); 171 return rv; 172 } 173 if (strncmp(recvBuf, buf, size) != 0) { 174 reportError("Received corrupt data :-(", 0, pool); 175 printf("We sent:\n%s\nWe received:\n%s\n", buf, recvBuf); 176 return EINVAL; 177 } 178 *t = testEnd - testStart; 179 return APR_SUCCESS; 180} 181 182static apr_status_t runTest(struct testSet *ts, struct testResult *res, 183 apr_pool_t *pool) 184{ 185 char *buffer; 186 apr_status_t rv; 187 int i; 188 apr_size_t sz = ts->size * TEST_SIZE; 189 190 buffer = apr_palloc(pool, sz); 191 if (!buffer) { 192 reportError("Unable to allocate buffer", ENOMEM, pool); 193 return ENOMEM; 194 } 195 memset(buffer, ts->c, sz); 196 197 res->iters = ts->iters > MAX_ITERS ? MAX_ITERS : ts->iters; 198 199 for (i = 0; i < res->iters; i++) { 200 apr_time_t iterTime; 201 rv = sendRecvBuffer(&iterTime, buffer, sz, pool); 202 if (rv != APR_SUCCESS) { 203 res->iters = i; 204 break; 205 } 206 res->msecs[i] = iterTime; 207 } 208 209 return rv; 210} 211 212int main(int argc, char **argv) 213{ 214 apr_pool_t *pool; 215 apr_status_t rv; 216 int i; 217 int nTests = sizeof(testRuns) / sizeof(testRuns[0]); 218 struct testResult *results; 219 220 printf("APR Test Application: sockperf\n"); 221 222 apr_initialize(); 223 atexit(apr_terminate); 224 225 apr_pool_create(&pool, NULL); 226 227 results = (struct testResult *)apr_pcalloc(pool, 228 sizeof(*results) * nTests); 229 230 for (i = 0; i < nTests; i++) { 231 printf("Test -> %c\n", testRuns[i].c); 232 results[i].size = testRuns[i].size * (apr_size_t)TEST_SIZE; 233 rv = runTest(&testRuns[i], &results[i], pool); 234 if (rv != APR_SUCCESS) { 235 /* error already reported */ 236 exit(1); 237 } 238 } 239 240 printf("Tests Complete!\n"); 241 for (i = 0; i < nTests; i++) { 242 int j; 243 apr_time_t totTime = 0; 244 printf("%10d byte block:\n", results[i].size); 245 printf("\t%2d iterations : ", results[i].iters); 246 for (j = 0; j < results[i].iters; j++) { 247 printf("%6" APR_TIME_T_FMT, results[i].msecs[j]); 248 totTime += results[i].msecs[j]; 249 } 250 printf("<\n"); 251 printf("\t Average: %6" APR_TIME_T_FMT "\n", 252 totTime / results[i].iters); 253 } 254 255 return 0; 256} 257