gen_tcp:recv - last segement when closed port

Per Hedeland hedeland@REDACTED
Sun Mar 12 19:04:55 CET 2006


jmT2 <johan@REDACTED> wrote:
>
>How does the gen_tcp:recv/2 work? It calls inet_db:lookup_socket/1 to see  
>where tehsocket belongs or ....? I guess this is where it detects that the  
>socket is closed? What happens in erlang:get_port_data/1? I guss the  
>problem is in how WinSock is used.

The interesting stuff in this case happens in the inet driver
(erts/emulator/drivers/common/inet_drv.c - the Erlang-side interaction
is in prim_inet:recv()), in particular I believe inet_drv should not
close the port when there is remaining data, and the {error, closed}
should only happen in response to a RECV request for a socket that has
{active, false} - i.e. the case in lookup_socket shouldn't come into
play.

Anyway, I wrote a little C program that quite faithfully reproduces the
packet trace you posted, including the piggybacked FIN - still no
"luck", I always get all the data with the Erlang client code. So, I
would guess that it's either a bug in WinSock or (as you suggest) in
inet_drv's usage of WinSock, neither of which I know anything about.

You could try reproducing with that program (below) - of course it won't
compile on Windows, but it should work on at least FreeBSD and Linux.
Note that the inter-packet spacing is dropped to 200 ms (approximately
what was in your trace), so if you use the tcpbug.erl client code you
probably want to drop the "parse time" to 100 ms or so.

--Per

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

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h>

#if defined(TCP_NOPUSH)
#define OPTNAME TCP_NOPUSH
#elif defined(TCP_CORK)
#define OPTNAME TCP_CORK
#else
#error "Can't work here"
#endif

int size[] = {638, 1400, 1400, 1270, 1199, 0};
char buf[1500];
struct timespec ms200 = {0, 200000000};

main()
{
    int s, a, i, val = 1;
    struct sockaddr_in sa;
    socklen_t sa_len = sizeof(sa);

    s = socket(PF_INET, SOCK_STREAM, 0);
    memset(&sa, '\0', sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = htonl(INADDR_ANY);
    sa.sin_port = 0;
    bind(s, (struct sockaddr *)&sa, sa_len);
    listen(s, 1);
    getsockname(s, (struct sockaddr *)&sa, &sa_len);
    printf("Listen port: %d\n", ntohs(sa.sin_port));
    memset(buf, 'a', sizeof(buf));

    while (1) {
	a = accept(s, NULL, NULL);
	for (i = 0; size[i] != 0; i++) {
	    nanosleep(&ms200, NULL);
	    if (size[i+1] == 0)
		setsockopt(a, IPPROTO_TCP, OPTNAME, &val, sizeof(val));
	    write(a, buf, size[i]);
	}
	close(a);
    }
}



More information about the erlang-questions mailing list