1 /**
2 ** \file stressclient.cpp
3 ** \date 2006-10-02
4 ** \author grymse@alhem.net
5 **/
6 /*
7 Copyright (C) 2006 Anders Hedstrom
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 */
23 #ifdef _WIN32
24 #pragma warning(disable:4786)
25 #endif
26 #include <Sockets/StdoutLog.h>
27 #include <Sockets/ListenSocket.h>
28 #include <Sockets/SocketHandlerEp.h>
29 #include <Sockets/TcpSocket.h>
30 #include <Sockets/Utility.h>
31 #ifndef _WIN32
32 #include <signal.h>
33 #include <stdint.h>
34 #else
35 typedef __int64 int64_t;
36 #endif
37 #include <Sockets/HttpGetSocket.h>
38 #include <iostream>
39
40 #ifdef SOCKETS_NAMESPACE
41 using namespace SOCKETS_NAMESPACE;
42 #endif
43
44 static int g_ant = 0;
45 static double g_min_time = 10000;
46 static double g_max_time = 0;
47 static double g_tot_time = 0;
48
49 static int g_ant2 = 0;
50 static double g_min_time2 = 10000;
51 static double g_max_time2 = 0;
52 static double g_tot_time2 = 0;
53
54 static int64_t gBytesIn = 0;
55 static int64_t gBytesOut = 0;
56
57 static int samples = 0;
58
59 static int g_tot_ant = 0;
60 static double g_tot_min_t = 0;
61 static double g_tot_max_t = 0;
62 static double g_tot_tot_t = 0;
63
64 static int g_tot_ant2 = 0;
65 static double g_tot_min_t2 = 0;
66 static double g_tot_max_t2 = 0;
67 static double g_tot_tot_t2 = 0;
68
69 static int64_t tot_gBytesIn = 0;
70 static int64_t tot_gBytesOut = 0;
71
72 static double tot_rt = 0;
73
74 static bool gQuit = false;
75 static size_t g_max_connections = 0;
76 static std::string gHost = "localhost";
77 static port_t gPort = 2222;
78 static bool g_b_flood = false;
79 static bool g_b_off = false;
80 static bool g_b_limit = false;
81 static bool g_b_repeat = false;
82 static std::string g_data;
83 static size_t g_data_size = 1024;
84 static bool g_b_stop = false;
85 #ifdef HAVE_OPENSSL
86 static bool g_b_ssl = false;
87 #endif
88 static bool g_b_instant = false;
89 static struct timeval g_t_start;
90
91
92 /**
93 * Return time difference between two struct timeval's, in seconds
94 * \param t0 start time
95 * \param t end time
96 */
Diff(struct timeval t0,struct timeval t)97 double Diff(struct timeval t0,struct timeval t)
98 {
99 t.tv_sec -= t0.tv_sec;
100 t.tv_usec -= t0.tv_usec;
101 if (t.tv_usec < 0)
102 {
103 t.tv_usec += 1000000;
104 t.tv_sec -= 1;
105 }
106 return t.tv_sec + (double)t.tv_usec / 1000000;
107 }
108
109
gettime(struct timeval * p,struct timezone *)110 void gettime(struct timeval *p, struct timezone *)
111 {
112 #ifdef _WIN32
113 FILETIME ft; // Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC).
114 GetSystemTimeAsFileTime(&ft);
115 uint64_t tt;
116 memcpy(&tt, &ft, sizeof(tt));
117 tt /= 10;
118 p->tv_sec = tt / 1000000;
119 p->tv_usec = tt % 1000000;
120 #else
121 gettimeofday(p, NULL);
122 #endif
123 }
124
125
print_tot()126 void print_tot()
127 {
128 printf("c ----- %6d (%.4f/%.4f/%.4f)", g_tot_ant / samples, g_tot_min_t / samples, g_tot_max_t / samples, g_tot_tot_t / g_tot_ant);
129
130 printf(" r --- %7d (%.4f/%.4f/%.4f)", g_tot_ant2 / samples, g_tot_min_t2 / samples, g_tot_max_t2 / samples, g_tot_tot_t2 / g_tot_ant2);
131
132 double mbi = (double)tot_gBytesIn / 1024 / samples;
133 mbi /= 1024;
134 mbi /= tot_rt / samples;
135 printf(" -/in %11lld", tot_gBytesIn / samples);
136 printf(" (%5.2f MB/s)", mbi);
137
138 double mbo = (double)tot_gBytesOut / 1024 / samples;
139 mbo /= 1024;
140 mbo /= tot_rt / samples;
141 printf(" -/out %11lld", tot_gBytesOut / samples);
142 printf(" (%5.2f MB/s)", mbo);
143
144 printf(" %5.2f s\n", tot_rt / samples);
145
146 }
147
148
printreport()149 void printreport()
150 {
151 struct timeval tv;
152 gettime(&tv, NULL);
153 double rt = Diff(g_t_start, tv);
154 g_t_start = tv;
155 //
156 // printf("connect %6d (%.4f/%.4f/%.4f)", g_ant, g_min_time, g_max_time, g_tot_time / g_ant);
157 g_tot_ant += g_ant;
158 g_tot_min_t += g_min_time;
159 g_tot_max_t += g_max_time;
160 g_tot_tot_t += g_tot_time;
161
162 // printf(" reply %7d (%.4f/%.4f/%.4f)", g_ant2, g_min_time2, g_max_time2, g_tot_time2 / g_ant2);
163 g_tot_ant2 += g_ant2;
164 g_tot_min_t2 += g_min_time2;
165 g_tot_max_t2 += g_max_time2;
166 g_tot_tot_t2 += g_tot_time2;
167
168 double mbi = (double)gBytesIn / 1024;
169 mbi /= 1024;
170 mbi /= rt;
171 // printf(" b/in %11lld", gBytesIn);
172 // printf(" (%5.2f MB/s)", mbi);
173 tot_gBytesIn += gBytesIn;
174
175 double mbo = (double)gBytesOut / 1024;
176 mbo /= 1024;
177 mbo /= rt;
178 // printf(" b/out %11lld", gBytesOut);
179 // printf(" (%5.2f MB/s)", mbo);
180 tot_gBytesOut += gBytesOut;
181
182 // printf(" %5.2f s\n", rt);
183 tot_rt += rt;
184
185 ++samples;
186 print_tot();
187 }
188
189
printreport_reset()190 void printreport_reset()
191 {
192 printreport();
193 //
194 g_min_time = 10000;
195 g_max_time = 0;
196 g_tot_time = 0;
197 g_ant = 0;
198 g_min_time2 = 10000;
199 g_max_time2 = 0;
200 g_tot_time2 = 0;
201 g_ant2 = 0;
202 gBytesIn = gBytesOut = 0;
203 }
204
205
206 class MySocket : public TcpSocket
207 {
208 public:
MySocket(ISocketHandler & h,bool one)209 MySocket(ISocketHandler& h,bool one) : TcpSocket(h), m_b_client(false), m_b_one(one), m_b_created(false), m_b_active(false) {
210 gettime(&m_create, NULL);
211 SetLineProtocol();
212 #ifdef HAVE_OPENSSL
213 if (g_b_ssl)
214 EnableSSL();
215 #endif
216 if (g_max_connections && !m_b_one && Handler().GetCount() >= g_max_connections)
217 {
218 fprintf(stderr, "\nConnection limit reached: %d, continuing in single connection stress mode\n", (int)g_max_connections);
219 if (g_b_off)
220 printreport_reset();
221 g_b_limit = true;
222 m_b_one = true;
223 //
224 g_b_flood = g_b_repeat;
225 }
226 #ifndef USE_EPOLL
227 if (!m_b_one && Handler().GetCount() >= Handler().MaxCount() - 17)
228 {
229 fprintf(stderr, "\nFD_SETSIZE connection limit reached: %d, continuing in single connection stress mode\n", (int)Handler().GetCount());
230 if (g_b_off)
231 printreport_reset();
232 g_b_limit = true;
233 m_b_one = true;
234 //
235 g_b_flood = g_b_repeat;
236 }
237 #endif
238 }
~MySocket()239 ~MySocket() {
240 }
241
OnConnect()242 void OnConnect() {
243 gettime(&m_connect, NULL);
244 m_b_active = true;
245 {
246 double tconnect = Diff(m_create, m_connect);
247 //
248 g_min_time = tconnect < g_min_time ? tconnect : g_min_time;
249 g_max_time = tconnect > g_max_time ? tconnect : g_max_time;
250 g_tot_time += tconnect;
251 g_ant += 1;
252 }
253 SendBlock();
254 m_b_client = true;
255 }
256
SendBlock()257 void SendBlock() {
258 gettime(&m_send, NULL);
259 Send(g_data + "\n");
260 }
261
OnLine(const std::string & line)262 void OnLine(const std::string& line) {
263 gettime(&m_reply, NULL);
264 m_b_active = true;
265 {
266 double treply = Diff(m_send, m_reply);
267 //
268 g_min_time2 = treply < g_min_time2 ? treply : g_min_time2;
269 g_max_time2 = treply > g_max_time2 ? treply : g_max_time2;
270 g_tot_time2 += treply;
271 g_ant2 += 1;
272 }
273 //
274 if (line != g_data)
275 {
276 fprintf(stderr, "\n%s\n%s\n", line.c_str(), g_data.c_str());
277 fprintf(stderr, "(reply did not match data - exiting)\n");
278 exit(-1);
279 }
280 //
281 gBytesIn += GetBytesReceived(true);
282 gBytesOut += GetBytesSent(true);
283 if (m_b_one)
284 {
285 SetCloseAndDelete();
286 }
287 else
288 if (g_b_repeat && g_b_limit)
289 {
290 SendBlock();
291 }
292 // add another
293 if (!m_b_created && (!g_b_limit || !g_b_off) && !g_b_instant)
294 {
295 MySocket *p = new MySocket(Handler(), m_b_one);
296 p -> SetDeleteByHandler();
297 p -> Open(gHost, gPort);
298 Handler().Add(p);
299 m_b_created = true;
300 }
301 }
302
IsActive()303 bool IsActive() {
304 bool b = m_b_active;
305 m_b_active = false;
306 return b;
307 }
308
309 private:
310 bool m_b_client;
311 bool m_b_one;
312 bool m_b_created;
313 bool m_b_active;
314 struct timeval m_create;
315 struct timeval m_connect;
316 struct timeval m_send;
317 struct timeval m_reply;
318 };
319
320
321 class MyHttpSocket : public HttpGetSocket
322 {
323 public:
MyHttpSocket(ISocketHandler & h,const std::string & url)324 MyHttpSocket(ISocketHandler& h,const std::string& url) : HttpGetSocket(h,url), m_url(url) {
325 gettime(&m_create, NULL);
326 AddResponseHeader("content-length", Utility::l2string(g_data_size));
327 }
~MyHttpSocket()328 ~MyHttpSocket() {}
329
OnConnect()330 void OnConnect() {
331 gettime(&m_connect, NULL);
332 {
333 double tconnect = Diff(m_create, m_connect);
334 //
335 g_min_time = tconnect < g_min_time ? tconnect : g_min_time;
336 g_max_time = tconnect > g_max_time ? tconnect : g_max_time;
337 g_tot_time += tconnect;
338 g_ant += 1;
339 }
340 gettime(&m_send, NULL);
341
342 // send request header
343 HttpGetSocket::OnConnect();
344
345 // send body
346 Send(g_data);
347 }
348
OnContent()349 void OnContent() {
350 // std::cout << GetContentLength() << std::endl;
351 gettime(&m_reply, NULL);
352 {
353 double treply = Diff(m_send, m_reply);
354 //
355 g_min_time2 = treply < g_min_time2 ? treply : g_min_time2;
356 g_max_time2 = treply > g_max_time2 ? treply : g_max_time2;
357 g_tot_time2 += treply;
358 g_ant2 += 1;
359 }
360 gBytesIn += GetBytesReceived(true);
361 gBytesOut += GetBytesSent(true);
362 CreateNew();
363 }
CreateNew()364 void CreateNew() {
365 if (g_b_off)
366 return;
367 MyHttpSocket *p = new MyHttpSocket(Handler(), m_url);
368 p -> SetDeleteByHandler();
369 Handler().Add(p);
370 SetCloseAndDelete();
371 }
372
OnDelete()373 void OnDelete() {
374 }
375
376 private:
377 std::string m_url;
378 struct timeval m_create;
379 struct timeval m_connect;
380 struct timeval m_send;
381 struct timeval m_reply;
382 };
383
384
385 int connectors = 0;
386
387 class ConnectorSocket : public TcpSocket
388 {
389 public:
ConnectorSocket(ISocketHandler & h)390 ConnectorSocket(ISocketHandler& h) : TcpSocket(h), m_state(0) {
391 }
~ConnectorSocket()392 ~ConnectorSocket() {}
393
OnConnect()394 void OnConnect() {
395 SetTimeout(5);
396 connectors += 1;
397 std::cout << "Connected: " << connectors << std::endl;
398 }
399
OnTimeout()400 void OnTimeout() {
401 SetTimeout(5);
402 if (!m_state)
403 {
404 m_state = 1;
405 }
406 else
407 {
408 ConnectorSocket *s = new ConnectorSocket(Handler());
409 s -> Open(gHost, gPort);
410 s -> SetDeleteByHandler();
411 Handler().Add(s);
412 }
413 }
414
415 private:
416 int m_state;
417 };
418
419
420 #ifndef _WIN32
sigint(int)421 void sigint(int)
422 {
423 printreport();
424 gQuit = true;
425 }
426
427
sigusr1(int)428 void sigusr1(int)
429 {
430 g_b_flood = true;
431 }
432
433
sigusr2(int)434 void sigusr2(int)
435 {
436 printreport_reset();
437 }
438 #endif
439
440
441 class MyHandler : public SocketHandlerEp
442 {
443 public:
MyHandler()444 MyHandler() : SocketHandlerEp() {
445 }
MyHandler(StdoutLog * p)446 MyHandler(StdoutLog *p) : SocketHandlerEp(p) {
447 }
~MyHandler()448 ~MyHandler() {
449 }
Flood()450 void Flood() {
451 for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
452 {
453 Socket *p0 = it -> second;
454 MySocket *p = dynamic_cast<MySocket *>(p0);
455 if (p)
456 {
457 p -> SendBlock();
458 }
459 }
460 }
Report()461 void Report() {
462 int ant = 0;
463 int act = 0;
464 for (socket_m::iterator it = m_sockets.begin(); it != m_sockets.end(); it++)
465 {
466 MySocket *p = dynamic_cast<MySocket *>(it -> second);
467 if (p)
468 {
469 ant++;
470 if (p -> IsActive())
471 {
472 act++;
473 }
474 }
475 }
476 // printf(" Number of //stress// sockets: %d Active: %d\n", ant, act);
477 }
478 };
479
480
main(int argc,char * argv[])481 int main(int argc,char *argv[])
482 {
483 bool many = false;
484 bool one = false;
485 bool enableLog = false;
486 bool http = false;
487 bool connector = false;
488 std::string url;
489 time_t report_period = 10;
490 for (int i = 1; i < argc; i++)
491 {
492 if (!strcmp(argv[i], "-many"))
493 many = true;
494 if (!strcmp(argv[i], "-one"))
495 one = true;
496 if (*argv[i] == '-' && strlen(argv[i]) > 1 && isdigit(argv[i][1]) )
497 g_max_connections = atoi(argv[i] + 1);
498 if (!strcmp(argv[i], "-host") && i < argc - 1)
499 gHost = argv[++i];
500 if (!strcmp(argv[i], "-port") && i < argc - 1)
501 gPort = atoi(argv[++i]);
502 if (!strcmp(argv[i], "-off"))
503 g_b_off = true;
504 if (!strcmp(argv[i], "-repeat"))
505 g_b_repeat = true;
506 if (!strcmp(argv[i], "-size") && i < argc - 1)
507 g_data_size = atoi(argv[++i]);
508 if (!strcmp(argv[i], "-log"))
509 enableLog = true;
510 if (!strcmp(argv[i], "-time") && i < argc - 1)
511 report_period = atoi(argv[++i]);
512 if (!strcmp(argv[i], "-stop"))
513 g_b_stop = true;
514 #ifdef HAVE_OPENSSL
515 if (!strcmp(argv[i], "-ssl"))
516 g_b_ssl = true;
517 #endif
518 if (!strcmp(argv[i], "-instant"))
519 g_b_instant = true;
520 if (!strcmp(argv[i], "-http"))
521 http = true;
522 if (!strcmp(argv[i], "-url") && i < argc - 1)
523 url = argv[++i];
524 if (!strcmp(argv[i], "-connector"))
525 connector = true;
526 }
527 if (argc < 2 || (!many && !one && !g_max_connections && !http && !connector) )
528 {
529 printf("Usage: %s [mode] [options]\n", *argv);
530 printf(" Modes (only use one of these):\n");
531 printf(" -many start max number of connections\n");
532 printf(" -one open - close - repeat\n");
533 printf(" -nn open nn connections, then start -one mode\n");
534 printf(" -http send/receive http request/response\n");
535 printf(" Options:\n");
536 printf(" -host xx host to connect to\n");
537 printf(" -port nn port number to connection to\n");
538 printf(" -off turn off new connections when connection limit reached\n");
539 printf(" -repeat send new block when reply is received\n");
540 printf(" -size nn size of block to send, default is 1024 bytes\n");
541 printf(" -log enable debug log\n");
542 printf(" -time nn time between reports, default 10s\n");
543 printf(" -stop stop after time elapsed\n");
544 printf(" -instant create all sockets at once\n");
545 #ifdef HAVE_OPENSSL
546 printf(" -ssl use ssl\n");
547 #endif
548 printf(" -url xx url to use in http mode (default http://<host>:<port>/)\n");
549 printf(" -connector Use connector stress test\n");
550 exit(-1);
551 }
552 fprintf(stderr, "Using data size: %d bytes\n", (int)g_data_size);
553 std::string chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
554 while (g_data.size() < g_data_size)
555 {
556 g_data += chars[rand() % chars.size()];
557 }
558 #ifndef _WIN32
559 signal(SIGINT, sigint);
560 signal(SIGUSR1, sigusr1);
561 signal(SIGUSR2, sigusr2);
562 signal(SIGPIPE, SIG_IGN);
563 #endif
564 StdoutLog *log = enableLog ? new StdoutLog() : NULL;
565 MyHandler h(log);
566 if (connector)
567 {
568 report_period = 4;
569 ConnectorSocket *s = new ConnectorSocket(h);
570 s -> Open(gHost, gPort);
571 s -> SetDeleteByHandler();
572 h.Add(s);
573 }
574 else
575 if (http)
576 {
577 if (!url.size())
578 {
579 url = "http://" + gHost + ":" + Utility::l2string(gPort) + "/";
580 }
581 MyHttpSocket *s = new MyHttpSocket(h, url);
582 s -> SetDeleteByHandler();
583 h.Add(s);
584 }
585 else
586 if (g_b_instant)
587 {
588 for (size_t i = 0; i < g_max_connections; i++)
589 {
590 MySocket *s = new MySocket(h, one);
591 s -> SetDeleteByHandler();
592 s -> Open(gHost, gPort);
593 h.Add(s);
594 }
595 g_b_limit = true;
596 }
597 else
598 {
599 MySocket *s = new MySocket(h, one);
600 s -> SetDeleteByHandler();
601 s -> Open(gHost, gPort);
602 h.Add(s);
603 }
604 time_t t = time(NULL);
605 gettime(&g_t_start, NULL);
606 while (!gQuit)
607 {
608 h.Select(1, 0);
609 if (g_b_flood)
610 {
611 fprintf(stderr, "\nFlooding\n");
612 h.Flood();
613 g_b_flood = false;
614 }
615 if (time(NULL) - t >= report_period) // report
616 {
617 t = time(NULL);
618 printreport_reset();
619 h.Report();
620 if (g_b_stop)
621 {
622 gQuit = true;
623 }
624 }
625 }
626 fprintf(stderr, "\nExiting...\n");
627 if (log)
628 {
629 // delete log;
630 }
631 return 0;
632 }
633
634
635