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