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