gen_tcp:recv - last segement when closed port

Per Hedeland hedeland@REDACTED
Sat Mar 11 14:19:59 CET 2006


jmT2 <johan@REDACTED> wrote:
>
>This is how I open the socket:
>
>     gen_tcp:connect(IP, Port, [list, {packet,0}, {active, false}]).
>
>And this is how I access the socket:
>
>     gen_tcp:recv(Socket, 0, 20000)  %(high timeout since I'm running over  
>mobile links)
>
>I'm reading from a web server that replies with HTTP/1.0 and thus closes  
>the TCP connection once the page is delivered. The page is delivered in  
>five TCP packets:
>
>638 bytes  header info
>1400 bytes body
>1400 bytes body
>1270 bytes body
>1199 bytes body
>
>My read-parse loop reads the stream by calling gen_tcp:recv/3 and gets the  
>following:
>
>head--- 638
>
>body--- 1024
>body--- 376   (aah the first 1400)
>
>body--- 1024
>body--- 376  (aah the second 1400)
>
>body--- 1024
>body--- 246 (aah the third 1270)
>
>body--- 1024  - ok this is why I say the usual 1024
>body closed  - ops where did the remaining 175 bytes go?
>
>My guess is that the port is closed while I'm parsing the last 1024 bytes  
>and that the 175 bytes are left in buffer.

That would be a pretty gross bug - I can't reproduce it though. With the
code below, I always get:

3> tcpbug:cli(56857).
Got 638 bytes
Got 1024 bytes
Got 376 bytes
Got 1024 bytes
Got 376 bytes
Got 1024 bytes
Got 246 bytes
Got 1024 bytes
Got 175 bytes
Got {error,closed}
ok

(I see where the 1024 is coming from - rather suboptimal choice of
default buffer size in inet_drv. Of course in "most" cases of "bulk" TCP
transfer you will probably have more than one packet worth of data
available for gen_tcp:recv() and hence get 1024 every time, but a
default of at least 1500 would avoid this "pathological" case without
any significant problems.)

Do you get a different result with that code? What version of Erlang/OTP
are you running? Tcpdumping the traffic, I see that this code results in
the FIN coming in its own packet rather than being "piggybacked" on the
last data packet, which may be different from your case and could
possibly be relevant - can you check that?

--Per


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

-module(tcpbug).

-export([srv/0, cli/1]).

srv() ->
    {ok, S} = gen_tcp:listen(0, []),
    {ok, P} = inet:port(S),
    io:format("Listen port: ~w~n", [P]),
    srv_loop(S).

srv_loop(S) ->
    {ok, A} = gen_tcp:accept(S),
    lists:foreach(fun(B) ->
			  sleep(2000),   % mobile link...
			  gen_tcp:send(A, lists:duplicate(B, $A))
		  end, [638, 1400, 1400, 1270, 1199]),
    gen_tcp:close(A),
    srv_loop(S).

cli(P) ->
    {ok, S} = gen_tcp:connect("localhost", P,
			      [list, {packet,0}, {active, false}]),
    cli_loop(S).

cli_loop(S) ->
    case gen_tcp:recv(S, 0, 20000) of
	{ok, L} ->
	    io:format("Got ~w bytes~n", [length(L)]),
	    sleep(500),   % parsing...
	    cli_loop(S);
	R ->
	    io:format("Got ~p~n", [R])
    end,
    gen_tcp:close(S).

sleep(T) ->
    receive
    after T ->
	    ok
    end.



More information about the erlang-questions mailing list