socket close() weirdness in 2.2 kernels

Martins Krikis (mkrikis@kenan.com)
Mon, 04 Oct 1999 12:46:39 -0400


My apologies for continuing about the same thing, but IMHO
the matters didn't get explained or resolved previously.

Most everybody said I'm not reading all the data, but I am!

This is about the dummy-webserver code (enclosed at the end),
that reads all headers and sends the response. Doing another
read after the headers returns 0 (IMHO meaning that there is
no more data (at least yet)). When the server does a close(),
Netscape or Exploder report a "Network Error", if they had
accessed server with a POST request.

According to Alan Cox, "Linux 2.2 sends a reset to indicate
to the other end that you shut down without reading
all the sent data". Like I said, this is all the data
(as the enclosed HTTP headers will testify), and further
read()-s return 0 (and I'm afraid they may block one day).
So the first question is, what's really going on there?
Note that these same browsers have no problem with this server
code running on 2.0 kernels or on Solaris, HPUX or OSF1.

If Linux 2.2 really sends a reset, why would it do so when I
actually have read all the data?

However, like I wrote before, I am thankful for the previous responses,
because I've found a kludge now. An ugly one, IMO, but something.
Basically, if before doing close() the server does a read() for
at least 2 characters (or 2 reads for 1 character), the problem
disappears. But all these reads still return 0 (and may block, right?),
so there was no data to read. Why would they make any difference?
And why wouldn't a single read() for just 1 character?

And finally, what should I do to solve the problem decently?
I can't believe I really have to do a useless read() for 2 characters
when I know well that there is no more data. (Plus, I'd have to
take care of possible blocking.)

I would very much like to understand what's going on and you are
the only people who can explain this, so please help me.

Thank you very much,

Martin mkrikis@kenan.com

------------------------------------------------------------------------------

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define LINELEN 1023

int main(int argc, char **argv)
{
int port = 0, on = 1, s, fd, i, j, l;
char line[LINELEN + 1];
struct sockaddr_in sa;
/* struct linger ls; */
char msg[] = "HTTP/1.0 200 OK\n"
"Content-Type: text/plain\n"
"Content-Length: 8\n"
"\n"
"abcdefg\n";

if (argc >= 2)
port = atoi(argv[1]);

if (!port)
port = 8080;

memset(&sa, 0, sizeof(struct sockaddr_in));
sa.sin_port = htons(port);
sa.sin_family = AF_INET;

if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket");
exit(1);
}

if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)))
perror("setsockopt");

/* This doesn't help either

ls.l_onoff = 1;
ls.l_linger = 500;

if (setsockopt(s, SOL_SOCKET, SO_LINGER, (const void *) &ls, sizeof(ls)))
perror("setsockopt");
*/

if (bind(s, (struct sockaddr *) (void *) &sa, sizeof(struct sockaddr_in))
< 0)
{
perror("bind");
exit(2);
}

if (listen(s, SOMAXCONN))
{
perror("listen");
exit(3);
}

printf("Listening on port %d\n", port);

for ( ; ; )
{

if ((fd = accept(s, NULL, NULL)) < 0)
{
perror("accept");
exit(4);
}

printf("Accepted a connection on fd %d\n", fd);

for ( ; ; ) // reading all the headers here
{

for (i = 0; i < LINELEN; i++)
{

if (read(fd, line + i, 1) < 0)
{
perror("read");
exit(5);
}

if (line[i] == '\n')
{
line[i + 1] = '\0';
printf("Just input: %s", line);
break;
}

}

if (i >= LINELEN)
{
printf("Line longer than %d\n", LINELEN);
exit(6);
}

if (!strcmp(line, "\n") || !strcmp(line, "\r\n"))
break;

}

l = strlen(msg);

for (i = 0; i < l; i += j)
if ((j = write(fd, msg + i, l - i)) < 0)
{
perror("write");
exit(7);
}

if (close(fd) < 0)
{
perror("close");
exit(8);
}

}

return 0; // not reached
}

---------------------------------------------------------------------------

Post to the Dummy Server

------------------------------------------------------------------------

$ ./dummyserver 8889
Listening on port 8889
Accepted a connection on fd 8
Just input: POST / HTTP/1.0
Just input: Referer: http://localhost:8000/localdummy.html
Just input: Connection: Keep-Alive
Just input: User-Agent: Mozilla/4.61 [en] (X11; U; Linux 2.2.13pre14 i586)
Just input: Host: localhost:8889
Just input: Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Just input: Accept-Encoding: gzip
Just input: Accept-Language: en
Just input: Accept-Charset: iso-8859-1,*,utf-8
Just input: Content-type: application/x-www-form-urlencoded
Just input: Content-length: 0
Just input:

---------------------------------------------------------------------

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/