如何正确使用gen_tcp:recv

时间:2012-01-09 08:15:14

标签: erlang

我编写简单的http客户端,遇到以下问题,我从官方文档中复制do_recv,但它的工作方式很奇怪:

do_recv(Sock, Bs) ->
    case gen_tcp:recv(Sock, 0, ?TIMEOUT) of
        {ok, B} ->
            gen_tcp:shutdown(Sock, write), % <-- this appears to fix the problem!
            do_recv(Sock, [Bs, B]);
        {error, closed} ->
            {ok, list_to_binary(Bs)}
    end.

聊天顺序如下:

{ok, S} = gen_tcp:connect(Ip, Port, [inet, binary,
                  {packet, 0},
                  {active, false},
                  {nodelay, true},
                  {reuseaddr, true}], 2000),
Req = io_lib:format("GET ~s HTTP/1.1\r\nHost: ~s\r\n\r\n", [Url, UrlHost]),
ok = gen_tcp:send(S, list_to_binary(Req)) of
do_recv(S, []);

do_recv的最终调用有时会按预期工作并返回服务器 respose,但有时它挂起和超时,我想因为服务器没有关闭套接字 在其自己的。 所以第二个超时的情况是我想避免的,任何想法 如何应对这种行为?

UPD:

我添加了gen_tcp:shutdown调用do_recv函数(请参阅代码示例中的注释), 这似乎解决了这个问题。问题是我知道的非常noobish和解决方案 非常像猜测,也许还有人可以解释这里发生的事情 他们通常如何解决这类问题。

1 个答案:

答案 0 :(得分:3)

您的代码存在一些问题。

如果你收到0,你可以获得GET字符串的一半,或者你可以获得超过整个GET字符串,具体取决于内核处理流的方式。 TCP是面向流的,所以你需要从套接字中获取数据,直到你有足够的数据。此外,您可以轻松地以{error,timeout}触发结束,因此您也必须处理该问题。否则它将无法按预期工作。基本上你需要一个收集数据的循环,直到你有足够的数据来解析GET。在获得所有数据之前,超时将在该循环中发生。

有些事情:

do_recv(Sock, Gathered) ->
  case gen_tcp:recv(Sock, 0, ?TIMEOUT) of
    {ok, Bin} ->
      Remaining = try_decode(Sock, <<Gathered/binary, Bin/binary>>),
      do_recv(Sock, Remaining);
    {error, timeout} ->
      do_recv(Sock, Remaining);
    {error, Reason} ->
      exit(Reason)
   end.

 try_decode(Sock, Gathered) ->
   case decode(Gathered) of
      {ok, Data, Rest} ->
         processor ! Data,
         try_decode(Sock, Rest);
      need_more_data ->
         do_recv(Sock, Gathered)
   end.

这里假设了几件事

  • decode / 1是一个尝试解码数据的函数,它可能无法执行此操作并请求更多数据。
  • 处理器是我们可以在解码后发送消息的过程。这也可以是一个函数调用,它对我们刚刚解码的数据做了一些事情。