【问题标题】:How to use gen_tcp:recv correctly如何正确使用 gen_tcp:recv
【发布时间】:2012-02-05 19:32:26
【问题描述】:

我编写了简单的 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:shutdowndo_recv 函数的调用(参见代码示例中的注释), 这似乎解决了这个问题。我知道这个问题很愚蠢,解决方案 很像猜测,也许有人仍然可以解释这里发生的事情 他们通常如何解决这类问题。

【问题讨论】:

    标签: erlang


    【解决方案1】:

    您的代码有一些问题。

    如果你收到 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 是一个尝试解码数据的函数,它可能会失败并请求更多数据。
    • 处理器是一个进程,一旦我们解码了某些内容,我们就可以将消息发送到该进程。这也可能是一个函数调用,它对我们刚刚解码的数据执行某些操作。

    【讨论】:

    • 能否提供固定版本的代码?还是看起来太复杂了?
    • 对于gen_tcp:recv(Socket, Length)Length 仅与选项raw 一起使用吗?我可以让 Length 0 工作,但任何其他 Length 都不起作用。还没有尝试过 raw 选项,虽然有些地方说要使用
    猜你喜欢
    • 2012-08-14
    • 2011-04-16
    • 2020-12-04
    • 2012-07-12
    • 2013-01-21
    • 2021-10-24
    • 2011-12-22
    • 2020-10-14
    • 2015-12-10
    相关资源
    最近更新 更多