1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #include <iostream.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <netinet/tcp.h>
29 #include <sys/filio.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include "/usr/include/netdb.h"
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <fstream.h>
37 #include <strstream.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <assert.h>
41 
42 enum Task_t {
43   TASK_NONE = 0,
44   TASK_DONE,
45   TASK_CONNECT,
46   TASK_LISTEN_SETUP,
47   TASK_ACCEPT,
48   TASK_SHUTDOWN_OUTPUT,
49   TASK_SHUTDOWN_INPUT,
50   TASK_SHUTDOWN_BOTH,
51   TASK_TRY_READ,
52   TASK_TRY_WRITE,
53   TASK_TRY_WRITE_THEN_SHUTDOWN_OUTPUT,
54   TASK_TRY_WRITE_THEN_SHUTDOWN_BOTH,
55   TASK_COUNT
56 };
57 
58 enum State_t {
59   STATE_IDLE = 0,
60   STATE_DONE,
61   STATE_ERROR,
62 };
63 
64 enum Connection_t {
65   CONNECTION_CLIENT,
66   CONNECTION_SERVER,
67 };
68 
69 enum Scenario_t {
70   SERVER_WRITE_CLIENT_READ,
71 
72   SERVER_SHUTDOWN_OUTPUT_CLIENT_TRY_READ,
73   SERVER_SHUTDOWN_INPUT_CLIENT_TRY_READ,
74   SERVER_SHUTDOWN_BOTH_CLIENT_TRY_READ,
75   SERVER_SHUTDOWN_OUTPUT_CLIENT_TRY_WRITE,
76   SERVER_SHUTDOWN_INPUT_CLIENT_TRY_WRITE,
77   SERVER_SHUTDOWN_BOTH_CLIENT_TRY_WRITE,
78 
79   CLIENT_SHUTDOWN_OUTPUT_SERVER_TRY_READ,
80   CLIENT_SHUTDOWN_INPUT_SERVER_TRY_READ,
81   CLIENT_SHUTDOWN_BOTH_SERVER_TRY_READ,
82   CLIENT_SHUTDOWN_OUTPUT_SERVER_TRY_WRITE,
83   CLIENT_SHUTDOWN_INPUT_SERVER_TRY_WRITE,
84   CLIENT_SHUTDOWN_BOTH_SERVER_TRY_WRITE,
85 
86   SERVER_WRITE_IMMIDIATE_SHUTDOWN_CLIENT_WRITE
87 };
88 
89 struct State {
90   State_t state;
91   int tasks_count;
92   Task_t tasks[100];
93   int64_t nbytes_write; // number of bytes to write
94   intte_t nbytes_read;  // number of bytes to read
95 
StateState96   State() : state(STATE_IDLE), tasks_count(0), nbytes_write(0), {}
97 };
98 
99 struct Conn {
100   Connection_t connection_type;
101   int listen_s;
102   int s;
103   struct sockaddr_in addr;
104   State state;
105   // State_t            state;
106   int state_delay_ms;
107 };
108 
109 Conn client, server;
110 int port_number;
111 Task_t server_set_next_client_task[TASK_COUNT];
112 Task_t client_set_next_server_task[TASK_COUNT];
113 char write_buf[10];
114 char read_buf[10];
115 int state_delay_ms = 0;
116 
117 #define IS_DONE(c) (c.state.state == STATE_DONE || c.state.state == STATE_ERROR)
118 
119 void main_loop();
120 void state_act(Conn *c);
121 void state_act_task(Conn *c);
122 int do_connect(Conn *from, Conn *to);
123 int do_listen_setup(Conn *c, int port_number);
124 int do_accept(Conn *c);
125 int create_nonblocking_socket();
126 int set_nonblocking_socket(int s);
127 int do_shutdown(int s, Task_t task);
128 int do_try_read(int s, char *buf, int length);
129 int do_try_write(int s, char *buf, int length);
130 void setup_scenario(Scenario_t scenario);
131 void dequeue_task(Conn *c);
132 ///////////////////////////////////////////////////////////
133 //
134 //  main()
135 //
136 ///////////////////////////////////////////////////////////
137 int
main(int argc,char ** argv)138 main(int argc, char **argv)
139 {
140   if (argc < 2) {
141     cout << "test_socket_close <port number> <state delay ms>" << endl;
142     return (0);
143   }
144   port_number = atoi(argv[1]);
145 
146   if (argc >= 3) {
147     state_delay_ms = atoi(argv[2]);
148   }
149 
150   memset(&client, '\0', sizeof(client));
151   memset(&server, '\0', sizeof(server));
152 
153   memset(&write_buf, 'B', sizeof(write_buf));
154   memset(&read_buf, '\0', sizeof(read_buf));
155 
156   client.connection_type = CONNECTION_CLIENT;
157   server.connection_type = CONNECTION_SERVER;
158 
159   client.state.state = STATE_IDLE;
160   server.state.state = STATE_IDLE;
161 
162   client.state.tasks_count = 0;
163   server.state.tasks_count = 1;
164   server.state.tasks[0]    = TASK_LISTEN_SETUP;
165 
166   client.state_delay_ms = state_delay_ms;
167   server.state_delay_ms = state_delay_ms;
168 
169   ////////////////////
170   // set next state //
171   ////////////////////
172   setup_scenario(SERVER_WRITE_IMMIDIATE_SHUTDOWN_CLIENT_WRITE);
173   // setup_scenario(SERVER_WRITE_CLIENT_READ);
174 
175   main_loop();
176 
177   return (0);
178 }
179 
180 ///////////////////////////////////////////////////////////
181 //
182 //  main_loop()
183 //
184 ///////////////////////////////////////////////////////////
185 void
main_loop()186 main_loop()
187 {
188   while (!IS_DONE(client) || !IS_DONE(server)) {
189     if (client.state.tasks_count > 0 && !IS_DONE(client)) {
190       state_act(&client);
191     }
192     if (server.state.tasks_count > 0 && !IS_DONE(server)) {
193       state_act(&server);
194     }
195   }
196   return;
197 }
198 
199 ///////////////////////////////////////////////////////////
200 //
201 //  state_act()
202 //
203 ///////////////////////////////////////////////////////////
204 void
state_act(Conn * c)205 state_act(Conn *c)
206 {
207   Task_t saved_task = c->state.tasks[0];
208 
209   Conn &cr = *c;
210 
211   while (c->state.tasks_count > 0 && !IS_DONE(cr)) {
212     if (c->state_delay_ms) {
213       poll(0, 0, c->state_delay_ms);
214     }
215     state_act_task(c);
216   }
217 
218   if (IS_DONE(cr)) {
219     cr.state.tasks_count = 1;
220     cr.state.tasks[0]    = TASK_DONE;
221   }
222 
223   if ((c == &client && IS_DONE(server)) || (c == &server && IS_DONE(client))) {
224     cr.state.tasks_count = 1;
225     cr.state.tasks[0]    = saved_task;
226   } else if (c == &client) {
227     server.state.tasks_count = 1;
228     server.state.tasks[0]    = client_set_next_server_task[saved_task];
229   } else {
230     client.state.tasks_count = 1;
231     client.state.tasks[0]    = server_set_next_client_task[saved_task];
232   }
233   return;
234 }
235 
236 ///////////////////////////////////////////////////////////
237 //
238 //  state_act_task()
239 //
240 ///////////////////////////////////////////////////////////
241 void
state_act_task(Conn * c)242 state_act_task(Conn *c)
243 {
244   char write_ch = 'T', read_ch;
245   int r;
246 
247   Task_t saved_task = c->state.tasks[0];
248 
249   switch (c->state.tasks[0]) {
250   case TASK_CONNECT:
251     assert(c == &client);
252     do_connect(&client, &server);
253     dequeue_task(&client);
254     break;
255 
256   case TASK_SHUTDOWN_OUTPUT:
257   case TASK_SHUTDOWN_INPUT:
258   case TASK_SHUTDOWN_BOTH:
259     if (do_shutdown(c->s, c->state.tasks[0]) < 0)
260       c->state.state = STATE_ERROR;
261     else
262       c->state.state = STATE_DONE;
263 
264     dequeue_task(c);
265     break;
266 
267   case TASK_TRY_READ:
268     r = do_try_read(c->s, &read_ch, 1);
269     if (r > 0)
270       c->state.state = STATE_IDLE;
271     else if (r == 0)
272       c->state.state = STATE_DONE; // EOS
273     else if (r != -EAGAIN)
274       c->state.state = STATE_ERROR; // error
275     dequeue_task(c);
276     break;
277 
278   case TASK_TRY_WRITE:
279     r = do_try_write(c->s, &write_ch, 1);
280     if (r <= 0 && r != -EAGAIN)
281       c->state.state = STATE_ERROR; // error
282     else
283       c->state.state = STATE_IDLE;
284     dequeue_task(c);
285     break;
286 
287   case TASK_TRY_WRITE_THEN_SHUTDOWN_OUTPUT:
288   case TASK_TRY_WRITE_THEN_SHUTDOWN_BOTH:
289     r = do_try_write(c->s, write_buf, c->state.nbytes_write);
290     if (r <= 0 && r != -EAGAIN)
291       c->state.state = STATE_ERROR; // error
292     else {
293       c->state.nbytes_write -= r;
294       if (c->state.nbytes_write == 0) {
295         // do shutdown
296         if (do_shutdown(c->s, c->state.tasks[0]) < 0)
297           c->state.state = STATE_ERROR;
298         else
299           c->state.state = STATE_DONE;
300 
301         dequeue_task(c);
302       }
303     }
304     break;
305 
306   case TASK_LISTEN_SETUP:
307     assert(c == &server);
308     if (do_listen_setup(&server, port_number) > 0) {
309       dequeue_task(&server);
310     }
311     break;
312 
313   case TASK_ACCEPT:
314     assert(c == &server);
315     if (do_accept(&server) > 0) {
316       dequeue_task(&server);
317     }
318     break;
319   }
320 
321   return;
322 }
323 
324 ///////////////////////////////////////////////////////////
325 //
326 //  do_connect()
327 //
328 //  'to' must be listening
329 ///////////////////////////////////////////////////////////
330 int
do_connect(Conn * from,Conn * to)331 do_connect(Conn *from, Conn *to)
332 {
333   assert(to->listen_s > 0);
334 
335   // create a non-blocking socket
336   if ((from->s = create_nonblocking_socket()) < 0) {
337     from->state.state = STATE_ERROR;
338     return (from->s);
339   }
340   // connect
341   if (connect(from->s, (struct sockaddr *)&to->addr, sizeof(to->addr)) < 0) {
342     int error = -errno;
343     if (error != -EINPROGRESS) {
344       ::close(from->s);
345       from->state.state = STATE_ERROR;
346       cout << "connect failed (" << error << ")" << endl;
347       return (error);
348     }
349   }
350   // success
351   cout << "connect is done" << endl;
352   from->state.state = STATE_IDLE;
353   return (from->s);
354 }
355 
356 ///////////////////////////////////////////////////////////
357 //
358 //  do_listen_setup()
359 //
360 ///////////////////////////////////////////////////////////
361 int
do_listen_setup(Conn * c,int port)362 do_listen_setup(Conn *c, int port)
363 {
364   int error;
365 
366   c->addr.sin_family = AF_INET;
367   memset(&c->addr.sin_zero, 0, 8);
368   c->addr.sin_addr.s_addr = htonl(INADDR_ANY);
369   c->addr.sin_port        = htons(port);
370 
371   // create a non-blocking socket
372   if ((c->listen_s = create_nonblocking_socket()) < 0) {
373     c->state.state = STATE_ERROR;
374     return (c->listen_s);
375   }
376   // bind socket to port
377   if (bind(c->listen_s, (struct sockaddr *)&c->addr, sizeof(c->addr)) < 0) {
378     error = -errno;
379     ::close(c->listen_s);
380     c->state.state = STATE_ERROR;
381     cout << "bind failed (" << error << ")" << endl;
382     return (error);
383   }
384   // listen
385   if (listen(c->listen_s, 5) < 0) {
386     error = -errno;
387     ::close(c->listen_s);
388     c->state.state = STATE_ERROR;
389     cout << "listen failed (" << error << ")" << endl;
390     return (-1);
391   }
392   // success
393   cout << "listen is done" << endl;
394   c->state.state = STATE_IDLE;
395 
396   return (c->listen_s);
397 }
398 
399 ///////////////////////////////////////////////////////////
400 //
401 //  do_accept()
402 //
403 ///////////////////////////////////////////////////////////
404 int
do_accept(Conn * c)405 do_accept(Conn *c)
406 {
407   assert(c->listen_s > 0);
408 
409   // check if socket is ready for read
410   fd_set readfds;
411   struct timeval timeout;
412   int addrlen;
413 
414   FD_ZERO(&readfds);
415   FD_SET(c->listen_s, &readfds);
416   timeout.tv_sec  = 0;
417   timeout.tv_usec = 10; /* 0.01 ms */
418 
419   if (select(c->listen_s + 1, &readfds, 0, 0, &timeout) > 0) {
420     addrlen = sizeof(c->addr);
421     c->s    = accept(c->listen_s, (struct sockaddr *)&c->addr, &addrlen);
422     if (c->s < 0) {
423       c->s = -errno;
424       cout << "accept failed (" << c->s << ")" << endl;
425       c->state.state = STATE_ERROR;
426       return (c->s);
427     }
428     if ((c->s = set_nonblocking_socket(c->s)) < 0) {
429       c->state.state = STATE_ERROR;
430       return (c->s);
431     }
432     c->state.state = STATE_IDLE;
433   }
434   cout << "accept is done" << endl;
435   return (c->s);
436 }
437 
438 ///////////////////////////////////////////////////////////
439 //
440 //  create_nonblocking_socket()
441 //
442 ///////////////////////////////////////////////////////////
443 int
create_nonblocking_socket()444 create_nonblocking_socket()
445 {
446   int s;
447 
448   if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
449     s = -errno;
450     cout << "socket failed (" << s << ")" << endl;
451     return (s);
452   }
453   return (set_nonblocking_socket(s));
454 }
455 
456 ///////////////////////////////////////////////////////////
457 //
458 //  set_nonblocking_socket()
459 //
460 ///////////////////////////////////////////////////////////
461 int
set_nonblocking_socket(int s)462 set_nonblocking_socket(int s)
463 {
464   if (fcntl(s, F_SETFL, O_NDELAY) < 0) {
465     int error = -errno;
466     ::close(s);
467     cout << "fcntl F_SETFD O_NDELAY failed (" << error << ")" << endl;
468     return (error);
469   }
470 
471   return (s);
472 }
473 
474 ///////////////////////////////////////////////////////////
475 //
476 //  do_shutdown()
477 //
478 ///////////////////////////////////////////////////////////
479 int
do_shutdown(int s,Task_t task)480 do_shutdown(int s, Task_t task)
481 {
482   int howto;
483 
484   switch (task) {
485   case TASK_SHUTDOWN_OUTPUT:
486   case TASK_TRY_WRITE_THEN_SHUTDOWN_OUTPUT:
487     howto = 1;
488     break;
489   case TASK_SHUTDOWN_INPUT:
490     howto = 0;
491     break;
492   case TASK_SHUTDOWN_BOTH:
493   case TASK_TRY_WRITE_THEN_SHUTDOWN_BOTH:
494     howto = 2;
495     break;
496   default:
497     assert(!"expected a shutdown state");
498     break;
499   }
500   if (shutdown(s, howto) < 0) {
501     int error = -errno;
502     cout << "shutdown failed (" << error << ")" << endl;
503     return (error);
504   }
505   // success
506   cout << "sutdowm is done" << endl;
507   return (0);
508 }
509 
510 ///////////////////////////////////////////////////////////
511 //
512 //  do_try_read()
513 //
514 ///////////////////////////////////////////////////////////
515 int
do_try_read(int s,char * buf,int length)516 do_try_read(int s, char *buf, int length)
517 {
518   int r;
519 
520   if ((r = read(s, buf, length)) < 0) {
521     r = -errno;
522     if (r != -EWOULDBLOCK) // EWOULDBLOCK == EAGAIN
523     {
524       cout << "read failed (" << r << ")" << endl;
525     }
526   } else if (r == 0) {
527     cout << "connection closed" << endl;
528   } else {
529     // read is successful
530     for (int i = 0; i < r; i++) {
531       cout << buf[i] << ' ';
532     }
533   }
534   return (r);
535 }
536 
537 ///////////////////////////////////////////////////////////
538 //
539 //  do_try_write()
540 //
541 ///////////////////////////////////////////////////////////
542 int
do_try_write(int s,char * buf,int length)543 do_try_write(int s, char *buf, int length)
544 {
545   int r;
546 
547   if ((r = write(s, buf, length)) <= 0) {
548     r = -errno;
549     if (r != -EWOULDBLOCK) {
550       cout << "write failed (" << r << ")" << endl;
551     }
552   }
553   return (r);
554 }
555 
556 ///////////////////////////////////////////////////////////
557 //
558 //  setup_scenario()
559 //
560 ///////////////////////////////////////////////////////////
561 void
setup_scenario(Scenario_t scenario)562 setup_scenario(Scenario_t scenario)
563 {
564   switch (scenario) {
565   case SERVER_WRITE_CLIENT_READ:
566     server_set_next_client_task[TASK_LISTEN_SETUP] = TASK_CONNECT;
567     client_set_next_server_task[TASK_CONNECT]      = TASK_ACCEPT;
568     server_set_next_client_task[TASK_ACCEPT]       = TASK_TRY_READ;
569     client_set_next_server_task[TASK_TRY_READ]     = TASK_TRY_WRITE;
570     server_set_next_client_task[TASK_TRY_WRITE]    = TASK_TRY_READ;
571     break;
572 
573   case SERVER_SHUTDOWN_OUTPUT_CLIENT_TRY_READ:
574     server_set_next_client_task[TASK_LISTEN_SETUP]    = TASK_CONNECT;
575     server_set_next_client_task[TASK_ACCEPT]          = TASK_TRY_READ;
576     server_set_next_client_task[TASK_SHUTDOWN_OUTPUT] = TASK_TRY_READ;
577 
578     client_set_next_server_task[TASK_CONNECT]  = TASK_ACCEPT;
579     client_set_next_server_task[TASK_TRY_READ] = TASK_SHUTDOWN_OUTPUT;
580     break;
581 
582   case SERVER_SHUTDOWN_INPUT_CLIENT_TRY_READ:
583     break;
584   case SERVER_SHUTDOWN_BOTH_CLIENT_TRY_READ:
585     break;
586   case SERVER_SHUTDOWN_OUTPUT_CLIENT_TRY_WRITE:
587     break;
588   case SERVER_SHUTDOWN_INPUT_CLIENT_TRY_WRITE:
589     break;
590   case SERVER_SHUTDOWN_BOTH_CLIENT_TRY_WRITE:
591     break;
592   case CLIENT_SHUTDOWN_OUTPUT_SERVER_TRY_READ:
593     break;
594   case CLIENT_SHUTDOWN_INPUT_SERVER_TRY_READ:
595     break;
596   case CLIENT_SHUTDOWN_BOTH_SERVER_TRY_READ:
597     break;
598   case CLIENT_SHUTDOWN_OUTPUT_SERVER_TRY_WRITE:
599     break;
600   case CLIENT_SHUTDOWN_INPUT_SERVER_TRY_WRITE:
601     break;
602   case CLIENT_SHUTDOWN_BOTH_SERVER_TRY_WRITE:
603     break;
604   case SERVER_WRITE_IMMIDIATE_SHUTDOWN_CLIENT_WRITE:
605     server_set_next_client_task[TASK_LISTEN_SETUP]                 = TASK_CONNECT;
606     client_set_next_server_task[TASK_CONNECT]                      = TASK_ACCEPT;
607     server_set_next_client_task[TASK_ACCEPT]                       = TASK_TRY_READ;
608     client_set_next_server_task[TASK_TRY_READ]                     = TASK_TRY_WRITE_THEN_SHUTDOWN_BOTH;
609     server_set_next_client_task[TASK_TRY_WRITE_THEN_SHUTDOWN_BOTH] = TASK_TRY_READ;
610     server_set_next_client_task[TASK_DONE]                         = TASK_TRY_READ;
611     server.state.nbytes_write                                      = sizeof(write_buf);
612     break;
613   }
614   return;
615 }
616 
617 void
dequeue_task(Conn * c)618 dequeue_task(Conn *c)
619 {
620   if (c->state.tasks_count == 0)
621     return;
622 
623   c->state.tasks_count--;
624   for (int i = 0; i < c->state.tasks_count; i++) {
625     c->state.tasks[i] = c->state.tasks[i + 1];
626   }
627   return;
628 }
629