Date: Thu, 29 Sep 2016 18:19:13 -0500 From: Lewis Donzis <lew@perftech.com> To: freebsd-net@freebsd.org Subject: recv() with MSG_WAITALL appears to be broken (sometimes) Message-ID: <BC66679E-3828-41F9-954A-F3C44F6D94EC@perftech.com>
next in thread | raw e-mail | index | archive | help
I posted this on bugzilla = (https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=3D212716) but thought = I=E2=80=99d see if anyone here can offer some advice. We have a simple test program to illustrate the problem. Run "server" on a machine, and then run "client" on the same machine. Server creates a listening socket and loops accepting connections, = sending a little data, closing the socket, and then back to the top of = the loop. Client creates a socket, connects to the server, does a recv() with = MSG_WAITALL, closes the socket, and loops. This runs anywhere from one to a few dozen times and then hangs. The = server socket is in FIN_WAIT_2, and the client socket is in CLOSE_WAIT. = So the client side is waiting for the application to close the socket, = but it's still stuck in the recv(), never being awakened by the EOF from = the server closing the socket. The same code runs on Linux and QNX without any problem. This seems ridiculously simple and seems like it would affect many = programs, so perhaps we=E2=80=99re just overlooking something. =20 Note that it makes no difference whether it's run in the same machine = (to localhost or to the machine's own IP address) or across the network, = or whether it's IPv4 or IPv6. It=E2=80=99s also the same with a UNIX = domain STREAM socket (but not a SEQPACKET socket), so it appears to be = unrelated to TCP and perhaps more of a general problem in the socket = layer. In all cases, the recv() never unblocks when the peer closes the = socket. Thanks, lew client.c: #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <arpa/inet.h> #include <err.h> int main (int argc, char *argv[]) { int c; bool verbose =3D false; char *addr =3D "127.0.0.1"; signal(SIGPIPE, SIG_IGN); while ((c =3D getopt(argc, argv, "v")) !=3D -1) { switch (c) { case 'v': ++verbose; break; default: return 1; break; } } argc -=3D optind - 1; argv +=3D optind - 1; if (argc > 1) addr =3D argv[1]; for (int try =3D 1;; try++) { printf("Try %d\n", try); int s; if ((s =3D socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) err(1, "socket"); =20 struct sockaddr_in sa =3D { .sin_family =3D AF_INET, .sin_port =3D = htons(79), .sin_addr.s_addr =3D inet_addr(addr) }; if (connect(s, (struct sockaddr *)&sa, sizeof sa) < 0) err(1, "connect"); // get the response char rbuf [4096]; int rcvLen; if ((rcvLen =3D recv(s, rbuf, sizeof rbuf, MSG_WAITALL)) < 0) err(1, "recv"); if (verbose) printf("Received: '%.*s'\n", rcvLen, rbuf); close(s); } return 0; } server.c: #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <ctype.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <arpa/inet.h> #include <err.h> int main (int argc, char *argv[]) { int c; bool verbose =3D false; signal(SIGPIPE, SIG_IGN); while ((c =3D getopt(argc, argv, "v")) !=3D -1) { switch (c) { case 'v': ++verbose; break; default: return 1; break; } } argc -=3D optind - 1; argv +=3D optind - 1; int s0; if ((s0 =3D socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) err(1, "socket"); int on =3D true; if (setsockopt(s0, SOL_SOCKET, SO_REUSEPORT, &on, sizeof on) < 0) err(1, "setsockopt"); struct sockaddr_in sa =3D { .sin_family =3D AF_INET, .sin_port =3D = htons(79) }; if (bind(s0, (struct sockaddr *)&sa, sizeof sa) < 0) err(1, "bind"); if (listen(s0, 1000) < 0) err(1, "listen"); printf("Listening\n"); for (int try =3D 1;; try++) { socklen_t sl; int s =3D accept(s0, (struct sockaddr *)&sa, &sl); if (s < 0) err(1, "accept"); printf("Try %d\n", try); // send the response static char sbuf [] =3D "Hello from server\n"; if (send(s, sbuf, sizeof sbuf - 1, 0) < 0) err(1, "send"); if (verbose) printf("Sent: %s", sbuf); close(s); } return 0; }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?BC66679E-3828-41F9-954A-F3C44F6D94EC>