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
39 struct 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
51 struct testResult {
52 int size;
53 int iters;
54 apr_time_t msecs[MAX_ITERS];
55 apr_time_t avg;
56 };
57
58 static apr_int16_t testPort = 4747;
59 static apr_sockaddr_t *sockAddr = NULL;
60
reportError(const char * msg,apr_status_t rv,apr_pool_t * pool)61 static 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
closeConnection(apr_socket_t * sock)71 static void closeConnection(apr_socket_t *sock)
72 {
73 apr_size_t len = 0;
74 apr_socket_send(sock, NULL, &len);
75 }
76
sendRecvBuffer(apr_time_t * t,const char * buf,apr_size_t size,apr_pool_t * pool)77 static 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
runTest(struct testSet * ts,struct testResult * res,apr_pool_t * pool)182 static 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
main(int argc,char ** argv)212 int 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